diff --git a/DEPS b/DEPS index 3a72844..fa07614 100644 --- a/DEPS +++ b/DEPS
@@ -145,11 +145,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': '6ef31815a694c7e8555c9217288af19f21af4901', + 'skia_revision': '5e2c489a335cc52155f983cc065565681e476d1f', # 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': '1d37448e10c317e8aa120892ee1c6ec7512130ed', + 'v8_revision': '83ed30595d74cd1ea3f4396984df34e8987669cc', # 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. @@ -157,15 +157,15 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '956770025c8ca163847b5c21da596f07d024d1dd', + 'angle_revision': '2251102112312a9762898ce785afce6adc4f5ca3', # 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': '34c59c9b88b76564cf534aad2f0033a472182d87', + 'swiftshader_revision': '5e4e8b0af5fa0b00de29c77ecf56014e409b53c0', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '12c357b447a45e4a10a96426e84c4fa495774108', + 'pdfium_revision': '5e97d0125fd968b043ce7e39e7a6409595343234', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -280,7 +280,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': 'b6d7c537088835c386bc7850d85300f9f72d4cf6', + 'dawn_revision': '35670f183a730beb8e11aecf75272053560b0ac3', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -814,7 +814,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7588713317d681bbf37e7df716c691bfbecd3524', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '811aead064fba1e209195aa5830eb00b1c19ad1b', 'condition': 'checkout_linux', }, @@ -829,7 +829,7 @@ # For Linux and Chromium OS. 'src/third_party/cros_system_api': { - 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'c267899484bac0f024a10b32452d4cee6ffa6068', + 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '7e90794dfac8e32ce3ecc14029046d7e05fd91b2', 'condition': 'checkout_linux', }, @@ -839,7 +839,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9fc459b69c7840c3c67d2359cd8931e962bcae84', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e765f652958c26fb12f5843b9160b15c7adad347', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -1212,7 +1212,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '827c26f6435e3515ee54d0f4fb5846ec51e2811e', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '5c7d9733468d518942b2cbfe471999c403503c39', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1380,7 +1380,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'abaae129d9a0c6e1e092067e0b105475df43352e', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '34aee67c11454e6c20c1a48702e92b48062cd2bf', + Var('webrtc_git') + '/src.git' + '@' + '01525f9e03384601605ccaa5603481f1de860444', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1421,7 +1421,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7741db7456bc7c76d7a91975efa47a8292a6d90b', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f9fbd0f9b5e93d2a36984ca8e0328e436d8b576d', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc index 7b27757a..247ee6d 100644 --- a/android_webview/browser/aw_metrics_service_client.cc +++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -59,6 +59,17 @@ // - WebView uses the low-entropy source for all studies, so there would be // crosstalk between the metrics sampling study and all other studies. bool IsInSample(const std::string& client_id) { + // TODO(ntfschr): remove these CHECK()s when we rule this out as the culprit + // for https://crbug.com/992250. + CHECK_EQ(36u, client_id.length()) << "client ID should be 36 chars exactly"; + for (const char& c : client_id) { + bool char_is_digit = (c >= '0' && c <= '9'); + bool char_is_a_through_f = (c >= 'a' && c <= 'f'); + bool char_is_hyphen = (c == '-'); + CHECK(char_is_digit || char_is_a_through_f || char_is_hyphen) + << "client_id should be composed of [0-9a-f] or '-'."; + } + // client_id comes from base::GenerateGUID(), so its value is random/uniform, // except for a few bit positions with fixed values, and some hyphens. Rather // than separating the random payload from the fixed bits, just hash the whole
diff --git a/android_webview/browser/js_java_interaction/js_api_handler.cc b/android_webview/browser/js_java_interaction/js_api_handler.cc index da29cda..7471e4c 100644 --- a/android_webview/browser/js_java_interaction/js_api_handler.cc +++ b/android_webview/browser/js_java_interaction/js_api_handler.cc
@@ -24,7 +24,7 @@ } void JsApiHandler::PostMessage( - const std::string& message, + const base::string16& message, std::vector<mojo::ScopedMessagePipeHandle> ports) { DCHECK(render_frame_host_); @@ -50,7 +50,7 @@ JNIEnv* env = base::android::AttachCurrentThread(); aw_contents->OnPostMessage( - env, base::android::ConvertUTF8ToJavaString(env, message), + env, base::android::ConvertUTF16ToJavaString(env, message), base::android::ConvertUTF8ToJavaString(env, source_origin.Serialize()), web_contents->GetMainFrame() == render_frame_host_, base::android::ToJavaIntArray(env, int_ports.data(), int_ports.size()));
diff --git a/android_webview/browser/js_java_interaction/js_api_handler.h b/android_webview/browser/js_java_interaction/js_api_handler.h index feeefa8..9952e7a 100644 --- a/android_webview/browser/js_java_interaction/js_api_handler.h +++ b/android_webview/browser/js_java_interaction/js_api_handler.h
@@ -5,10 +5,10 @@ #ifndef ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_API_HANDLER_H_ #define ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_API_HANDLER_H_ -#include <string> #include <vector> #include "android_webview/common/js_java_interaction/interfaces.mojom.h" +#include "base/strings/string16.h" #include "mojo/public/cpp/bindings/associated_receiver.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" @@ -32,7 +32,7 @@ mojo::PendingAssociatedReceiver<mojom::JsApiHandler> pending_receiver); // mojom::JsApiHandler implementation. - void PostMessage(const std::string& message, + void PostMessage(const base::string16& message, std::vector<mojo::ScopedMessagePipeHandle> ports) override; private:
diff --git a/android_webview/browser/js_java_interaction/js_java_configurator_host.cc b/android_webview/browser/js_java_interaction/js_java_configurator_host.cc index 8dc26b0..147dcabd 100644 --- a/android_webview/browser/js_java_interaction/js_java_configurator_host.cc +++ b/android_webview/browser/js_java_interaction/js_java_configurator_host.cc
@@ -28,16 +28,14 @@ bool need_to_inject_js_object, const base::android::JavaParamRef<jstring>& js_object_name, const base::android::JavaParamRef<jobjectArray>& allowed_origin_rules) { - std::string native_js_object_name; - base::android::ConvertJavaStringToUTF8(env, js_object_name, - &native_js_object_name); + base::android::ConvertJavaStringToUTF16(env, js_object_name, + &js_object_name_); std::vector<std::string> native_allowed_origin_rules; AppendJavaStringArrayToStringVector(env, allowed_origin_rules, &native_allowed_origin_rules); need_to_inject_js_object_ = need_to_inject_js_object; - js_object_name_ = native_js_object_name; allowed_origin_rules_ = net::ProxyBypassRules(); for (auto& rule : native_allowed_origin_rules) { if (!allowed_origin_rules_.AddRuleFromString(rule)) {
diff --git a/android_webview/browser/js_java_interaction/js_java_configurator_host.h b/android_webview/browser/js_java_interaction/js_java_configurator_host.h index 16cc3da..b943dab 100644 --- a/android_webview/browser/js_java_interaction/js_java_configurator_host.h +++ b/android_webview/browser/js_java_interaction/js_java_configurator_host.h
@@ -5,11 +5,9 @@ #ifndef ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_JAVA_CONFIGURATOR_HOST_H_ #define ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_JAVA_CONFIGURATOR_HOST_H_ -#include <string> -#include <vector> - #include "android_webview/common/js_java_interaction/interfaces.mojom.h" #include "base/android/scoped_java_ref.h" +#include "base/strings/string16.h" #include "content/public/browser/web_contents_observer.h" #include "net/proxy_resolution/proxy_bypass_rules.h" #include "services/network/public/mojom/proxy_config.mojom.h" @@ -46,7 +44,7 @@ void NotifyFrame(content::RenderFrameHost* render_frame_host); bool need_to_inject_js_object_ = false; - std::string js_object_name_; + base::string16 js_object_name_; // We use ProxyBypassRules because it has the functionality that suitable // here, but it is not for proxy bypass. net::ProxyBypassRules allowed_origin_rules_;
diff --git a/android_webview/common/js_java_interaction/interfaces.mojom b/android_webview/common/js_java_interaction/interfaces.mojom index ab80e75..0fc719b 100644 --- a/android_webview/common/js_java_interaction/interfaces.mojom +++ b/android_webview/common/js_java_interaction/interfaces.mojom
@@ -4,6 +4,7 @@ module android_webview.mojom; +import "mojo/public/mojom/base/string16.mojom"; import "services/network/public/mojom/proxy_config.mojom"; // For JavaScript postMessage() API, implemented by browser. @@ -12,7 +13,8 @@ // the current frame |origin|. // The |message| is an opaque type and the contents are defined by the client // of this API. - PostMessage(string message, array<handle<message_pipe>> ports); + PostMessage(mojo_base.mojom.String16 message, + array<handle<message_pipe>> ports); }; // For browser to configure renderer, implemented by renderer. @@ -21,6 +23,7 @@ // a JavaScript object with the given |js_object_name| based on // |need_to_inject_js_object| flag. Only frames with the origin matches // |allowed_origin_rules| will have the object injected. - SetJsApiService(bool need_to_inject_js_object, string js_object_name, + SetJsApiService(bool need_to_inject_js_object, + mojo_base.mojom.String16 js_object_name, network.mojom.ProxyBypassRules allowed_origin_rules); }; \ No newline at end of file
diff --git a/android_webview/renderer/js_java_interaction/js_binding.cc b/android_webview/renderer/js_java_interaction/js_binding.cc index 65b0d43..9a684e30 100644 --- a/android_webview/renderer/js_java_interaction/js_binding.cc +++ b/android_webview/renderer/js_java_interaction/js_binding.cc
@@ -4,7 +4,6 @@ #include "android_webview/renderer/js_java_interaction/js_binding.h" -#include <string> #include <vector> #include "base/strings/string_util.h" @@ -32,7 +31,7 @@ // static std::unique_ptr<JsBinding> JsBinding::Install( content::RenderFrame* render_frame, - const std::string& js_object_name) { + const base::string16& js_object_name) { CHECK(!js_object_name.empty()) << "JavaScript wrapper name shouldn't be empty"; @@ -72,7 +71,7 @@ } void JsBinding::PostMessage(gin::Arguments* args) { - std::string message; + base::string16 message; if (!args->GetNext(&message)) { args->ThrowError(); return;
diff --git a/android_webview/renderer/js_java_interaction/js_binding.h b/android_webview/renderer/js_java_interaction/js_binding.h index 4e37254..5c28c09 100644 --- a/android_webview/renderer/js_java_interaction/js_binding.h +++ b/android_webview/renderer/js_java_interaction/js_binding.h
@@ -9,6 +9,7 @@ #include "android_webview/common/js_java_interaction/interfaces.mojom.h" #include "base/auto_reset.h" +#include "base/strings/string16.h" #include "gin/arguments.h" #include "gin/wrappable.h" #include "mojo/public/cpp/bindings/associated_remote.h" @@ -27,8 +28,9 @@ class JsBinding : public gin::Wrappable<JsBinding> { public: static gin::WrapperInfo kWrapperInfo; - static std::unique_ptr<JsBinding> Install(content::RenderFrame* render_frame, - const std::string& js_object_name); + static std::unique_ptr<JsBinding> Install( + content::RenderFrame* render_frame, + const base::string16& js_object_name); ~JsBinding() final;
diff --git a/android_webview/renderer/js_java_interaction/js_java_configurator.cc b/android_webview/renderer/js_java_interaction/js_java_configurator.cc index 701e4b0b..6b2e974 100644 --- a/android_webview/renderer/js_java_interaction/js_java_configurator.cc +++ b/android_webview/renderer/js_java_interaction/js_java_configurator.cc
@@ -25,7 +25,7 @@ void JsJavaConfigurator::SetJsApiService( bool need_to_inject_js_object, - const std::string& js_object_name, + const base::string16& js_object_name, const net::ProxyBypassRules& allowed_origin_rules) { need_to_inject_js_object_ = need_to_inject_js_object; js_object_name_ = js_object_name;
diff --git a/android_webview/renderer/js_java_interaction/js_java_configurator.h b/android_webview/renderer/js_java_interaction/js_java_configurator.h index ed0b078..048b0c0 100644 --- a/android_webview/renderer/js_java_interaction/js_java_configurator.h +++ b/android_webview/renderer/js_java_interaction/js_java_configurator.h
@@ -5,10 +5,8 @@ #ifndef ANDROID_WEBVIEW_RENDERER_JS_JAVA_INTERACTION_JS_JAVA_CONFIGURATOR_H_ #define ANDROID_WEBVIEW_RENDERER_JS_JAVA_INTERACTION_JS_JAVA_CONFIGURATOR_H_ -#include <string> -#include <vector> - #include "android_webview/common/js_java_interaction/interfaces.mojom.h" +#include "base/strings/string16.h" #include "content/public/renderer/render_frame_observer.h" #include "mojo/public/cpp/bindings/associated_receiver.h" #include "net/proxy_resolution/proxy_bypass_rules.h" @@ -30,7 +28,7 @@ // mojom::Configurator implementation void SetJsApiService( bool inject_js_object, - const std::string& js_object_name, + const base::string16& js_object_name, const net::ProxyBypassRules& allowed_origin_rules) override; // RenderFrameObserver implementation @@ -45,7 +43,7 @@ bool IsOriginMatch(); bool need_to_inject_js_object_ = false; - std::string js_object_name_; + base::string16 js_object_name_; // We use ProxyBypassRules because it has the functionality that suitable // here, but it is not for proxy bypass. net::ProxyBypassRules js_object_allowed_origin_rules_;
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 5759f80b..b7f1e63 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -1527,6 +1527,9 @@ <message name="IDS_ASH_LOGIN_POD_PASSWORD_TAP_PLACEHOLDER" desc="Text to display as placeholder in the password field when the user can click/tap their profile picture to unlock."> Select your photo </message> + <message name="IDS_ASH_LOGIN_POD_PASSWORD_SMART_CARD_PIN_PLACEHOLDER" desc="Text to display as placeholder in the password field on the Login or Lock screen when the smart card PIN is requested."> + Smart card PIN + </message> <message name="IDS_ASH_LOGIN_ERROR_AUTHENTICATING" desc="Couldn't sign in because password is invalid"> Sorry, your password couldn't be verified. Please try again. </message> @@ -1692,6 +1695,12 @@ <message name="IDS_ASH_LOGIN_PARENT_ACCESS_NEXT_NUMBER_PROMPT" desc="Accessible prompt read when next access code input field has been focused. Asks user to enter next piece of the access code."> Next number </message> + <message name="IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_MESSAGE" desc="Label for the button shown in the user pod that starts signing in via a smart card."> + Sign in with smart card + </message> + <message name="IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_FAILURE_MESSAGE" desc="Message shown in the user pod when the sign in attempt via a smart card fails."> + Couldn’t recognize your smart card. Try again. + </message> <!-- Multi-profiles intro dialog --> <message name="IDS_ASH_MULTIPROFILES_INTRO_HEADLINE" desc="Describes which feature multi-profiles intro dialog presents.">
diff --git a/ash/ash_strings_grd/IDS_ASH_LOGIN_POD_PASSWORD_SMART_CARD_PIN_PLACEHOLDER.png.sha1 b/ash/ash_strings_grd/IDS_ASH_LOGIN_POD_PASSWORD_SMART_CARD_PIN_PLACEHOLDER.png.sha1 new file mode 100644 index 0000000..5a7e621 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_LOGIN_POD_PASSWORD_SMART_CARD_PIN_PLACEHOLDER.png.sha1
@@ -0,0 +1 @@ +c29aeb9b9a37e6d56634bc3c62093819ac29da9d \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_FAILURE_MESSAGE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_FAILURE_MESSAGE.png.sha1 new file mode 100644 index 0000000..9cdb1de38 --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_FAILURE_MESSAGE.png.sha1
@@ -0,0 +1 @@ +c861ffeea450a04e09976aa3583b47b224a248c1 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_MESSAGE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_MESSAGE.png.sha1 new file mode 100644 index 0000000..f879d6f --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_MESSAGE.png.sha1
@@ -0,0 +1 @@ +ea11d470a9b22d0378b31cf3aab15e1c2e8d3f4c \ No newline at end of file
diff --git a/ash/login/ui/lock_screen_media_controls_view.cc b/ash/login/ui/lock_screen_media_controls_view.cc index 43b2398..a296126 100644 --- a/ash/login/ui/lock_screen_media_controls_view.cc +++ b/ash/login/ui/lock_screen_media_controls_view.cc
@@ -19,12 +19,15 @@ #include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" #include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/message_center/message_center.h" #include "ui/message_center/vector_icons.h" #include "ui/views/background.h" #include "ui/views/controls/button/image_button_factory.h" #include "ui/views/controls/image_view.h" +#include "ui/views/controls/label.h" #include "ui/views/layout/box_layout.h" namespace ash { @@ -48,6 +51,8 @@ constexpr int kMinimumArtworkSize = 50; constexpr int kDesiredArtworkSize = 80; constexpr gfx::Size kArtworkSize = gfx::Size(80, 80); +constexpr int kArtworkRowSeparator = 10; +constexpr gfx::Size kArtworkRowPreferredSize = gfx::Size(300, 10); constexpr gfx::Size kMediaButtonSize = gfx::Size(38, 38); constexpr int kMediaButtonRowSeparator = 24; constexpr gfx::Insets kButtonRowInsets = gfx::Insets(10, 0, 0, 0); @@ -156,11 +161,54 @@ std::make_unique<MediaControlsHeaderView>(base::BindOnce( &LockScreenMediaControlsView::Dismiss, base::Unretained(this)))); + // |artwork_row| contains the session artwork, artist and track info. + auto artwork_row = std::make_unique<NonAccessibleView>(); + artwork_row->SetPreferredSize(kArtworkRowPreferredSize); + auto* artwork_row_layout = + artwork_row->SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), + kArtworkRowSeparator)); + artwork_row_layout->set_cross_axis_alignment( + views::BoxLayout::CrossAxisAlignment::kCenter); + artwork_row_layout->set_main_axis_alignment( + views::BoxLayout::MainAxisAlignment::kStart); + auto session_artwork = std::make_unique<views::ImageView>(); session_artwork->SetPreferredSize(kArtworkSize); - session_artwork->SetHorizontalAlignment( - views::ImageView::Alignment::kLeading); - session_artwork_ = contents_view_->AddChildView(std::move(session_artwork)); + session_artwork_ = artwork_row->AddChildView(std::move(session_artwork)); + + // |track_column| contains the title and artist labels of the current media + // session. + auto track_column = std::make_unique<NonAccessibleView>(); + auto* track_column_layout = + track_column->SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical)); + track_column_layout->set_main_axis_alignment( + views::BoxLayout::MainAxisAlignment::kCenter); + + const gfx::FontList& base_font_list = views::Label::GetDefaultFontList(); + + auto title_label = std::make_unique<views::Label>(); + title_label->SetFontList(base_font_list.Derive( + 2, gfx::Font::FontStyle::NORMAL, gfx::Font::Weight::BOLD)); + title_label->SetEnabledColor(SK_ColorWHITE); + title_label->SetAutoColorReadabilityEnabled(false); + title_label->SetElideBehavior(gfx::ELIDE_TAIL); + title_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT); + title_label_ = track_column->AddChildView(std::move(title_label)); + + auto artist_label = std::make_unique<views::Label>(); + artist_label->SetFontList(base_font_list.Derive( + 0, gfx::Font::FontStyle::NORMAL, gfx::Font::Weight::LIGHT)); + artist_label->SetEnabledColor(SK_ColorWHITE); + artist_label->SetAutoColorReadabilityEnabled(false); + artist_label->SetElideBehavior(gfx::ELIDE_TAIL); + artist_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT); + artist_label_ = track_column->AddChildView(std::move(artist_label)); + + artwork_row->AddChildView(std::move(track_column)); + + contents_view_->AddChildView(std::move(artwork_row)); progress_ = contents_view_->AddChildView( std::make_unique<media_message_center::MediaControlsProgressView>( @@ -340,6 +388,9 @@ : session_metadata.source_title; header_row_->SetAppName(source_title); + title_label_->SetText(session_metadata.title); + artist_label_->SetText(session_metadata.artist); + accessible_name_ = media_message_center::GetAccessibleNameFromMetadata(session_metadata); } @@ -517,16 +568,19 @@ media_message_center::GetTopVisibleActions(enabled_actions_, ignored_actions, kMaxActions); + bool should_invalidate = false; for (auto* view : button_row_->children()) { views::Button* action_button = views::Button::AsButton(view); bool should_show = base::Contains( visible_actions, media_message_center::GetActionFromButtonTag(*action_button)); + should_invalidate |= should_show != action_button->GetVisible(); action_button->SetVisible(should_show); } - PreferredSizeChanged(); + if (should_invalidate) + button_row_->InvalidateLayout(); } void LockScreenMediaControlsView::SetIsPlaying(bool playing) {
diff --git a/ash/login/ui/lock_screen_media_controls_view.h b/ash/login/ui/lock_screen_media_controls_view.h index 32ba818..630f7f7 100644 --- a/ash/login/ui/lock_screen_media_controls_view.h +++ b/ash/login/ui/lock_screen_media_controls_view.h
@@ -20,6 +20,7 @@ } namespace views { +class Label; class ImageView; class ToggleImageButton; } // namespace views @@ -200,6 +201,8 @@ // Container views attached to |contents_view_|. MediaControlsHeaderView* header_row_ = nullptr; views::ImageView* session_artwork_ = nullptr; + views::Label* title_label_ = nullptr; + views::Label* artist_label_ = nullptr; NonAccessibleView* button_row_ = nullptr; views::ToggleImageButton* play_pause_button_ = nullptr; media_message_center::MediaControlsProgressView* progress_ = nullptr;
diff --git a/ash/login/ui/lock_screen_media_controls_view_unittest.cc b/ash/login/ui/lock_screen_media_controls_view_unittest.cc index 297ef3e..5078f15 100644 --- a/ash/login/ui/lock_screen_media_controls_view_unittest.cc +++ b/ash/login/ui/lock_screen_media_controls_view_unittest.cc
@@ -21,6 +21,7 @@ #include "ui/views/animation/bounds_animator.h" #include "ui/views/animation/bounds_animator_observer.h" #include "ui/views/controls/image_view.h" +#include "ui/views/controls/label.h" namespace ash { @@ -202,6 +203,14 @@ return media_controls_view_->session_artwork_; } + views::Label* title_label() const { + return media_controls_view_->title_label_; + } + + views::Label* artist_label() const { + return media_controls_view_->artist_label_; + } + media_message_center::MediaControlsProgressView* progress_view() const { return media_controls_view_->progress_; } @@ -237,18 +246,26 @@ }; TEST_F(LockScreenMediaControlsViewTest, DoNotUpdateMetadataBetweenSessions) { - // Set app name for current session + // Set metadata for current session media_session::MediaMetadata metadata; metadata.source_title = kTestAppName; + metadata.title = base::ASCIIToUTF16("title"); + metadata.artist = base::ASCIIToUTF16("artist"); + media_controls_view_->MediaSessionMetadataChanged(metadata); // Simulate new media session starting. metadata.source_title = base::ASCIIToUTF16("AppName2"); + metadata.title = base::ASCIIToUTF16("title2"); + metadata.artist = base::ASCIIToUTF16("artist2"); + SimulateMediaSessionChanged( media_session::mojom::MediaPlaybackState::kPlaying); media_controls_view_->MediaSessionMetadataChanged(metadata); EXPECT_EQ(kTestAppName, GetAppName()); + EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText()); + EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText()); } TEST_F(LockScreenMediaControlsViewTest, DoNotUpdateArtworkBetweenSessions) { @@ -533,7 +550,7 @@ EXPECT_EQ(kAppIconSize, icon_view()->GetImage().height()); } -TEST_F(LockScreenMediaControlsViewTest, UpdateAppName) { +TEST_F(LockScreenMediaControlsViewTest, UpdateMetadata) { // Verify that the app name is initialized to the default. EXPECT_EQ( message_center::MessageCenter::Get()->GetSystemNotificationAppName(), @@ -542,16 +559,21 @@ media_session::MediaMetadata metadata; media_controls_view_->MediaSessionMetadataChanged(metadata); - // Verify that default name is used if no name is provided. + // Verify that default app name is used if no name is provided. EXPECT_EQ( message_center::MessageCenter::Get()->GetSystemNotificationAppName(), GetAppName()); metadata.source_title = kTestAppName; + metadata.title = base::ASCIIToUTF16("title"); + metadata.artist = base::ASCIIToUTF16("artist"); + media_controls_view_->MediaSessionMetadataChanged(metadata); - // Verify that the provided app name is used. + // Verify that the provided data is used. EXPECT_EQ(kTestAppName, GetAppName()); + EXPECT_EQ(metadata.title, title_label()->GetText()); + EXPECT_EQ(metadata.artist, artist_label()->GetText()); } TEST_F(LockScreenMediaControlsViewTest, UpdateImagesConvertColors) {
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc index 8013578c..e352e58 100644 --- a/ash/login/ui/login_auth_user_view.cc +++ b/ash/login/ui/login_auth_user_view.cc
@@ -533,14 +533,14 @@ } base::string16 GetTextForLabelButton() const { - // TODO(crbug.com/983103): Put localized strings after the string review. switch (state_) { case State::kInitial: case State::kAuthenticating: - return base::ASCIIToUTF16("Unlock with smart card"); + return l10n_util::GetStringUTF16( + IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_MESSAGE); case State::kFailure: - return base::UTF8ToUTF16( - "Couldn’t recognize your smart card. Try again."); + return l10n_util::GetStringUTF16( + IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_FAILURE_MESSAGE); } } @@ -948,8 +948,8 @@ // Note: both |security_token_pin_request_| and |has_tap| must have higher // priority than |has_pin| when determining the placeholder. if (security_token_pin_request_) { - // TODO(crbug.com/983103): Put the localized string after the string review. - password_view_->SetPlaceholderText(base::UTF8ToUTF16("smart card PIN")); + password_view_->SetPlaceholderText(l10n_util::GetStringUTF16( + IDS_ASH_LOGIN_POD_PASSWORD_SMART_CARD_PIN_PLACEHOLDER)); } else if (has_tap) { password_view_->SetPlaceholderText( l10n_util::GetStringUTF16(IDS_ASH_LOGIN_POD_PASSWORD_TAP_PLACEHOLDER));
diff --git a/ash/login/ui/media_controls_header_view.cc b/ash/login/ui/media_controls_header_view.cc index bb98d2f..15cd7dd 100644 --- a/ash/login/ui/media_controls_header_view.cc +++ b/ash/login/ui/media_controls_header_view.cc
@@ -30,16 +30,29 @@ constexpr int kHeaderTextFontSize = 12; constexpr gfx::Insets kIconPadding = gfx::Insets(1, 1, 1, 1); constexpr gfx::Insets kAppNamePadding = gfx::Insets(0, 10, 0, 0); +constexpr gfx::Size kAppNamePreferredSize = gfx::Size(200, 10); constexpr int kIconCornerRadius = 1; constexpr gfx::Size kCloseButtonSize = gfx::Size(20, 20); constexpr int kCloseButtonIconSize = 18; -constexpr gfx::Size kSpacerPreferredSize = gfx::Size(10, 5); +constexpr gfx::Size kSpacerPreferredSize = gfx::Size(5, 5); } // namespace MediaControlsHeaderView::MediaControlsHeaderView( base::OnceClosure close_button_cb) : close_button_cb_(std::move(close_button_cb)) { + const views::FlexSpecification kAppNameFlex = + views::FlexSpecification::ForSizeRule( + views::MinimumFlexSizeRule::kScaleToZero, + views::MaximumFlexSizeRule::kPreferred) + .WithOrder(1); + + const views::FlexSpecification kSpacerFlex = + views::FlexSpecification::ForSizeRule( + views::MinimumFlexSizeRule::kScaleToMinimum, + views::MaximumFlexSizeRule::kUnbounded) + .WithOrder(2); + auto* layout = SetLayoutManager(std::make_unique<views::FlexLayout>()); layout->SetInteriorMargin(kHeaderViewInsets); @@ -65,15 +78,15 @@ app_name_view->SetEnabledColor(SK_ColorWHITE); app_name_view->SetAutoColorReadabilityEnabled(false); app_name_view->SetBorder(views::CreateEmptyBorder(kAppNamePadding)); + app_name_view->SetPreferredSize(kAppNamePreferredSize); + app_name_view->SetProperty(views::kFlexBehaviorKey, kAppNameFlex); + app_name_view->SetElideBehavior(gfx::ELIDE_TAIL); app_name_view_ = AddChildView(std::move(app_name_view)); // Space between app name and close button. auto spacer = std::make_unique<NonAccessibleView>(); spacer->SetPreferredSize(kSpacerPreferredSize); - spacer->SetProperty(views::kFlexBehaviorKey, - views::FlexSpecification::ForSizeRule( - views::MinimumFlexSizeRule::kScaleToMinimum, - views::MaximumFlexSizeRule::kUnbounded)); + spacer->SetProperty(views::kFlexBehaviorKey, kSpacerFlex); AddChildView(std::move(spacer)); auto close_button = CreateVectorImageButton(this);
diff --git a/ash/public/cpp/session/session_controller.h b/ash/public/cpp/session/session_controller.h index f9386c8..bf5fc61 100644 --- a/ash/public/cpp/session/session_controller.h +++ b/ash/public/cpp/session/session_controller.h
@@ -77,9 +77,6 @@ // Adds a countdown timer to the system tray menu and creates or updates a // notification saying the session length is limited (e.g. a public session in // a library). Setting |length_limit| to zero removes the notification. - // NOTE: Chrome enforces the limit, not ash. Ash could enforce it if local - // state prefs and user activity monitoring were available under mustash. - // http://crbug.com/729808 virtual void SetSessionLengthLimit(base::TimeDelta length_limit, base::TimeTicks start_time) = 0;
diff --git a/ash/style/default_color_constants.h b/ash/style/default_color_constants.h index 32fe744..f211dae 100644 --- a/ash/style/default_color_constants.h +++ b/ash/style/default_color_constants.h
@@ -25,6 +25,8 @@ SkColorSetA(SK_ColorBLACK, 0x23); // 14% constexpr SkColor kSeparatorOnDarkBackgroundColor = SkColorSetA(SK_ColorWHITE, 0x23); // 14% + +constexpr SkColor kNotificationBackgroundColor = SK_ColorWHITE; //----------------------------------End---------------------------------------- #endif // ASH_STYLE_DEFAULT_COLOR_CONSTANTS_H_
diff --git a/ash/system/audio/unified_volume_view.cc b/ash/system/audio/unified_volume_view.cc index 03e9005..de6a30b 100644 --- a/ash/system/audio/unified_volume_view.cc +++ b/ash/system/audio/unified_volume_view.cc
@@ -9,6 +9,7 @@ #include "ash/system/audio/unified_volume_slider_controller.h" #include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_popup_utils.h" +#include "ash/system/unified/unified_system_tray_view.h" #include "base/i18n/rtl.h" #include "base/stl_util.h" #include "components/vector_icons/vector_icons.h" @@ -110,13 +111,15 @@ std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override { return TrayPopupUtils::CreateInkDropRipple( TrayPopupInkDropStyle::FILL_BOUNDS, this, - GetInkDropCenterBasedOnLastEvent(), kIconOnDarkBackgroundColor); + GetInkDropCenterBasedOnLastEvent(), + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() const override { return TrayPopupUtils::CreateInkDropHighlight( - TrayPopupInkDropStyle::FILL_BOUNDS, this, kIconOnDarkBackgroundColor); + TrayPopupInkDropStyle::FILL_BOUNDS, this, + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override {
diff --git a/ash/system/message_center/unified_message_center_view.cc b/ash/system/message_center/unified_message_center_view.cc index ac83bdd..1b5ed74 100644 --- a/ash/system/message_center/unified_message_center_view.cc +++ b/ash/system/message_center/unified_message_center_view.cc
@@ -27,7 +27,6 @@ #include "ui/gfx/animation/linear_animation.h" #include "ui/gfx/canvas.h" #include "ui/message_center/message_center.h" -#include "ui/message_center/public/cpp/message_center_constants.h" #include "ui/message_center/views/message_view.h" #include "ui/views/animation/flood_fill_ink_drop_ripple.h" #include "ui/views/animation/ink_drop_highlight.h" @@ -91,6 +90,10 @@ label()->SetFontList(views::Label::GetDefaultFontList().Derive( 1, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM)); TrayPopupUtils::ConfigureTrayPopupButton(this); + + background_color_ = AshColorProvider::Get()->DeprecatedGetBaseLayerColor( + AshColorProvider::BaseLayerType::kTransparentWithoutBlur, + kNotificationBackgroundColor); } ~StackingBarClearAllButton() override = default; @@ -126,13 +129,13 @@ std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override { return TrayPopupUtils::CreateInkDropRipple( TrayPopupInkDropStyle::FILL_BOUNDS, this, - GetInkDropCenterBasedOnLastEvent(), SK_ColorBLACK); + GetInkDropCenterBasedOnLastEvent(), background_color_); } std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() const override { return TrayPopupUtils::CreateInkDropHighlight( - TrayPopupInkDropStyle::FILL_BOUNDS, this, SK_ColorBLACK); + TrayPopupInkDropStyle::FILL_BOUNDS, this, background_color_); } std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override { @@ -152,6 +155,8 @@ } private: + SkColor background_color_ = gfx::kPlaceholderColor; + DISALLOW_COPY_AND_ASSIGN(StackingBarClearAllButton); }; @@ -227,7 +232,9 @@ void StackingNotificationCounterView::OnPaint(gfx::Canvas* canvas) { cc::PaintFlags flags; - flags.setColor(message_center::kNotificationBackgroundColor); + flags.setColor(AshColorProvider::Get()->DeprecatedGetBaseLayerColor( + AshColorProvider::BaseLayerType::kTransparentWithoutBlur, + kNotificationBackgroundColor)); flags.setStyle(cc::PaintFlags::kFill_Style); flags.setAntiAlias(true);
diff --git a/ash/system/network/vpn_list_view.cc b/ash/system/network/vpn_list_view.cc index b499ae40..35b4475dd1 100644 --- a/ash/system/network/vpn_list_view.cc +++ b/ash/system/network/vpn_list_view.cc
@@ -26,6 +26,7 @@ #include "ash/system/tray/tray_popup_utils.h" #include "ash/system/tray/tri_view.h" #include "ash/system/tray/view_click_listener.h" +#include "ash/system/unified/unified_system_tray_view.h" #include "base/strings/utf_string_conversions.h" #include "chromeos/network/network_connect.h" #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h" @@ -127,7 +128,8 @@ gfx::CreateVectorIcon(kSystemMenuAddConnectionIcon, image_color); SystemMenuButton* add_vpn_button = new SystemMenuButton(this, icon, icon, button_accessible_name_id); - add_vpn_button->SetInkDropColor(image_color); + add_vpn_button->SetInkDropColor( + UnifiedSystemTrayView::GetBackgroundColor()); add_vpn_button->SetEnabled(true); tri_view->AddView(TriView::Container::END, add_vpn_button); }
diff --git a/ash/system/tray/actionable_view.cc b/ash/system/tray/actionable_view.cc index 7da908d..803e7fe 100644 --- a/ash/system/tray/actionable_view.cc +++ b/ash/system/tray/actionable_view.cc
@@ -4,8 +4,6 @@ #include "ash/system/tray/actionable_view.h" -#include "ash/public/cpp/ash_constants.h" -#include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_popup_utils.h" #include "ui/accessibility/ax_node_data.h" #include "ui/events/keycodes/keyboard_codes.h" @@ -28,8 +26,6 @@ destroyed_(nullptr), ink_drop_style_(ink_drop_style) { SetFocusBehavior(FocusBehavior::ALWAYS); - set_ink_drop_base_color(kTrayPopupInkDropBaseColor); - set_ink_drop_visible_opacity(kTrayPopupInkDropRippleOpacity); set_has_ink_drop_action_on_click(false); set_notify_enter_exit_on_child(true); SetFocusPainter(TrayPopupUtils::CreateFocusPainter()); @@ -77,13 +73,18 @@ std::unique_ptr<views::InkDropRipple> ActionableView::CreateInkDropRipple() const { + // TODO(minch): Do not hard code the background color. Add it as a constructor + // argument to ActionableView. return TrayPopupUtils::CreateInkDropRipple( - ink_drop_style_, this, GetInkDropCenterBasedOnLastEvent()); + ink_drop_style_, this, GetInkDropCenterBasedOnLastEvent(), SK_ColorWHITE); } std::unique_ptr<views::InkDropHighlight> ActionableView::CreateInkDropHighlight() const { - return TrayPopupUtils::CreateInkDropHighlight(ink_drop_style_, this); + // TODO(minch): Do not hard code the background color. Add it as a constructor + // argument to ActionableView. + return TrayPopupUtils::CreateInkDropHighlight(ink_drop_style_, this, + SK_ColorWHITE); } void ActionableView::ButtonPressed(Button* sender, const ui::Event& event) {
diff --git a/ash/system/tray/system_menu_button.cc b/ash/system/tray/system_menu_button.cc index ecd0d7a..d0eb83c 100644 --- a/ash/system/tray/system_menu_button.cc +++ b/ash/system/tray/system_menu_button.cc
@@ -4,8 +4,6 @@ #include "ash/system/tray/system_menu_button.h" -#include "ash/public/cpp/ash_constants.h" -#include "ash/public/cpp/ash_features.h" #include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_popup_ink_drop_style.h" #include "ash/system/tray/tray_popup_utils.h" @@ -14,9 +12,6 @@ #include "ui/views/animation/flood_fill_ink_drop_ripple.h" #include "ui/views/animation/ink_drop_highlight.h" #include "ui/views/animation/ink_drop_impl.h" -#include "ui/views/animation/ink_drop_mask.h" -#include "ui/views/animation/square_ink_drop_ripple.h" -#include "ui/views/painter.h" #include "ui/views/view_class_properties.h" namespace ash { @@ -76,19 +71,22 @@ return TrayPopupUtils::CreateInkDrop(this); } +// TODO(minch): Do not hard code the background color for InkDropRipple and +// InkDropHighlight. Add it as a constructor argument to SystemMenuButton. +// Then, |ink_drop_color_| related logic can be removed. std::unique_ptr<views::InkDropRipple> SystemMenuButton::CreateInkDropRipple() const { return TrayPopupUtils::CreateInkDropRipple( TrayPopupInkDropStyle::HOST_CENTERED, this, GetInkDropCenterBasedOnLastEvent(), - ink_drop_color_.value_or(kTrayPopupInkDropBaseColor)); + ink_drop_color_.value_or(SK_ColorWHITE)); } std::unique_ptr<views::InkDropHighlight> SystemMenuButton::CreateInkDropHighlight() const { return TrayPopupUtils::CreateInkDropHighlight( TrayPopupInkDropStyle::HOST_CENTERED, this, - ink_drop_color_.value_or(kTrayPopupInkDropBaseColor)); + ink_drop_color_.value_or(SK_ColorWHITE)); } const char* SystemMenuButton::GetClassName() const {
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc index 72305fc..12fd5eff 100644 --- a/ash/system/tray/tray_background_view.cc +++ b/ash/system/tray/tray_background_view.cc
@@ -17,6 +17,7 @@ #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" +#include "ash/style/ash_color_provider.h" #include "ash/system/model/system_tray_model.h" #include "ash/system/status_area_widget.h" #include "ash/system/status_area_widget_delegate.h" @@ -293,9 +294,13 @@ std::unique_ptr<views::InkDropRipple> TrayBackgroundView::CreateInkDropRipple() const { + // TODO(minch): Do not hard code the color, get the background color of shelf + // instead. + const AshColorProvider::RippleAttributes ripple_attributes = + AshColorProvider::Get()->GetRippleAttributes(SK_ColorBLACK); return std::make_unique<views::FloodFillInkDropRipple>( size(), GetBackgroundInsets(), GetInkDropCenterBasedOnLastEvent(), - GetInkDropBaseColor(), ink_drop_visible_opacity()); + ripple_attributes.base_color, ripple_attributes.inkdrop_opacity); } std::unique_ptr<views::InkDropHighlight> @@ -310,11 +315,15 @@ const int icon_size = kTrayIconSize + 2 * kTrayImageItemPadding; bounds.set_width(bounds.width() + 2 * icon_size); bounds.set_height(bounds.height() + 2 * icon_size); + // TODO(minch): Do not hard code the color, get the background color of shelf + // instead. + const AshColorProvider::RippleAttributes ripple_attributes = + AshColorProvider::Get()->GetRippleAttributes(SK_ColorBLACK); std::unique_ptr<views::InkDropHighlight> highlight( new views::InkDropHighlight(bounds.size(), 0, gfx::RectF(bounds).CenterPoint(), - GetInkDropBaseColor())); - highlight->set_visible_opacity(kTrayPopupInkDropHighlightOpacity); + ripple_attributes.base_color)); + highlight->set_visible_opacity(ripple_attributes.highlight_opacity); return highlight; } @@ -475,5 +484,4 @@ return insets; } - } // namespace ash
diff --git a/ash/system/tray/tray_constants.cc b/ash/system/tray/tray_constants.cc index 8f35a1a5..5557ea8e 100644 --- a/ash/system/tray/tray_constants.cc +++ b/ash/system/tray/tray_constants.cc
@@ -47,9 +47,6 @@ const int kHitRegionPadding = 4; const int kHitRegionPaddingDense = 2; -const SkColor kTrayPopupInkDropBaseColor = SK_ColorBLACK; -const float kTrayPopupInkDropRippleOpacity = 0.06f; -const float kTrayPopupInkDropHighlightOpacity = 0.08f; const int kTrayPopupInkDropInset = 4; const int kTrayPopupInkDropCornerRadius = 2;
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h index 8ea7572e..3901a2f 100644 --- a/ash/system/tray/tray_constants.h +++ b/ash/system/tray/tray_constants.h
@@ -104,16 +104,6 @@ // The visual padding to the left of icons in the system menu. extern const int kMenuEdgeEffectivePadding; -// The base color used for all ink drops in the system menu. -extern const SkColor kTrayPopupInkDropBaseColor; - -// The opacity of the ink drop ripples for all ink drops in the system menu. -extern const float kTrayPopupInkDropRippleOpacity; - -// The opacity of the ink drop ripples for all ink highlights in the system -// menu. -extern const float kTrayPopupInkDropHighlightOpacity; - // The inset applied to clickable surfaces in the system menu that do not have // the ink drop filling the entire bounds. extern const int kTrayPopupInkDropInset; @@ -133,8 +123,6 @@ SkColorSetRGB(0x25, 0x81, 0xdf); constexpr SkColor kUnifiedMenuButtonColorDisabled = SkColorSetA(kUnifiedMenuButtonColor, 0xa); -constexpr SkColor kUnifiedFeaturePodHoverColor = - SkColorSetRGB(0xff, 0xff, 0xff); constexpr SkColor kUnifiedRecordingIconColor = gfx::kGoogleRed300; constexpr gfx::Insets kUnifiedMenuItemPadding(0, 16, 16, 16);
diff --git a/ash/system/tray/tray_popup_utils.cc b/ash/system/tray/tray_popup_utils.cc index 797c7a1..cd179c36 100644 --- a/ash/system/tray/tray_popup_utils.cc +++ b/ash/system/tray/tray_popup_utils.cc
@@ -198,12 +198,8 @@ void TrayPopupUtils::ConfigureTrayPopupButton(views::Button* button) { button->SetInstallFocusRingOnFocus(true); button->SetFocusForPlatform(); - button->SetInkDropMode(views::InkDropHostView::InkDropMode::ON); button->set_has_ink_drop_action_on_click(true); - button->set_ink_drop_base_color(kTrayPopupInkDropBaseColor); - button->set_ink_drop_visible_opacity(kTrayPopupInkDropRippleOpacity); - button->set_ink_drop_highlight_opacity(kTrayPopupInkDropHighlightOpacity); } void TrayPopupUtils::ConfigureAsStickyHeader(views::View* view) { @@ -251,22 +247,28 @@ TrayPopupInkDropStyle ink_drop_style, const views::View* host, const gfx::Point& center_point, - SkColor color) { + SkColor background_color) { + const AshColorProvider::RippleAttributes ripple_attributes = + AshColorProvider::Get()->GetRippleAttributes(background_color); return std::make_unique<views::FloodFillInkDropRipple>( host->size(), TrayPopupUtils::GetInkDropInsets(ink_drop_style), - center_point, color, kTrayPopupInkDropRippleOpacity); + center_point, ripple_attributes.base_color, + ripple_attributes.inkdrop_opacity); } std::unique_ptr<views::InkDropHighlight> TrayPopupUtils::CreateInkDropHighlight( TrayPopupInkDropStyle ink_drop_style, const views::View* host, - SkColor color) { + SkColor background_color) { + const AshColorProvider::RippleAttributes ripple_attributes = + AshColorProvider::Get()->GetRippleAttributes(background_color); const gfx::Rect bounds = TrayPopupUtils::GetInkDropBounds(ink_drop_style, host); std::unique_ptr<views::InkDropHighlight> highlight( new views::InkDropHighlight(bounds.size(), 0, - gfx::PointF(bounds.CenterPoint()), color)); - highlight->set_visible_opacity(kTrayPopupInkDropHighlightOpacity); + gfx::PointF(bounds.CenterPoint()), + ripple_attributes.base_color)); + highlight->set_visible_opacity(ripple_attributes.highlight_opacity); return highlight; }
diff --git a/ash/system/tray/tray_popup_utils.h b/ash/system/tray/tray_popup_utils.h index 06ee842..b6337fa 100644 --- a/ash/system/tray/tray_popup_utils.h +++ b/ash/system/tray/tray_popup_utils.h
@@ -154,7 +154,7 @@ TrayPopupInkDropStyle ink_drop_style, const views::View* host, const gfx::Point& center_point, - SkColor color = kTrayPopupInkDropBaseColor); + SkColor background_color); // Creates in InkDropHighlight instance for |host| according to the // |ink_drop_style|. @@ -164,7 +164,7 @@ static std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight( TrayPopupInkDropStyle ink_drop_style, const views::View* host, - SkColor color = kTrayPopupInkDropBaseColor); + SkColor background_color); // Creates a SkPath matching the TrayPopupInkDropStyle. This path is normally // used to generate the focus ring and ink drop shapes.
diff --git a/ash/system/unified/custom_shape_button.cc b/ash/system/unified/custom_shape_button.cc index f0687af..3054f56 100644 --- a/ash/system/unified/custom_shape_button.cc +++ b/ash/system/unified/custom_shape_button.cc
@@ -6,6 +6,7 @@ #include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_popup_utils.h" +#include "ash/system/unified/unified_system_tray_view.h" #include "ui/compositor/paint_recorder.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/skbitmap_operations.h" @@ -71,13 +72,15 @@ const { return TrayPopupUtils::CreateInkDropRipple( TrayPopupInkDropStyle::FILL_BOUNDS, this, - GetInkDropCenterBasedOnLastEvent(), kIconOnDarkBackgroundColor); + GetInkDropCenterBasedOnLastEvent(), + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropHighlight> CustomShapeButton::CreateInkDropHighlight() const { return TrayPopupUtils::CreateInkDropHighlight( - TrayPopupInkDropStyle::FILL_BOUNDS, this, kIconOnDarkBackgroundColor); + TrayPopupInkDropStyle::FILL_BOUNDS, this, + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropMask> CustomShapeButton::CreateInkDropMask()
diff --git a/ash/system/unified/feature_pod_button.cc b/ash/system/unified/feature_pod_button.cc index c6b292c..c617ac2 100644 --- a/ash/system/unified/feature_pod_button.cc +++ b/ash/system/unified/feature_pod_button.cc
@@ -9,6 +9,7 @@ #include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_popup_utils.h" #include "ash/system/unified/feature_pod_controller_base.h" +#include "ash/system/unified/unified_system_tray_view.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/canvas.h" @@ -83,13 +84,15 @@ FeaturePodIconButton::CreateInkDropRipple() const { return TrayPopupUtils::CreateInkDropRipple( TrayPopupInkDropStyle::FILL_BOUNDS, this, - GetInkDropCenterBasedOnLastEvent(), kIconOnDarkBackgroundColor); + GetInkDropCenterBasedOnLastEvent(), + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropHighlight> FeaturePodIconButton::CreateInkDropHighlight() const { return TrayPopupUtils::CreateInkDropHighlight( - TrayPopupInkDropStyle::FILL_BOUNDS, this, kIconOnDarkBackgroundColor); + TrayPopupInkDropStyle::FILL_BOUNDS, this, + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropMask> FeaturePodIconButton::CreateInkDropMask() @@ -189,13 +192,15 @@ FeaturePodLabelButton::CreateInkDropRipple() const { return TrayPopupUtils::CreateInkDropRipple( TrayPopupInkDropStyle::FILL_BOUNDS, this, - GetInkDropCenterBasedOnLastEvent(), kUnifiedFeaturePodHoverColor); + GetInkDropCenterBasedOnLastEvent(), + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropHighlight> FeaturePodLabelButton::CreateInkDropHighlight() const { return TrayPopupUtils::CreateInkDropHighlight( - TrayPopupInkDropStyle::FILL_BOUNDS, this, kUnifiedFeaturePodHoverColor); + TrayPopupInkDropStyle::FILL_BOUNDS, this, + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropMask> FeaturePodLabelButton::CreateInkDropMask()
diff --git a/ash/system/unified/feature_pod_button.h b/ash/system/unified/feature_pod_button.h index 92da75a..d4b8ce6 100644 --- a/ash/system/unified/feature_pod_button.h +++ b/ash/system/unified/feature_pod_button.h
@@ -48,7 +48,7 @@ DISALLOW_COPY_AND_ASSIGN(FeaturePodIconButton); }; -// Buton internally used in FeaturePodButton. Should not be used directly. +// Button internally used in FeaturePodButton. Should not be used directly. class FeaturePodLabelButton : public views::Button { public: explicit FeaturePodLabelButton(views::ButtonListener* listener);
diff --git a/ash/system/unified/rounded_label_button.cc b/ash/system/unified/rounded_label_button.cc index ea77543..cd97c75 100644 --- a/ash/system/unified/rounded_label_button.cc +++ b/ash/system/unified/rounded_label_button.cc
@@ -6,6 +6,7 @@ #include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_popup_utils.h" +#include "ash/system/unified/unified_system_tray_view.h" #include "ui/gfx/canvas.h" #include "ui/views/animation/ink_drop.h" #include "ui/views/animation/ink_drop_highlight.h" @@ -63,13 +64,15 @@ const { return TrayPopupUtils::CreateInkDropRipple( TrayPopupInkDropStyle::FILL_BOUNDS, this, - GetInkDropCenterBasedOnLastEvent(), kIconOnDarkBackgroundColor); + GetInkDropCenterBasedOnLastEvent(), + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropHighlight> RoundedLabelButton::CreateInkDropHighlight() const { return TrayPopupUtils::CreateInkDropHighlight( - TrayPopupInkDropStyle::FILL_BOUNDS, this, kIconOnDarkBackgroundColor); + TrayPopupInkDropStyle::FILL_BOUNDS, this, + UnifiedSystemTrayView::GetBackgroundColor()); } std::unique_ptr<views::InkDropMask> RoundedLabelButton::CreateInkDropMask()
diff --git a/ash/system/unified/top_shortcut_button.cc b/ash/system/unified/top_shortcut_button.cc index 6ef800b..863a482d 100644 --- a/ash/system/unified/top_shortcut_button.cc +++ b/ash/system/unified/top_shortcut_button.cc
@@ -6,6 +6,7 @@ #include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_popup_utils.h" +#include "ash/system/unified/unified_system_tray_view.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/canvas.h" #include "ui/gfx/paint_vector_icon.h" @@ -47,7 +48,6 @@ SetTooltipText(l10n_util::GetStringUTF16(accessible_name_id)); TrayPopupUtils::ConfigureTrayPopupButton(this); - set_ink_drop_base_color(kIconOnDarkBackgroundColor); auto path = std::make_unique<SkPath>(); path->addOval(gfx::RectToSkRect(gfx::Rect(CalculatePreferredSize()))); @@ -74,6 +74,21 @@ return TrayPopupUtils::CreateInkDrop(this); } +std::unique_ptr<views::InkDropRipple> TopShortcutButton::CreateInkDropRipple() + const { + return TrayPopupUtils::CreateInkDropRipple( + TrayPopupInkDropStyle::FILL_BOUNDS, this, + GetInkDropCenterBasedOnLastEvent(), + UnifiedSystemTrayView::GetBackgroundColor()); +} + +std::unique_ptr<views::InkDropHighlight> +TopShortcutButton::CreateInkDropHighlight() const { + return TrayPopupUtils::CreateInkDropHighlight( + TrayPopupInkDropStyle::FILL_BOUNDS, this, + UnifiedSystemTrayView::GetBackgroundColor()); +} + const char* TopShortcutButton::GetClassName() const { return "TopShortcutButton"; }
diff --git a/ash/system/unified/top_shortcut_button.h b/ash/system/unified/top_shortcut_button.h index f29b4e12..f0382a2 100644 --- a/ash/system/unified/top_shortcut_button.h +++ b/ash/system/unified/top_shortcut_button.h
@@ -29,6 +29,9 @@ // views::ImageButton: void PaintButtonContents(gfx::Canvas* canvas) override; std::unique_ptr<views::InkDrop> CreateInkDrop() override; + std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override; + std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() + const override; const char* GetClassName() const override; private:
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc index df0aef7d..cea8ad3 100644 --- a/ash/system/unified/unified_system_tray_view.cc +++ b/ash/system/unified/unified_system_tray_view.cc
@@ -230,6 +230,25 @@ DISALLOW_COPY_AND_ASSIGN(FocusSearch); }; +// static +SkColor UnifiedSystemTrayView::GetBackgroundColor() { + if (app_list_features::IsBackgroundBlurEnabled()) { + return AshColorProvider::Get()->DeprecatedGetBaseLayerColor( + AshColorProvider::BaseLayerType::kTransparentWithBlur, + kUnifiedMenuBackgroundColorWithBlur); + } + return AshColorProvider::Get()->DeprecatedGetBaseLayerColor( + AshColorProvider::BaseLayerType::kTransparentWithoutBlur, + kUnifiedMenuBackgroundColor); +} + +// static +std::unique_ptr<views::Background> UnifiedSystemTrayView::CreateBackground() { + return views::CreateBackgroundFromPainter( + views::Painter::CreateSolidRoundRectPainter(GetBackgroundColor(), + kUnifiedTrayCornerRadius)); +} + UnifiedSystemTrayView::UnifiedSystemTrayView( UnifiedSystemTrayController* controller, bool initially_expanded) @@ -449,23 +468,6 @@ return feature_pods_container_->GetVisibleCount(); } -// static -std::unique_ptr<views::Background> UnifiedSystemTrayView::CreateBackground() { - SkColor bg_color = gfx::kPlaceholderColor; - if (app_list_features::IsBackgroundBlurEnabled()) { - bg_color = AshColorProvider::Get()->DeprecatedGetBaseLayerColor( - AshColorProvider::BaseLayerType::kTransparentWithBlur, - kUnifiedMenuBackgroundColorWithBlur); - } else { - bg_color = AshColorProvider::Get()->DeprecatedGetBaseLayerColor( - AshColorProvider::BaseLayerType::kTransparentWithoutBlur, - kUnifiedMenuBackgroundColor); - } - return views::CreateBackgroundFromPainter( - views::Painter::CreateSolidRoundRectPainter(bg_color, - kUnifiedTrayCornerRadius)); -} - void UnifiedSystemTrayView::OnGestureEvent(ui::GestureEvent* event) { gfx::Point screen_location = event->location(); ConvertPointToScreen(this, &screen_location);
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h index 112bfb9..a07a42e 100644 --- a/ash/system/unified/unified_system_tray_view.h +++ b/ash/system/unified/unified_system_tray_view.h
@@ -60,6 +60,12 @@ class ASH_EXPORT UnifiedSystemTrayView : public views::View, public views::FocusTraversable { public: + // Get the background color of unified system tray. + static SkColor GetBackgroundColor(); + + // Create background of UnifiedSystemTray with rounded corners. + static std::unique_ptr<views::Background> CreateBackground(); + UnifiedSystemTrayView(UnifiedSystemTrayController* controller, bool initially_expanded); ~UnifiedSystemTrayView() override; @@ -119,10 +125,6 @@ // Returns the number of visible feature pods. int GetVisibleFeaturePodCount() const; - // Create background of UnifiedSystemTray that is semi-transparent and has - // rounded corners. - static std::unique_ptr<views::Background> CreateBackground(); - // views::View: void OnGestureEvent(ui::GestureEvent* event) override; void ChildPreferredSizeChanged(views::View* child) override;
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h index c7c8dad..3d55a15e 100644 --- a/ash/wm/overview/overview_grid.h +++ b/ash/wm/overview/overview_grid.h
@@ -304,6 +304,10 @@ views::Widget* drop_target_widget() { return drop_target_widget_.get(); } + OverviewGridPreEventHandler* grid_pre_event_handler() { + return grid_pre_event_handler_.get(); + } + private: class TargetWindowObserver; friend class OverviewSessionTest;
diff --git a/ash/wm/overview/overview_grid_pre_event_handler.h b/ash/wm/overview/overview_grid_pre_event_handler.h index 6a42b90..d6cb178d 100644 --- a/ash/wm/overview/overview_grid_pre_event_handler.h +++ b/ash/wm/overview/overview_grid_pre_event_handler.h
@@ -28,11 +28,11 @@ explicit OverviewGridPreEventHandler(OverviewGrid* grid); ~OverviewGridPreEventHandler() override; - private: // ui::EventHandler: void OnMouseEvent(ui::MouseEvent* event) override; void OnGestureEvent(ui::GestureEvent* event) override; + private: void HandleClickOrTap(ui::Event* event); // Cached value of the OverviewGrid that handles a series of gesture scroll
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc index 56246964..fc01c4d6 100644 --- a/ash/wm/overview/overview_item.cc +++ b/ash/wm/overview/overview_item.cc
@@ -21,6 +21,7 @@ #include "ash/wm/overview/overview_constants.h" #include "ash/wm/overview/overview_controller.h" #include "ash/wm/overview/overview_grid.h" +#include "ash/wm/overview/overview_grid_pre_event_handler.h" #include "ash/wm/overview/overview_highlight_controller.h" #include "ash/wm/overview/overview_utils.h" #include "ash/wm/overview/overview_window_drag_controller.h" @@ -812,6 +813,10 @@ } void OverviewItem::HandleGestureEvent(ui::GestureEvent* event) { + if (ShouldUseTabletModeGridLayout()) { + HandleGestureEventForTabletModeLayout(event); + return; + } const gfx::PointF location = event->details().bounding_box_f().CenterPoint(); switch (event->type()) { case ui::ET_GESTURE_TAP_DOWN: @@ -841,6 +846,41 @@ } } +void OverviewItem::HandleGestureEventForTabletModeLayout( + ui::GestureEvent* event) { + const gfx::PointF location = event->details().bounding_box_f().CenterPoint(); + switch (event->type()) { + case ui::ET_GESTURE_SCROLL_UPDATE: + if (IsDragItem()) + HandleDragEvent(location); + else + overview_grid()->grid_pre_event_handler()->OnGestureEvent(event); + break; + case ui::ET_SCROLL_FLING_START: + HandleFlingStartEvent(location, event->details().velocity_x(), + event->details().velocity_y()); + break; + case ui::ET_GESTURE_SCROLL_END: + if (IsDragItem()) + HandleReleaseEvent(location); + else + overview_grid()->grid_pre_event_handler()->OnGestureEvent(event); + break; + case ui::ET_GESTURE_LONG_PRESS: + HandlePressEvent(location, /*from_touch_gesture=*/true); + break; + case ui::ET_GESTURE_TAP: + overview_session_->SelectWindow(this); + break; + case ui::ET_GESTURE_END: + HandleGestureEndEvent(); + break; + default: + overview_grid()->grid_pre_event_handler()->OnGestureEvent(event); + break; + } +} + bool OverviewItem::ShouldIgnoreGestureEvents() { return IsSlidingOutOverviewFromShelf(); }
diff --git a/ash/wm/overview/overview_item.h b/ash/wm/overview/overview_item.h index 1d896021..5ef3baa 100644 --- a/ash/wm/overview/overview_item.h +++ b/ash/wm/overview/overview_item.h
@@ -185,6 +185,9 @@ OverviewAnimationType GetExitOverviewAnimationType(); OverviewAnimationType GetExitTransformAnimationType(); + // If kNewOverviewLayout is on, use this function for handling events. + void HandleGestureEventForTabletModeLayout(ui::GestureEvent* event); + // CaptionContainerView::EventDelegate: void HandleMouseEvent(const ui::MouseEvent& event) override; void HandleGestureEvent(ui::GestureEvent* event) override;
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc index 600ace1..1df88147 100644 --- a/ash/wm/overview/overview_session.cc +++ b/ash/wm/overview/overview_session.cc
@@ -755,11 +755,16 @@ item->CloseWindow(); } -void OverviewSession::OnDisplayRemoved(const display::Display& display) { - // TODO(flackr): Keep window selection active on remaining displays. +void OverviewSession::OnDisplayAdded(const display::Display& display) { EndOverview(); } +void OverviewSession::OnDisplayRemoved(const display::Display& display) { + // Removing a display causes a window activation which will end overview mode + // so that |OnDisplayRemoved| is never called. + NOTREACHED(); +} + void OverviewSession::OnDisplayMetricsChanged(const display::Display& display, uint32_t metrics) { // For metrics changes that happen when the split view mode is active, the @@ -881,7 +886,7 @@ } void OverviewSession::OnShellDestroying() { - // Cancel selection will call |Shutodnw()|, which will remove observer. + // Cancel selection will call |Shutdown()|, which will remove observer. EndOverview(); } @@ -927,6 +932,11 @@ // Notify |split_view_drag_indicators_| if split view mode ended. if (split_view_drag_indicators_ && state == SplitViewState::kNoSnap) split_view_drag_indicators_->OnSplitViewModeEnded(); + + // Transfer focus from |window| to |overview_focus_widget_| to match the + // behavior of entering overview mode in the beginning. + DCHECK(overview_focus_widget_); + wm::ActivateWindow(GetOverviewFocusWindow()); } void OverviewSession::OnSplitViewDividerPositionChanged() {
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h index 1d6450ef4..0fecfe38 100644 --- a/ash/wm/overview/overview_session.h +++ b/ash/wm/overview/overview_session.h
@@ -239,6 +239,7 @@ void OnHighlightedItemClosed(OverviewItem* item); // display::DisplayObserver: + void OnDisplayAdded(const display::Display& display) override; void OnDisplayRemoved(const display::Display& display) override; void OnDisplayMetricsChanged(const display::Display& display, uint32_t metrics) override;
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc index 6541a19..db18dd06 100644 --- a/ash/wm/overview/overview_session_unittest.cc +++ b/ash/wm/overview/overview_session_unittest.cc
@@ -1086,29 +1086,6 @@ EXPECT_FALSE(InOverviewSession()); } -// Regression test for crash when closing the last overview mode window under -// SingleProcessMash. https://crbug.com/922293. -TEST_F(OverviewSessionTest, DontRestoreFocusToUnparentedWindow) { - const gfx::Rect bounds(400, 400); - std::unique_ptr<aura::Window> parent = CreateTestWindow(bounds); - std::unique_ptr<aura::Window> child = CreateChildWindow(parent.get(), bounds); - - // Enter overview with a focused child window. This simulates an app window - // web contents RenderWidgetHostViewAura. - child->Focus(); - ToggleOverview(); - - // Simulate the asynchronous window teardown for used by client widgets. - // Hierarchy changes are processed first, so the child is removed from its - // parent, then the windows are destroyed. - parent->RemoveChild(child.get()); - parent.reset(); - child.reset(); - - // Overview mode exits without crashing. - EXPECT_FALSE(InOverviewSession()); -} - // Tests that entering overview mode restores a window to its original // target location. TEST_F(OverviewSessionTest, QuickReentryRestoresInitialTransform) { @@ -2821,11 +2798,25 @@ EnterTabletMode(); } + SplitViewController* split_view_controller() { + return Shell::Get()->split_view_controller(); + } + void GenerateScrollSequence(const gfx::Point& start, const gfx::Point& end) { GetEventGenerator()->GestureScrollSequence( start, end, base::TimeDelta::FromMilliseconds(10), 10); } + protected: + void DispatchLongPress(OverviewItem* item) { + ui::TouchEvent long_press( + ui::ET_GESTURE_LONG_PRESS, + gfx::ToRoundedPoint(item->target_bounds().CenterPoint()), + base::TimeTicks::Now(), + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH)); + GetEventGenerator()->Dispatch(&long_press); + } + private: base::test::ScopedFeatureList scoped_feature_list_; @@ -2986,10 +2977,9 @@ // Snap |window1| to the left and exit overview. |window2| should have higher // z-order now, since it is the MRU window. - auto* split_view_controller = Shell::Get()->split_view_controller(); - split_view_controller->SnapWindow(window1.get(), SplitViewController::LEFT); + split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); ToggleOverview(); - ASSERT_EQ(SplitViewState::kBothSnapped, split_view_controller->state()); + ASSERT_EQ(SplitViewState::kBothSnapped, split_view_controller()->state()); ASSERT_GT(IndexOf(window2.get(), window2->parent()), IndexOf(window1.get(), window1->parent())); @@ -3006,6 +2996,103 @@ IndexOf(window1.get(), window1->parent())); } +// Test that scrolling occurs if started on top of a window using the window's +// center-point as a start. +TEST_F(OverviewSessionNewLayoutTest, CheckScrollingOnWindowItems) { + std::vector<std::unique_ptr<aura::Window>> windows(8); + for (int i = 7; i >= 0; --i) + windows[i] = CreateTestWindow(); + + ToggleOverview(); + ASSERT_TRUE(InOverviewSession()); + + OverviewItem* leftmost_window = + GetOverviewItemInGridWithWindow(0, windows[0].get()); + const gfx::Point topleft_window_center = + gfx::ToRoundedPoint(leftmost_window->target_bounds().CenterPoint()); + const gfx::RectF left_bounds = leftmost_window->target_bounds(); + + GenerateScrollSequence(topleft_window_center, gfx::Point(-500, 50)); + EXPECT_LT(leftmost_window->target_bounds(), left_bounds); +} + +// Test that tapping a window in overview closes overview mode. +TEST_F(OverviewSessionNewLayoutTest, CheckWindowActivateOnTap) { + base::UserActionTester user_action_tester; + std::vector<std::unique_ptr<aura::Window>> windows(8); + for (int i = 7; i >= 0; --i) + windows[i] = CreateTestWindow(); + wm::ActivateWindow(windows[1].get()); + + ToggleOverview(); + ASSERT_TRUE(InOverviewSession()); + + // Tap on |windows|1|| to exit overview. + GetEventGenerator()->GestureTapAt( + GetTransformedTargetBounds(windows[1].get()).CenterPoint()); + EXPECT_EQ( + 0, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview)); + + // |windows|1|| remains active. Click on it to exit overview. + ASSERT_EQ(windows[1].get(), window_util::GetFocusedWindow()); + ToggleOverview(); + ClickWindow(windows[1].get()); + EXPECT_EQ( + 0, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview)); +} + +// Tests that windows snap through long press and drag to left or right side of +// the screen. +TEST_F(OverviewSessionNewLayoutTest, DragOverviewWindowToSnap) { + const gfx::Rect bounds(400, 400); + std::unique_ptr<aura::Window> window1(CreateTestWindow(bounds)); + std::unique_ptr<aura::Window> window2(CreateTestWindow(bounds)); + std::unique_ptr<aura::Window> window3(CreateTestWindow(bounds)); + + ToggleOverview(); + ASSERT_TRUE(overview_controller()->InOverviewSession()); + ASSERT_FALSE(split_view_controller()->InSplitViewMode()); + + // Dispatches a long press event at the |overview_item1|'s current location to + // start dragging in SplitView. Drags |overview_item1| to the left border of + // the screen. SplitView should trigger and upon completing drag, + // |overview_item1| should snap to the left. + OverviewItem* overview_item1 = + GetOverviewItemInGridWithWindow(0, window1.get()); + const gfx::PointF snap_left_location = + gfx::PointF(GetGridBounds().left_center()); + + DispatchLongPress(overview_item1); + overview_session()->Drag( + overview_item1, + gfx::PointF(overview_item1->target_bounds().left_center())); + overview_session()->CompleteDrag(overview_item1, snap_left_location); + + ASSERT_TRUE(split_view_controller()->InSplitViewMode()); + EXPECT_EQ(split_view_controller()->state(), SplitViewState::kLeftSnapped); + EXPECT_EQ(split_view_controller()->left_window(), window1.get()); + + // Dispatches a long press event at the |overview_item2|'s current location to + // start dragging in SplitView. Drags |overview_item2| to the right border of + // the screen. Upon completing drag, |overview_item2| should snap to the + // right. + OverviewItem* overview_item2 = + GetOverviewItemInGridWithWindow(0, window2.get()); + const gfx::PointF snap_right_location = + gfx::PointF(GetGridBounds().right_center()); + + DispatchLongPress(overview_item2); + overview_session()->Drag( + overview_item2, + gfx::PointF(overview_item2->target_bounds().right_center())); + overview_session()->CompleteDrag(overview_item2, snap_right_location); + + EXPECT_FALSE(InOverviewSession()); + EXPECT_TRUE(split_view_controller()->InSplitViewMode()); + EXPECT_EQ(split_view_controller()->state(), SplitViewState::kBothSnapped); + EXPECT_EQ(split_view_controller()->right_window(), window2.get()); +} + // Test the split view and overview functionalities in tablet mode. class SplitViewOverviewSessionTest : public OverviewSessionTest { public: @@ -4368,6 +4455,29 @@ EXPECT_TRUE(InOverviewSession()); } +// Tests closing a snapped window while in overview mode. +TEST_F(SplitViewOverviewSessionTest, ClosingSplitViewWindow) { + const gfx::Rect bounds(400, 400); + std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); + std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); + + ToggleOverview(); + // Drag |window1| selector item to snap to left. + OverviewItem* overview_item1 = + GetOverviewItemInGridWithWindow(0, window1.get()); + DragWindowTo(overview_item1, gfx::PointF(0, 0)); + EXPECT_TRUE(overview_controller()->InOverviewSession()); + EXPECT_TRUE(split_view_controller()->InSplitViewMode()); + + // Now close the snapped |window1|. We should remain in overview mode and the + // overview focus window should regain focus. + window1.reset(); + EXPECT_TRUE(overview_controller()->InOverviewSession()); + EXPECT_FALSE(split_view_controller()->InSplitViewMode()); + EXPECT_EQ(overview_session()->GetOverviewFocusWindow(), + window_util::GetFocusedWindow()); +} + // Test the split view and overview functionalities in clamshell mode. Split // view is only active when overview is active in clamshell mode. class SplitViewOverviewSessionInClamshellTest @@ -4489,22 +4599,30 @@ WindowStateType::kLeftSnapped); EXPECT_TRUE(overview_controller()->InOverviewSession()); EXPECT_TRUE(split_view_controller()->InSplitViewMode()); + // End overview, test that we'll not auto-snap a window to the right side of + // the screen. + EXPECT_EQ(WindowState::Get(window4.get())->GetStateType(), + WindowStateType::kDefault); ToggleOverview(); EXPECT_EQ(WindowState::Get(window4.get())->GetStateType(), - WindowStateType::kRightSnapped); + WindowStateType::kDefault); EXPECT_FALSE(overview_controller()->InOverviewSession()); EXPECT_FALSE(split_view_controller()->InSplitViewMode()); // 5. Test if one window is snapped, the other window are showing in overview, - // activating an new window will open in splitview, which ends splitview and - // overview. + // activating an new window will not auto-snap the new window. Overview and + // splitview should be ended. ToggleOverview(); overview_item1 = GetOverviewItemInGridWithWindow(grid_index, window1.get()); DragWindowTo(overview_item1, gfx::PointF(0, 0)); EXPECT_TRUE(overview_controller()->InOverviewSession()); EXPECT_TRUE(split_view_controller()->InSplitViewMode()); std::unique_ptr<aura::Window> window5(CreateWindow(bounds)); + EXPECT_EQ(WindowState::Get(window5.get())->GetStateType(), + WindowStateType::kDefault); wm::ActivateWindow(window5.get()); + EXPECT_EQ(WindowState::Get(window5.get())->GetStateType(), + WindowStateType::kDefault); EXPECT_FALSE(overview_controller()->InOverviewSession()); EXPECT_FALSE(split_view_controller()->InSplitViewMode()); @@ -4523,6 +4641,22 @@ EXPECT_FALSE(split_view_controller()->InSplitViewMode()); // Overview bounds will adjust from snapped bounds to fullscreen bounds. EXPECT_EQ(GetGridBounds(), overview_bounds); + + // 7. Test if split view mode is active, open the app list will not end + // overview and splitview. + overview_item3 = GetOverviewItemInGridWithWindow(grid_index, window3.get()); + DragWindowTo(overview_item3, gfx::PointF(0, 0)); + EXPECT_TRUE(overview_controller()->InOverviewSession()); + EXPECT_TRUE(split_view_controller()->InSplitViewMode()); + // Open app list. + AppListControllerImpl* app_list_controller = + Shell::Get()->app_list_controller(); + app_list_controller->ToggleAppList( + display::Screen::GetScreen()->GetDisplayNearestWindow(window3.get()).id(), + app_list::AppListShowSource::kSearchKey, base::TimeTicks()); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(overview_controller()->InOverviewSession()); + EXPECT_TRUE(split_view_controller()->InSplitViewMode()); } // Test that if app list is visible when overview is open, overview should
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc index b458070..4c5fb64 100644 --- a/ash/wm/splitview/split_view_controller.cc +++ b/ash/wm/splitview/split_view_controller.cc
@@ -12,6 +12,7 @@ #include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/presentation_time_recorder.h" #include "ash/public/cpp/window_properties.h" +#include "ash/scoped_animation_disabler.h" #include "ash/screen_util.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" @@ -406,6 +407,14 @@ } } + // Disable the bounds change animation for a to-be-snapped window if the + // window has an un-identity transform. We'll do transform animation for the + // window in OnWindowSnapped() function. + std::unique_ptr<ScopedAnimationDisabler> animation_disabler; + auto iter = snapping_window_transformed_bounds_map_.find(window); + if (iter != snapping_window_transformed_bounds_map_.end()) + animation_disabler = std::make_unique<ScopedAnimationDisabler>(window); + if (WindowState::Get(window)->GetStateType() == GetStateTypeFromSnapPosition(snap_position)) { // Update its snapped bounds as its bounds may not be the expected snapped @@ -918,15 +927,6 @@ return; } - // This may be called while SnapWindow is still underway because SnapWindow - // will end the overview start animations which will cause the overview focus - // window to be activated. - aura::Window* overview_focus_window = - GetOverviewSession() ? GetOverviewSession()->GetOverviewFocusWindow() - : nullptr; - DCHECK(InSplitViewMode() || - (overview_focus_window && overview_focus_window == gained_active)); - // If |gained_active| was activated as a side effect of a window disposition // change, do nothing. For example, when a snapped window is closed, another // window will be activated before OnWindowDestroying() is called. We should @@ -934,12 +934,26 @@ if (reason == ActivationReason::WINDOW_DISPOSITION_CHANGED) return; - // Only snap window that hasn't been snapped. - if (!gained_active || gained_active == left_window_ || - gained_active == right_window_) { + // Only windows that are in the MRU list and are not already in split view can + // be auto-snapped. + if (!gained_active || IsWindowInSplitView(gained_active) || + !base::Contains( + Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk), + gained_active)) { return; } + // We do not auto snap windows in clamshell splitview mode if a new window + // is activated when clamshell splitview mode is active. In this case we'll + // just end overview mode which will then end splitview mode. + // TODO(xdai): Handle this logic in OverivewSession::OnWindowActivating(). + if (InClamshellSplitViewMode()) { + Shell::Get()->overview_controller()->EndOverview(); + return; + } + + DCHECK(InTabletSplitViewMode()); + // Do not snap the window if the activation change is caused by dragging a // window, or by dragging a tab. Note the two values WindowState::is_dragged() // and IsDraggingTabs() might not be exactly the same under certain @@ -953,13 +967,6 @@ return; } - // Only windows in MRU list can be snapped. - if (!base::Contains( - Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk), - gained_active)) { - return; - } - // If the divider is animating, then |gained_active| cannot be snapped (and is // not already snapped either, because then we would have bailed out by now). // Then if |gained_active| is user-positionable, we should end split view @@ -1025,16 +1032,18 @@ return; } - OverviewGrid* current_grid = - overview_session->GetGridWithRootWindow(root_window); - if (!current_grid || current_grid->empty()) { - // If overview is ended with an empty overview grid, end split view as well - // if we're in clamshell splitview mode. - if (InClamshellSplitViewMode()) - EndSplitView(); + // If we're in clamshell splitview mode, do not auto snap overview window + // when overview ends. + if (split_view_type_ == SplitViewType::kClamshellType) { + EndSplitView(); return; } + OverviewGrid* current_grid = + overview_session->GetGridWithRootWindow(root_window); + if (!current_grid || current_grid->empty()) + return; + // If split view mode is active but only has one snapped window when overview // mode is ending, retrieve the first snappable window in the overview window // grid and snap it. @@ -1207,6 +1216,13 @@ if (split_view_divider_) split_view_divider_->RemoveObservedWindow(window); Shell::Get()->shadow_controller()->UpdateShadowForWindow(window); + + // It's possible that when we try to snap an ARC app window, while we are + // waiting for its state/bounds to the expected state/bounds, another window + // snap request comes in and causing the previous to-be-snapped window to + // be un-observed, in this case we should restore the previous to-be-snapped + // window's transform if it's unidentity. + RestoreTransformIfApplicable(window); } } @@ -1685,8 +1701,6 @@ } void SplitViewController::RestoreTransformIfApplicable(aura::Window* window) { - DCHECK(IsWindowInSplitView(window)); - // If the transform of the window has been changed, calculate a good starting // transform based on its transformed bounds before to be snapped. auto iter = snapping_window_transformed_bounds_map_.find(window);
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc index fa8fecf..f8feb49e 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_state.cc +++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -498,8 +498,6 @@ // avoid flashing. if (window_state->IsMaximized()) window_state->SetBoundsDirectCrossFade(bounds_in_parent); - else if (window_state->IsSnapped()) - window_state->SetBoundsDirect(bounds_in_parent); else window_state->SetBoundsDirectAnimated(bounds_in_parent); }
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc index d6b02a3..9c6ddce2 100644 --- a/ash/wm/window_cycle_controller.cc +++ b/ash/wm/window_cycle_controller.cc
@@ -68,7 +68,6 @@ window_cycle_list_ = std::make_unique<WindowCycleList>(window_list); event_filter_ = std::make_unique<WindowCycleEventFilter>(); - cycle_start_time_ = base::Time::Now(); base::RecordAction(base::UserMetricsAction("WindowCycleController_Cycle")); UMA_HISTOGRAM_COUNTS_100("Ash.WindowCycleController.Items", window_list.size()); @@ -92,8 +91,6 @@ } void WindowCycleController::StopCycling() { - UMA_HISTOGRAM_COUNTS_100("Ash.WindowCycleController.SelectionDepth", - window_cycle_list_->current_index() + 1); window_cycle_list_.reset(); aura::Window* active_window_after_window_cycle = GetActiveWindow( @@ -101,8 +98,6 @@ // Remove our key event filter. event_filter_.reset(); - UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowCycleController.CycleTime", - base::Time::Now() - cycle_start_time_); if (active_window_after_window_cycle != nullptr && active_window_before_window_cycle_ != active_window_after_window_cycle) {
diff --git a/ash/wm/window_cycle_controller.h b/ash/wm/window_cycle_controller.h index 4e687b7..5c017d9 100644 --- a/ash/wm/window_cycle_controller.h +++ b/ash/wm/window_cycle_controller.h
@@ -75,8 +75,6 @@ // Non-null while actively cycling. std::unique_ptr<WindowCycleEventFilter> event_filter_; - base::Time cycle_start_time_; - DISALLOW_COPY_AND_ASSIGN(WindowCycleController); };
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc index 5516d338..2b92f46 100644 --- a/ash/wm/window_util.cc +++ b/ash/wm/window_util.cc
@@ -22,7 +22,6 @@ #include "ash/wm/window_positioning_utils.h" #include "ash/wm/window_state.h" #include "ash/wm/wm_event.h" -#include "ui/aura/client/aura_constants.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" @@ -185,11 +184,6 @@ -kResizeOutsideBoundsSize); gfx::Insets touch_extend = mouse_extend.Scale(kResizeOutsideBoundsScaleForTouch); - // TODO: EasyResizeWindowTargeter makes it so children get events outside - // their bounds. This only works in mash when mash is providing the non-client - // frame. Mus needs to support an api for the WindowManager that enables - // events to be dispatched to windows outside the windows bounds that this - // function calls into. http://crbug.com/679056. window->SetEventTargeter(std::make_unique<::wm::EasyResizeWindowTargeter>( mouse_extend, touch_extend)); } @@ -202,10 +196,6 @@ void InstallResizeHandleWindowTargeterForWindow(aura::Window* window) { window->SetEventTargeter(std::make_unique<InteriorResizeHandleTargeter>()); - // For Mash, ServerWindows will override the event targeter with a - // ServerWindowTargeter, so make sure it knows about the resize insets. - window->SetProperty(aura::client::kResizeHandleInset, - kResizeInsideBoundsSize); } bool IsDraggingTabs(const aura::Window* window) {
diff --git a/base/memory/protected_memory_cfi.h b/base/memory/protected_memory_cfi.h index a90023b..812e3cc 100644 --- a/base/memory/protected_memory_cfi.h +++ b/base/memory/protected_memory_cfi.h
@@ -64,7 +64,9 @@ template <typename T> auto UnsanitizedCfiCall(const ProtectedMemory<T>& PM) { #if PROTECTED_MEMORY_ENABLED - DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd); + DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd) + << "ProtectedMemoryStart=" << ProtectedMemoryStart << ", &PM=" << &PM + << ", ProtectedMemoryEnd=" << ProtectedMemoryEnd; #endif // PROTECTED_MEMORY_ENABLED return internal::UnsanitizedCfiCall<T>(*PM); } @@ -76,7 +78,9 @@ template <typename T, typename Member> auto UnsanitizedCfiCall(const ProtectedMemory<T>& PM, Member member) { #if PROTECTED_MEMORY_ENABLED - DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd); + DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd) + << "ProtectedMemoryStart=" << ProtectedMemoryStart << ", &PM=" << &PM + << ", ProtectedMemoryEnd=" << ProtectedMemoryEnd; #endif // PROTECTED_MEMORY_ENABLED return internal::UnsanitizedCfiCall<decltype(*PM.*member)>(*PM.*member); }
diff --git a/base/supports_user_data.cc b/base/supports_user_data.cc index 8060543..f4a459d 100644 --- a/base/supports_user_data.cc +++ b/base/supports_user_data.cc
@@ -66,4 +66,9 @@ // examining a being-destroyed object. } +void SupportsUserData::ClearAllUserData() { + DCHECK(sequence_checker_.CalledOnValidSequence()); + user_data_.clear(); +} + } // namespace base
diff --git a/base/supports_user_data.h b/base/supports_user_data.h index f8aa492c..418d4cc 100644 --- a/base/supports_user_data.h +++ b/base/supports_user_data.h
@@ -56,6 +56,10 @@ protected: virtual ~SupportsUserData(); + // Clear all user data from this object. This can be used if the subclass + // needs to provide reset functionality. + void ClearAllUserData(); + private: using DataMap = std::map<const void*, std::unique_ptr<Data>>;
diff --git a/base/supports_user_data_unittest.cc b/base/supports_user_data_unittest.cc index b30ede02..7bf11409 100644 --- a/base/supports_user_data_unittest.cc +++ b/base/supports_user_data_unittest.cc
@@ -12,7 +12,10 @@ namespace base { namespace { -struct TestSupportsUserData : public SupportsUserData {}; +struct TestSupportsUserData : public SupportsUserData { + // Make ClearAllUserData public so tests can access it. + using SupportsUserData::ClearAllUserData; +}; struct UsesItself : public SupportsUserData::Data { UsesItself(SupportsUserData* supports_user_data, const void* key) @@ -54,5 +57,21 @@ EXPECT_EQ(nullptr, supports_user_data_2.GetUserData(&key2)); } +TEST(SupportsUserDataTest, ClearAllUserData) { + TestSupportsUserData supports_user_data; + char key1 = 0; + supports_user_data.SetUserData(&key1, std::make_unique<TestData>()); + char key2 = 0; + supports_user_data.SetUserData(&key2, std::make_unique<TestData>()); + + EXPECT_TRUE(supports_user_data.GetUserData(&key1)); + EXPECT_TRUE(supports_user_data.GetUserData(&key2)); + + supports_user_data.ClearAllUserData(); + + EXPECT_FALSE(supports_user_data.GetUserData(&key1)); + EXPECT_FALSE(supports_user_data.GetUserData(&key2)); +} + } // namespace } // namespace base
diff --git a/base/task/thread_pool/job_task_source.cc b/base/task/thread_pool/job_task_source.cc index 9adf020..6eca51e9d 100644 --- a/base/task/thread_pool/job_task_source.cc +++ b/base/task/thread_pool/job_task_source.cc
@@ -7,6 +7,7 @@ #include <utility> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/task/task_features.h" @@ -39,8 +40,12 @@ queue_time_(ThreadPoolClock::Now()) {} JobTaskSource::~JobTaskSource() { +#if DCHECK_IS_ON() + auto worker_count = worker_count_.load(std::memory_order_relaxed); // Make sure there's no outstanding run intent left. - DCHECK_EQ(0U, worker_count_.load(std::memory_order_relaxed)); + DCHECK(worker_count == 0U || worker_count == kInvalidWorkerCount) + << worker_count; +#endif } ExecutionEnvironment JobTaskSource::GetExecutionEnvironment() { @@ -48,25 +53,37 @@ } TaskSource::RunIntent JobTaskSource::WillRunTask() { + // When this call is caused by an increase of max concurrency followed by an + // associated NotifyConcurrencyIncrease(), the priority queue lock guarantees + // an happens-after relation with NotifyConcurrencyIncrease(). The memory + // operations on |worker_count| below and in DidProcessTask() use + // std::memory_order_release and std::memory_order_acquire respectively to + // establish a Release-Acquire ordering. This ensures that all memory + // side-effects made before this point, including an increase of max + // concurrency followed by NotifyConcurrencyIncrease() are visible to a + // DidProcessTask() call which is ordered after this one. const size_t max_concurrency = GetMaxConcurrency(); - const size_t worker_count_initial = + size_t worker_count_before_add = worker_count_.load(std::memory_order_relaxed); + + // std::memory_order_release on success to make the newest |max_concurrency| + // visible to a thread that calls DidProcessTask() containing a matching + // std::memory_order_acquire. + while (worker_count_before_add < max_concurrency && + !worker_count_.compare_exchange_weak( + worker_count_before_add, worker_count_before_add + 1, + std::memory_order_release, std::memory_order_relaxed)) { + } // Don't allow this worker to run the task if either: // A) |worker_count_| is already at |max_concurrency|. // B) |max_concurrency| was lowered below or to |worker_count_|. - if (worker_count_initial >= max_concurrency) { - // The caller receives an invalid RunIntent and should skip this TaskSource. + // C) |worker_count_| was invalidated. + if (worker_count_before_add >= max_concurrency) { + // The caller receives an invalid RunIntent and should skip this + // TaskSource. return RunIntent(); } - const size_t worker_count_before_add = - worker_count_.fetch_add(1, std::memory_order_relaxed); - // WillRunTask() has external synchronization to prevent concurrent calls and - // it is the only place where |worker_count_| is incremented. Therefore, the - // second reading of |worker_count_| from WillRunTask() cannot be greater than - // the first reading. However, since DidProcessTask() can decrement - // |worker_count_| concurrently with WillRunTask(), the second reading can be - // lower than the first reading. - DCHECK_LE(worker_count_before_add, worker_count_initial); + DCHECK_LT(worker_count_before_add, max_concurrency); return MakeRunIntent(max_concurrency == worker_count_before_add + 1 ? Saturated::kYes @@ -74,6 +91,8 @@ } size_t JobTaskSource::GetRemainingConcurrency() const { + // std::memory_order_relaxed is sufficient because no other state is + // synchronized with GetRemainingConcurrency(). const size_t worker_count = worker_count_.load(std::memory_order_relaxed); const size_t max_concurrency = GetMaxConcurrency(); // Avoid underflows. @@ -96,28 +115,57 @@ return base::make_optional<Task>(from_here_, worker_task_, TimeDelta()); } -bool JobTaskSource::DidProcessTask(RunResult run_result) { +bool JobTaskSource::DidProcessTask() { size_t worker_count_before_sub = - worker_count_.fetch_sub(1, std::memory_order_relaxed); + worker_count_.load(std::memory_order_relaxed); + + // std::memory_order_acquire on |worker_count_| is necessary to establish + // Release-Acquire ordering (see WillRunTask()). + // When the task source needs to be queued, either because the current task + // yielded or because of NotifyConcurrencyIncrease(), one of the following is + // true: + // A) The JobTaskSource is already in the queue (no worker picked up the + // extra work yet): Incorrectly returning false is fine and the memory + // barrier may be ineffective. + // B) The JobTaskSource() is no longer in the queue: The Release-Acquire + // ordering with WillRunTask() established by |worker_count| ensures that + // the upcoming call for GetMaxConcurrency() happens-after any + // NotifyConcurrencyIncrease() that happened-before WillRunTask(). If + // this task completed because it yielded, this barrier guarantees that + // it sees an up-to-date concurrency value and correctly re-enqueues. + // + // Note that stale values the other way around (incorrectly re-enqueuing) are + // not an issue because the queues support empty task sources. + while (worker_count_before_sub != kInvalidWorkerCount && + !worker_count_.compare_exchange_weak( + worker_count_before_sub, worker_count_before_sub - 1, + std::memory_order_acquire, std::memory_order_relaxed)) { + } + if (worker_count_before_sub == kInvalidWorkerCount) + return false; + DCHECK_GT(worker_count_before_sub, 0U); + // Re-enqueue the TaskSource if the task ran and the worker count is below the // max concurrency. - const bool must_be_queued = - run_result == RunResult::kSkippedAtShutdown - ? false - : worker_count_before_sub <= GetMaxConcurrency(); - return must_be_queued; + return worker_count_before_sub <= GetMaxConcurrency(); } SequenceSortKey JobTaskSource::GetSortKey() const { return SequenceSortKey(traits_.priority(), queue_time_); } -void JobTaskSource::Clear() { - // Reset callbacks to release any reference held by this task source and - // prevent further calls to WillRunTask(). - worker_task_.Reset(); - max_concurrency_callback_.Reset(); +Optional<Task> JobTaskSource::Clear() { + // Invalidate |worker_count_| so that further calls to WillRunTask() never + // succeed. std::memory_order_relaxed is sufficient because this task source + // never needs to be re-enqueued after Clear(). + size_t worker_count_before_store = + worker_count_.exchange(kInvalidWorkerCount, std::memory_order_relaxed); + DCHECK_GT(worker_count_before_store, 0U); + // Nothing is cleared since outstanding RunIntents might still racily run + // tasks. For simplicity, the destructor will take care of it once all + // references are released. + return base::make_optional<Task>(from_here_, DoNothing(), TimeDelta()); } } // namespace internal
diff --git a/base/task/thread_pool/job_task_source.h b/base/task/thread_pool/job_task_source.h index b0d7004fe..38d90cc45 100644 --- a/base/task/thread_pool/job_task_source.h +++ b/base/task/thread_pool/job_task_source.h
@@ -8,6 +8,7 @@ #include <stddef.h> #include <atomic> +#include <limits> #include "base/base_export.h" #include "base/callback.h" @@ -42,6 +43,9 @@ size_t GetRemainingConcurrency() const override; private: + static constexpr size_t kInvalidWorkerCount = + std::numeric_limits<size_t>::max(); + ~JobTaskSource() override; // Returns the maximum number of tasks from this TaskSource that can run @@ -51,13 +55,12 @@ // TaskSource: Optional<Task> TakeTask() override; - bool DidProcessTask(RunResult run_result) override; + Optional<Task> Clear() override; + bool DidProcessTask() override; SequenceSortKey GetSortKey() const override; - void Clear() override; // The current number of workers concurrently running tasks from this - // TaskSource. "memory_order_relaxed" is sufficient to access this variable as - // no other state is synchronized with it. + // TaskSource. std::atomic_size_t worker_count_{0U}; const Location from_here_;
diff --git a/base/task/thread_pool/job_task_source_unittest.cc b/base/task/thread_pool/job_task_source_unittest.cc index 5af75ce..05a8f546 100644 --- a/base/task/thread_pool/job_task_source_unittest.cc +++ b/base/task/thread_pool/job_task_source_unittest.cc
@@ -53,23 +53,60 @@ } } -// Verifies that a job task source doesn't get reenqueued when a task is not -// run. -TEST(ThreadPoolJobTaskSourceTest, SkipTask) { +// Verifies that a job task source doesn't allow any new RunIntent after Clear() +// is called. +TEST(ThreadPoolJobTaskSourceTest, Clear) { auto job_task = base::MakeRefCounted<test::MockJobTask>( - DoNothing(), /* num_tasks_to_run */ 1); + DoNothing(), /* num_tasks_to_run */ 5); scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource( FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT}); TaskSource::Transaction task_source_transaction( task_source->BeginTransaction()); + EXPECT_EQ(5U, task_source->GetRemainingConcurrency()); - auto run_intent = task_source->WillRunTask(); - EXPECT_TRUE(run_intent); - EXPECT_TRUE(run_intent.IsSaturated()); - auto task = task_source_transaction.TakeTask(&run_intent); - EXPECT_FALSE(task_source_transaction.DidProcessTask( - std::move(run_intent), TaskSource::RunResult::kSkippedAtShutdown)); + auto run_intent_a = task_source->WillRunTask(); + EXPECT_TRUE(run_intent_a); + auto task_a = task_source_transaction.TakeTask(&run_intent_a); + + auto run_intent_b = task_source->WillRunTask(); + EXPECT_TRUE(run_intent_b); + + auto run_intent_c = task_source->WillRunTask(); + EXPECT_TRUE(run_intent_c); + + auto run_intent_d = task_source->WillRunTask(); + EXPECT_TRUE(run_intent_d); + EXPECT_FALSE(run_intent_c.IsSaturated()); + + { + EXPECT_EQ(1U, task_source->GetRemainingConcurrency()); + auto task = task_source_transaction.Clear(std::move(run_intent_c)); + std::move(task->task).Run(); + EXPECT_EQ(0U, task_source->GetRemainingConcurrency()); + } + // The task source shouldn't allow any further tasks after Clear. + EXPECT_FALSE(task_source->WillRunTask()); + + // Another outstanding RunIntent can still call Clear. + { + auto task = task_source_transaction.Clear(std::move(run_intent_d)); + std::move(task->task).Run(); + EXPECT_EQ(0U, task_source->GetRemainingConcurrency()); + } + + // A task that was already acquired can still run. + std::move(task_a->task).Run(); + task_source_transaction.DidProcessTask(std::move(run_intent_a)); + + // A valid outstanding RunIntent can also take & run a task. + { + auto task = task_source_transaction.TakeTask(&run_intent_b); + std::move(task->task).Run(); + task_source_transaction.DidProcessTask(std::move(run_intent_b)); + } + // Sanity check. + EXPECT_FALSE(task_source->WillRunTask()); } // Verifies that multiple tasks can run in parallel up to |max_concurrency|.
diff --git a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc index e33235f..ed11e70 100644 --- a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc +++ b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
@@ -139,19 +139,22 @@ // |task| will be pushed to |sequence|, and |sequence| will be queued // to |priority_queue_| iff |sequence_should_be_queued| is true. const bool sequence_should_be_queued = transaction.WillPushTask(); - if (!sequence_should_be_queued) { - transaction.PushTask(std::move(task)); - return true; + RegisteredTaskSource task_source; + if (sequence_should_be_queued) { + task_source = task_tracker_->WillQueueTaskSource(sequence); + // We shouldn't push |task| if we're not allowed to queue |task_source|. + if (!task_source) + return false; } - auto registered_task_source = task_tracker_->WillQueueTaskSource(sequence); - if (!registered_task_source) + if (!task_tracker_->WillPostTaskNow(task, transaction.traits().priority())) return false; - task_tracker_->WillPostTaskNow(task, transaction.traits().priority()); transaction.PushTask(std::move(task)); - bool should_wakeup = EnqueueTaskSource( - {std::move(registered_task_source), std::move(transaction)}); - if (should_wakeup) - worker_->WakeUp(); + if (task_source) { + bool should_wakeup = + EnqueueTaskSource({std::move(task_source), std::move(transaction)}); + if (should_wakeup) + worker_->WakeUp(); + } return true; }
diff --git a/base/task/thread_pool/priority_queue.cc b/base/task/thread_pool/priority_queue.cc index 1670dca..7807e84 100644 --- a/base/task/thread_pool/priority_queue.cc +++ b/base/task/thread_pool/priority_queue.cc
@@ -82,8 +82,13 @@ if (!is_flush_task_sources_on_destroy_enabled_) return; - while (!container_.empty()) - PopTaskSource().Unregister()->BeginTransaction().Clear(); + while (!container_.empty()) { + auto task_source = PopTaskSource().Unregister(); + auto task = + task_source->BeginTransaction().Clear(task_source->WillRunTask()); + if (task) + std::move(task->task).Run(); + } } PriorityQueue& PriorityQueue::operator=(PriorityQueue&& other) = default;
diff --git a/base/task/thread_pool/sequence.cc b/base/task/thread_pool/sequence.cc index 9e70451..1e21817 100644 --- a/base/task/thread_pool/sequence.cc +++ b/base/task/thread_pool/sequence.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/bind.h" #include "base/critical_closure.h" #include "base/feature_list.h" #include "base/logging.h" @@ -83,7 +84,7 @@ return std::move(next_task); } -bool Sequence::DidProcessTask(RunResult run_result) { +bool Sequence::DidProcessTask() { // There should never be a call to DidProcessTask without an associated // WillRunTask(). DCHECK(has_worker_); @@ -103,15 +104,21 @@ return SequenceSortKey(traits_.priority(), queue_.front().queue_time); } -void Sequence::Clear() { - bool queue_was_empty = queue_.empty(); - while (!queue_.empty()) - queue_.pop(); - if (!queue_was_empty) { - // No member access after this point, ReleaseTaskRunner() might have deleted - // |this|. - ReleaseTaskRunner(); - } +Optional<Task> Sequence::Clear() { + has_worker_ = false; + return base::make_optional<Task>( + FROM_HERE, + base::BindOnce( + [](scoped_refptr<Sequence> self, base::queue<Task> queue) { + bool queue_was_empty = queue.empty(); + while (!queue.empty()) + queue.pop(); + if (!queue_was_empty) { + self->ReleaseTaskRunner(); + } + }, + scoped_refptr<Sequence>(this), std::move(queue_)), + TimeDelta()); } void Sequence::ReleaseTaskRunner() {
diff --git a/base/task/thread_pool/sequence.h b/base/task/thread_pool/sequence.h index e1c3b99..58cdf02 100644 --- a/base/task/thread_pool/sequence.h +++ b/base/task/thread_pool/sequence.h
@@ -100,9 +100,9 @@ // TaskSource: Optional<Task> TakeTask() override WARN_UNUSED_RESULT; - bool DidProcessTask(RunResult run_result) override; + Optional<Task> Clear() override WARN_UNUSED_RESULT; + bool DidProcessTask() override; SequenceSortKey GetSortKey() const override; - void Clear() override; // Releases reference to TaskRunner. This might cause this object to be // deleted; therefore, no member access should be made after this method.
diff --git a/base/task/thread_pool/task_source.cc b/base/task/thread_pool/task_source.cc index 6d39aeb..4a02f2d 100644 --- a/base/task/thread_pool/task_source.cc +++ b/base/task/thread_pool/task_source.cc
@@ -63,23 +63,26 @@ return task_source_->TakeTask(); } -bool TaskSource::Transaction::DidProcessTask(RunIntent intent, - RunResult run_result) { +Optional<Task> TaskSource::Transaction::Clear(RunIntent intent) { + DCHECK_EQ(intent.task_source_, task_source()); + DCHECK_EQ(intent.run_step_, RunIntent::State::kInitial); + intent.run_step_ = RunIntent::State::kCompleted; + intent.Release(); + return task_source_->Clear(); +} + +bool TaskSource::Transaction::DidProcessTask(RunIntent intent) { DCHECK_EQ(intent.task_source_, task_source()); DCHECK_EQ(intent.run_step_, RunIntent::State::kTaskAcquired); intent.run_step_ = RunIntent::State::kCompleted; intent.Release(); - return task_source_->DidProcessTask(run_result); + return task_source_->DidProcessTask(); } SequenceSortKey TaskSource::Transaction::GetSortKey() const { return task_source_->GetSortKey(); } -void TaskSource::Transaction::Clear() { - task_source_->Clear(); -} - void TaskSource::Transaction::UpdatePriority(TaskPriority priority) { if (FeatureList::IsEnabled(kAllTasksUserBlocking)) return;
diff --git a/base/task/thread_pool/task_source.h b/base/task/thread_pool/task_source.h index bb9deaa..df28aea 100644 --- a/base/task/thread_pool/task_source.h +++ b/base/task/thread_pool/task_source.h
@@ -71,14 +71,9 @@ }; public: - // Indicates if a task was run or skipped as a result of shutdown. - enum class RunResult { - kDidRun, - kSkippedAtShutdown, - }; - // Result of WillRunTask(). A single task associated with a RunIntent may be - // accessed with TakeTask() and run iff this evaluates to true. + // accessed with TakeTask() and run, or the task source may be cleared with + // Clear() iff this evaluates to true. class BASE_EXPORT RunIntent { public: RunIntent() = default; @@ -145,11 +140,15 @@ // https://crbug.com/783309 Optional<Task> TakeTask(RunIntent* intent) WARN_UNUSED_RESULT; - // Must be called once the task was run or skipped. |run_result| indicates - // if the task executed. Cannot be called on an empty TaskSource. Returns - // true if the TaskSource should be queued after this operation. - bool DidProcessTask(RunIntent intent, - RunResult run_result = RunResult::kDidRun); + // Returns a task that clears this TaskSource to make it empty. This should + // be called only with a valid |intent|, but may be called for each valid + // outstanding RunIntent. + Optional<Task> Clear(RunIntent intent) WARN_UNUSED_RESULT; + + // Must be called once the task was run. Cannot be called on an empty + // TaskSource. Returns true if the TaskSource should be queued after this + // operation. + bool DidProcessTask(RunIntent intent); // Returns a SequenceSortKey representing the priority of the TaskSource. // Cannot be called on an empty TaskSource. @@ -158,9 +157,6 @@ // Sets TaskSource priority to |priority|. void UpdatePriority(TaskPriority priority); - // Deletes all tasks contained in this TaskSource. - void Clear(); - // Returns the traits of all Tasks in the TaskSource. TaskTraits traits() const { return task_source_->traits_; } @@ -194,14 +190,12 @@ // Informs this TaskSource that an additional Task could be run. Returns a // RunIntent that evaluates to true if this operation is allowed (TakeTask() - // can be called), or false otherwise. This function is not thread safe and - // must be externally synchronized (e.g. by the lock of the PriorityQueue - // holding the TaskSource). + // or Clear() can be called), or false otherwise. virtual RunIntent WillRunTask() = 0; // Thread-safe but the returned value may immediately be obsolete. As such // this should only be used as a best-effort guess of how many more workers - // are needed. + // are needed. This may be called on an empty task source. virtual size_t GetRemainingConcurrency() const = 0; // Support for IntrusiveHeap. @@ -231,11 +225,13 @@ // Informs this TaskSource that a task was processed. |was_run| indicates // whether the task executed or not. Returns true if the TaskSource // should be queued after this operation. - virtual bool DidProcessTask(RunResult run_result) = 0; + virtual bool DidProcessTask() = 0; virtual SequenceSortKey GetSortKey() const = 0; - virtual void Clear() = 0; + // This may be called for each outstanding RunIntent. If applicable, the + // implementation needs to support this being called multiple times. + virtual Optional<Task> Clear() = 0; // Sets TaskSource priority to |priority|. void UpdatePriority(TaskPriority priority);
diff --git a/base/task/thread_pool/task_tracker.cc b/base/task/thread_pool/task_tracker.cc index 95a40ec2..5a55a5a 100644 --- a/base/task/thread_pool/task_tracker.cc +++ b/base/task/thread_pool/task_tracker.cc
@@ -129,18 +129,6 @@ : 0]; } -// Returns shutdown behavior based on |traits|; returns SKIP_ON_SHUTDOWN if -// shutdown behavior is BLOCK_SHUTDOWN and |is_delayed|, because delayed tasks -// are not allowed to block shutdown. -TaskShutdownBehavior GetEffectiveShutdownBehavior( - TaskShutdownBehavior shutdown_behavior, - bool is_delayed) { - if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN && is_delayed) { - return TaskShutdownBehavior::SKIP_ON_SHUTDOWN; - } - return shutdown_behavior; -} - bool HasLogBestEffortTasksSwitch() { // The CommandLine might not be initialized if ThreadPool is initialized in a // dynamic library which doesn't have access to argc/argv. @@ -394,10 +382,9 @@ if (state_->HasShutdownStarted()) { // A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't - // started. - if (GetEffectiveShutdownBehavior(shutdown_behavior, - !task->delayed_run_time.is_null()) != - TaskShutdownBehavior::BLOCK_SHUTDOWN) { + // started and the task is not delayed. + if (shutdown_behavior != TaskShutdownBehavior::BLOCK_SHUTDOWN || + !task->delayed_run_time.is_null()) { return false; } @@ -414,12 +401,15 @@ return true; } -void TaskTracker::WillPostTaskNow(const Task& task, TaskPriority priority) { +bool TaskTracker::WillPostTaskNow(const Task& task, TaskPriority priority) { + if (!task.delayed_run_time.is_null() && state_->HasShutdownStarted()) + return false; if (has_log_best_effort_tasks_switch_ && priority == TaskPriority::BEST_EFFORT) { // A TaskPriority::BEST_EFFORT task is being posted. LOG(INFO) << task.posted_from.ToString(); } + return true; } RegisteredTaskSource TaskTracker::WillQueueTaskSource( @@ -453,43 +443,35 @@ DCHECK(run_intent_with_task_source); auto task_source = run_intent_with_task_source.take_task_source(); + const bool can_run_worker_task = + BeforeRunTask(task_source->shutdown_behavior()); + // Run the next task in |task_source|. Optional<Task> task; TaskTraits traits{ThreadPool()}; { TaskSource::Transaction task_source_transaction( task_source->BeginTransaction()); - task = task_source_transaction.TakeTask(&run_intent_with_task_source); + task = can_run_worker_task + ? task_source_transaction.TakeTask(&run_intent_with_task_source) + : task_source_transaction.Clear( + std::move(run_intent_with_task_source)); traits = task_source_transaction.traits(); } if (task) { - const TaskShutdownBehavior effective_shutdown_behavior = - GetEffectiveShutdownBehavior(task_source->shutdown_behavior(), - !task->delayed_run_time.is_null()); - - const bool can_run_task = BeforeRunTask(effective_shutdown_behavior); - - RunOrSkipTask(std::move(task.value()), task_source.get(), traits, - can_run_task); - + // Run the |task| (whether it's a worker task or the Clear() closure). + RunTask(std::move(task.value()), task_source.get(), traits); + } + if (can_run_worker_task) { + AfterRunTask(task_source->shutdown_behavior()); const bool task_source_must_be_queued = task_source->BeginTransaction().DidProcessTask( - std::move(run_intent_with_task_source), - can_run_task ? TaskSource::RunResult::kDidRun - : TaskSource::RunResult::kSkippedAtShutdown); - - if (can_run_task) { - IncrementNumTasksRun(); - AfterRunTask(effective_shutdown_behavior); - } - + std::move(run_intent_with_task_source)); // |task_source| should be reenqueued iff requested by DidProcessTask(). - if (task_source_must_be_queued) { + if (task_source_must_be_queued) return task_source; - } } - return nullptr; } @@ -543,10 +525,9 @@ num_tasks_run_.fetch_add(1, std::memory_order_relaxed); } -void TaskTracker::RunOrSkipTask(Task task, - TaskSource* task_source, - const TaskTraits& traits, - bool can_run_task) { +void TaskTracker::RunTask(Task task, + TaskSource* task_source, + const TaskTraits& traits) { DCHECK(task_source); RecordLatencyHistogram(LatencyHistogramType::TASK_LATENCY, traits, task.queue_time); @@ -599,21 +580,19 @@ break; } - if (can_run_task) { - TRACE_TASK_EXECUTION("ThreadPool_RunTask", task); + TRACE_TASK_EXECUTION("ThreadPool_RunTask", task); - // TODO(gab): In a better world this would be tacked on as an extra arg - // to the trace event generated above. This is not possible however until - // http://crbug.com/652692 is resolved. - TRACE_EVENT1("thread_pool", "ThreadPool_TaskInfo", "task_info", - std::make_unique<TaskTracingInfo>( - traits, - kExecutionModeString[static_cast<size_t>( - task_source->execution_mode())], - environment.token)); + // TODO(gab): In a better world this would be tacked on as an extra arg + // to the trace event generated above. This is not possible however until + // http://crbug.com/652692 is resolved. + TRACE_EVENT1("thread_pool", "ThreadPool_TaskInfo", "task_info", + std::make_unique<TaskTracingInfo>( + traits, + kExecutionModeString[static_cast<size_t>( + task_source->execution_mode())], + environment.token)); - RunTaskWithShutdownBehavior(traits.shutdown_behavior(), &task); - } + RunTaskWithShutdownBehavior(traits.shutdown_behavior(), &task); // Make sure the arguments bound to the callback are deleted within the // scope in which the callback runs. @@ -652,9 +631,8 @@ return !state_->HasShutdownStarted(); } -bool TaskTracker::BeforeRunTask( - TaskShutdownBehavior effective_shutdown_behavior) { - switch (effective_shutdown_behavior) { +bool TaskTracker::BeforeRunTask(TaskShutdownBehavior shutdown_behavior) { + switch (shutdown_behavior) { case TaskShutdownBehavior::BLOCK_SHUTDOWN: { // The number of tasks blocking shutdown has been incremented when the // task was posted. @@ -693,9 +671,9 @@ return false; } -void TaskTracker::AfterRunTask( - TaskShutdownBehavior effective_shutdown_behavior) { - if (effective_shutdown_behavior == TaskShutdownBehavior::SKIP_ON_SHUTDOWN) { +void TaskTracker::AfterRunTask(TaskShutdownBehavior shutdown_behavior) { + IncrementNumTasksRun(); + if (shutdown_behavior == TaskShutdownBehavior::SKIP_ON_SHUTDOWN) { DecrementNumItemsBlockingShutdown(); } }
diff --git a/base/task/thread_pool/task_tracker.h b/base/task/thread_pool/task_tracker.h index f44c6d0..1f2e34c 100644 --- a/base/task/thread_pool/task_tracker.h +++ b/base/task/thread_pool/task_tracker.h
@@ -100,8 +100,10 @@ bool WillPostTask(Task* task, TaskShutdownBehavior shutdown_behavior); // Informs this TaskTracker that |task| that is about to be pushed to a task - // source with |priority|. - void WillPostTaskNow(const Task& task, TaskPriority priority); + // source with |priority|. Returns true if this operation is allowed (the + // operation should be performed if-and-only-if it is). + bool WillPostTaskNow(const Task& task, + TaskPriority priority) WARN_UNUSED_RESULT; // Informs this TaskTracker that |task_source| is about to be queued. Returns // a RegisteredTaskSource that should be queued if-and-only-if it evaluates to @@ -166,16 +168,14 @@ bool HasIncompleteTaskSourcesForTesting() const; protected: - // Runs and deletes |task| if |can_run_task| is true. Otherwise, just deletes - // |task|. |task| is always deleted in the environment where it runs or would - // have run. |task_source| is the task source from which |task| was extracted. + // Runs and deletes |task|. |task| is deleted in the environment where it + // runs. |task_source| is the task source from which |task| was extracted. // |traits| are the traits of |task_source|. An override is expected to call // its parent's implementation but is free to perform extra work before and // after doing so. - virtual void RunOrSkipTask(Task task, - TaskSource* task_source, - const TaskTraits& traits, - bool can_run_task); + virtual void RunTask(Task task, + TaskSource* task_source, + const TaskTraits& traits); private: friend class RegisteredTaskSource; @@ -186,16 +186,16 @@ // Called before WillPostTask() informs the tracing system that a task has // been posted. Updates |num_items_blocking_shutdown_| if necessary and // returns true if the current shutdown state allows the task to be posted. - bool BeforeQueueTaskSource(TaskShutdownBehavior effective_shutdown_behavior); + bool BeforeQueueTaskSource(TaskShutdownBehavior shutdown_behavior); // Called before a task with |effective_shutdown_behavior| is run by // RunTask(). Updates |num_items_blocking_shutdown_| if necessary and returns // true if the current shutdown state allows the task to be run. - bool BeforeRunTask(TaskShutdownBehavior effective_shutdown_behavior); + bool BeforeRunTask(TaskShutdownBehavior shutdown_behavior); // Called after a task with |effective_shutdown_behavior| has been run by // RunTask(). Updates |num_items_blocking_shutdown_| if necessary. - void AfterRunTask(TaskShutdownBehavior effective_shutdown_behavior); + void AfterRunTask(TaskShutdownBehavior shutdown_behavior); // Informs this TaskTracker that |task_source| won't be reenqueued and returns // the underlying TaskSource. This is called before destroying a valid
diff --git a/base/task/thread_pool/task_tracker_posix.cc b/base/task/thread_pool/task_tracker_posix.cc index ded8ce1c..eb49a7e 100644 --- a/base/task/thread_pool/task_tracker_posix.cc +++ b/base/task/thread_pool/task_tracker_posix.cc
@@ -15,14 +15,12 @@ TaskTrackerPosix::TaskTrackerPosix(StringPiece name) : TaskTracker(name) {} TaskTrackerPosix::~TaskTrackerPosix() = default; -void TaskTrackerPosix::RunOrSkipTask(Task task, - TaskSource* task_source, - const TaskTraits& traits, - bool can_run_task) { +void TaskTrackerPosix::RunTask(Task task, + TaskSource* task_source, + const TaskTraits& traits) { DCHECK(io_thread_task_runner_); FileDescriptorWatcher file_descriptor_watcher(io_thread_task_runner_); - TaskTracker::RunOrSkipTask(std::move(task), task_source, traits, - can_run_task); + TaskTracker::RunTask(std::move(task), task_source, traits); } } // namespace internal
diff --git a/base/task/thread_pool/task_tracker_posix.h b/base/task/thread_pool/task_tracker_posix.h index 67a0f71b..8f59d53 100644 --- a/base/task/thread_pool/task_tracker_posix.h +++ b/base/task/thread_pool/task_tracker_posix.h
@@ -41,10 +41,9 @@ protected: // TaskTracker: - void RunOrSkipTask(Task task, - TaskSource* task_source, - const TaskTraits& traits, - bool can_run_task) override; + void RunTask(Task task, + TaskSource* task_source, + const TaskTraits& traits) override; private: scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner_;
diff --git a/base/task/thread_pool/thread_pool_impl.cc b/base/task/thread_pool/thread_pool_impl.cc index c8b3ff9..6a58c6a 100644 --- a/base/task/thread_pool/thread_pool_impl.cc +++ b/base/task/thread_pool/thread_pool_impl.cc
@@ -358,7 +358,8 @@ if (!task_source) return false; } - task_tracker_->WillPostTaskNow(task, transaction.traits().priority()); + if (!task_tracker_->WillPostTaskNow(task, transaction.traits().priority())) + return false; transaction.PushTask(std::move(task)); if (task_source) { const TaskTraits traits = transaction.traits();
diff --git a/base/test/ios/wait_util.h b/base/test/ios/wait_util.h index 3a08929c4..35003cb 100644 --- a/base/test/ios/wait_util.h +++ b/base/test/ios/wait_util.h
@@ -33,6 +33,12 @@ // complete. extern const NSTimeInterval kWaitForActionTimeout; +// Constant for timeout in seconds while waiting for clear browsing data. It +// seems this can take a very long time on the bots when running simulators in +// parallel. TODO(crbug.com/993513): Investigate why this is sometimes very +// slow. +extern const NSTimeInterval kWaitForClearBrowsingDataTimeout; + // Constant for timeout in seconds while waiting for cookies operations to // complete. extern const NSTimeInterval kWaitForCookiesTimeout;
diff --git a/base/test/ios/wait_util.mm b/base/test/ios/wait_util.mm index 1e79343..c1538c5 100644 --- a/base/test/ios/wait_util.mm +++ b/base/test/ios/wait_util.mm
@@ -22,6 +22,7 @@ const NSTimeInterval kWaitForDownloadTimeout = 10.0; const NSTimeInterval kWaitForPageLoadTimeout = 10.0; const NSTimeInterval kWaitForActionTimeout = 10.0; +const NSTimeInterval kWaitForClearBrowsingDataTimeout = 45.0; const NSTimeInterval kWaitForCookiesTimeout = 4.0; const NSTimeInterval kWaitForFileOperationTimeout = 2.0;
diff --git a/base/test/launcher/test_launcher_unittest.cc b/base/test/launcher/test_launcher_unittest.cc index 0efd407..e4a9f904 100644 --- a/base/test/launcher/test_launcher_unittest.cc +++ b/base/test/launcher/test_launcher_unittest.cc
@@ -689,6 +689,7 @@ FilePath path = dir.GetPath().AppendASCII("SaveSummaryResult.json"); command_line.AppendSwitchPath("test-launcher-summary-output", path); command_line.AppendSwitch("gtest_also_run_disabled_tests"); + command_line.AppendSwitch("--test-launcher-retry-limit=0"); #if defined(OS_WIN) // In Windows versions prior to Windows 8, nested job objects are // not allowed and cause this test to fail.
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc index 198568c6..d9adc91 100644 --- a/base/test/scoped_task_environment.cc +++ b/base/test/scoped_task_environment.cc
@@ -115,10 +115,9 @@ friend class ScopedTaskEnvironment; // internal::ThreadPoolImpl::TaskTrackerImpl: - void RunOrSkipTask(internal::Task task, - internal::TaskSource* sequence, - const TaskTraits& traits, - bool can_run_task) override; + void RunTask(internal::Task task, + internal::TaskSource* sequence, + const TaskTraits& traits) override; // Synchronizes accesses to members below. mutable Lock lock_; @@ -739,11 +738,10 @@ return true; } -void ScopedTaskEnvironment::TestTaskTracker::RunOrSkipTask( +void ScopedTaskEnvironment::TestTaskTracker::RunTask( internal::Task task, internal::TaskSource* sequence, - const TaskTraits& traits, - bool can_run_task) { + const TaskTraits& traits) { { AutoLock auto_lock(lock_); @@ -753,8 +751,8 @@ ++num_tasks_running_; } - internal::ThreadPoolImpl::TaskTrackerImpl::RunOrSkipTask( - std::move(task), sequence, traits, can_run_task); + internal::ThreadPoolImpl::TaskTrackerImpl::RunTask(std::move(task), sequence, + traits); { AutoLock auto_lock(lock_);
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index 26e23cdc..12ba1f1c 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -8905259265255449984 \ No newline at end of file +8905231778037491328 \ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index 8daaa4d9..37a5393 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -8905261579048864528 \ No newline at end of file +8905235025956967056 \ No newline at end of file
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index b0ac6b0..af0f90f 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -1606,12 +1606,6 @@ shared_libraries = [ ":libchrome" ] - # Android supports webp transparent resources properly since API level 18, - # so this can only be activated for modern ones (which target API >= 21). - # TODO(digit): Turn this on for all builds once JellyBean support is - # dropped in the future. - png_to_webp = _is_modern && !is_java_debug - # Native libraries can be loaded directly from the APK using the # Chromium linker. However, we disable this for J-K due to an OEM-specific # platform bug, where overzealous SELinux settings prevent mapping some apk @@ -1926,6 +1920,9 @@ proguard_enabled = true proguard_configs = [ "//chrome/android/java/apk_for_test.flags" ] } + + # TODO(crbug.com/993340): Update test goldens with webp versions of images. + png_to_webp = false } }
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 82fb51b..cf7e11c2 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -1415,6 +1415,7 @@ "java/src/org/chromium/chrome/browser/share/ShareParams.java", "java/src/org/chromium/chrome/browser/sharing/SharingNotificationUtil.java", "java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java", + "java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallUma.java", "java/src/org/chromium/chrome/browser/sharing/shared_clipboard/SharedClipboardMessageHandler.java", "java/src/org/chromium/chrome/browser/sharing/SharingJNIBridge.java", "java/src/org/chromium/chrome/browser/signin/AccountPickerDialogFragment.java", @@ -1556,6 +1557,7 @@ "java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java", "java/src/org/chromium/chrome/browser/tabmodel/TabModelObserverJniBridge.java", "java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java", + "java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java", "java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java", "java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java", "java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni index 7c94586..5a5960ee 100644 --- a/chrome/android/chrome_public_apk_tmpl.gni +++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -173,6 +173,13 @@ support_zh_hk = true } + # Android supports webp transparent resources properly since API level 18, + # so this can only be activated for modern ones (which target API >= 21). + if (!defined(png_to_webp)) { + png_to_webp = !is_java_debug + } + + # Removes metadata needed for Resources.getIdentifier("resource_name"). strip_resource_names = !is_java_debug # TODO(753402): enable once aapt2>=3.6.0 and bundletool>0.9.0 are rolled in. @@ -449,7 +456,6 @@ if (is_monochrome) { proguard_configs += [ "//android_webview/apk/java/proguard.flags" ] } - png_to_webp = true } if (!defined(deps)) {
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni index 2011387..e6017f7 100644 --- a/chrome/android/chrome_test_java_sources.gni +++ b/chrome/android/chrome_test_java_sources.gni
@@ -45,6 +45,7 @@ "javatests/src/org/chromium/chrome/browser/TabsTest.java", "javatests/src/org/chromium/chrome/browser/UrlSchemeTest.java", "javatests/src/org/chromium/chrome/browser/VideoFullscreenOrientationLockChromeTest.java", + "javatests/src/org/chromium/chrome/browser/ViewHighlighterTestUtils.java", "javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java", "javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java", "javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java", @@ -499,6 +500,8 @@ "javatests/src/org/chromium/chrome/browser/webapps/ActivityAssignerTest.java", "javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenDialogTest.java", "javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenManagerTest.java", + "javatests/src/org/chromium/chrome/browser/webapps/WebApkActivityTest.java", + "javatests/src/org/chromium/chrome/browser/webapps/WebApkActivityTestRule.java", "javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java", "javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java", "javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java",
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java index e9be526..e0c52b0 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java
@@ -46,6 +46,10 @@ /** Default value for initial non cached page size. */ public static final long INITIAL_NON_CACHED_PAGE_SIZE_DEFAULT = 10; + private static final String LIMIT_PAGE_UPDATES_IN_HEAD = "limit_page_updates_in_head"; + /** Default value for whether to update HEAD when making a page request. */ + public static final boolean LIMIT_PAGE_UPDATES_IN_HEAD_DEFAULT = false; + private static final String LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS = "logging_immediate_content_threshold_ms"; /** Default value for logging immediate content threshold. */ @@ -151,6 +155,14 @@ (int) INITIAL_NON_CACHED_PAGE_SIZE_DEFAULT); } + /** @return Whether to update HEAD when making a page request. */ + @VisibleForTesting + static boolean getLimitPageUpdatesInHead() { + return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean( + ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS, LIMIT_PAGE_UPDATES_IN_HEAD, + LIMIT_PAGE_UPDATES_IN_HEAD_DEFAULT); + } + /** * @return How long before showing content after opening NTP is no longer considered immediate * in UMA. @@ -290,6 +302,8 @@ .put(ConfigKey.FEED_UI_ENABLED, FeedConfiguration.getFeedUiEnabled()) .put(ConfigKey.INITIAL_NON_CACHED_PAGE_SIZE, FeedConfiguration.getInitialNonCachedPageSize()) + .put(ConfigKey.LIMIT_PAGE_UPDATES_IN_HEAD, + FeedConfiguration.getLimitPageUpdatesInHead()) .put(ConfigKey.LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS, FeedConfiguration.getLoggingImmediateContentThresholdMs()) .put(ConfigKey.MANAGE_INTERESTS_ENABLED,
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/NtpStreamLifecycleManager.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/NtpStreamLifecycleManager.java index 6d60a681c..48adcec2 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/NtpStreamLifecycleManager.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/NtpStreamLifecycleManager.java
@@ -5,9 +5,9 @@ package org.chromium.chrome.browser.feed; import android.app.Activity; +import android.support.annotation.Nullable; import com.google.android.libraries.feed.api.client.stream.Stream; -import com.sun.istack.internal.Nullable; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.ntp.NewTabPage;
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java index 5c453ce..550fed8 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java
@@ -6,9 +6,9 @@ import android.app.Activity; import android.support.annotation.IntDef; +import android.support.annotation.Nullable; import com.google.android.libraries.feed.api.client.stream.Stream; -import com.sun.istack.internal.Nullable; import org.chromium.base.ActivityState; import org.chromium.base.ApplicationStatus;
diff --git a/chrome/android/java/res/drawable/ic_error_blue_18dp.xml b/chrome/android/java/res/drawable/ic_error_blue_18dp.xml new file mode 100644 index 0000000..75c86e6 --- /dev/null +++ b/chrome/android/java/res/drawable/ic_error_blue_18dp.xml
@@ -0,0 +1,16 @@ +<?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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:targetApi="21" + android:width="18dp" + android:height="18dp" + android:viewportWidth="48.0" + android:viewportHeight="48.0"> + <path + android:pathData="M24 4C12.96 4 4 12.95 4 24s8.96 20 20 20 20-8.95 20-20S35.04 4 24 4zm2 30h-4v-4h4v4zm0-8h-4V14h4v12z" + android:fillColor="@color/default_icon_color_blue"/> +</vector>
diff --git a/chrome/android/java/res/layout/sms_receiver_dialog.xml b/chrome/android/java/res/layout/sms_receiver_dialog.xml index 6e9208f..aa402a4 100644 --- a/chrome/android/java/res/layout/sms_receiver_dialog.xml +++ b/chrome/android/java/res/layout/sms_receiver_dialog.xml
@@ -38,6 +38,13 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" > <ImageView + android:id="@+id/error_icon" + android:layout_height="20dp" + android:layout_width="20dp" + android:visibility="gone" + app:srcCompat="@drawable/ic_error_blue_18dp" + tools:ignore="ContentDescription" /> + <ImageView android:id="@+id/done_icon" android:layout_height="20dp" android:layout_width="20dp" @@ -70,11 +77,11 @@ app:stackedMargin="@dimen/button_bar_stacked_margin" app:buttonAlignment="end"> <org.chromium.ui.widget.ButtonCompat - android:id="@+id/confirm_button" + android:id="@+id/confirm_or_try_again_button" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_gravity="end" - android:text="@string/sms_dialog_confirm_button_text" + android:text="@string/confirm" android:enabled="false" style="@style/TextButton" /> <org.chromium.ui.widget.ButtonCompat
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml index faf144d7..be8b661 100644 --- a/chrome/android/java/res/values/colors.xml +++ b/chrome/android/java/res/values/colors.xml
@@ -85,9 +85,9 @@ <!-- Compositor Tab Colors --> <color name="compositor_background_tab_bg">@color/modern_grey_300</color> - <color name="compositor_background_tab_bg_incognito">#2A2D2F</color> + <color name="compositor_background_tab_bg_incognito">@color/modern_grey_900</color> <color name="compositor_background_tab_outline">#ACB1B6</color> - <color name="compositor_background_tab_outline_incognito">#0E0F10</color> + <color name="compositor_background_tab_outline_incognito">@android:color/black</color> <!-- Compositor Tab Title Colors --> <color name="compositor_tab_title_bar_text">@color/default_text_color</color> @@ -125,11 +125,11 @@ <color name="progress_bar_background">#3D4386F7</color> <!-- Incognito background and branding colors --> - <color name="incognito_modern_primary_color">@color/modern_grey_800</color> + <color name="incognito_modern_primary_color">@color/default_bg_color_dark_elev_3</color> <!-- Toolbar colors --> <color name="toolbar_background_primary">@color/default_bg_color_elev_3</color> - <color name="toolbar_background_primary_incognito">@color/modern_grey_800</color> + <color name="toolbar_background_primary_incognito">@color/default_bg_color_dark_elev_3</color> <color name="toolbar_text_box_background">@color/modern_grey_100</color> <color name="toolbar_text_box_background_incognito">@color/black_alpha_38</color> <color name="toolbar_shadow_color">@color/black_alpha_11</color>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DeviceConditions.java b/chrome/android/java/src/org/chromium/chrome/browser/DeviceConditions.java index c8dd1db..1697fc1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/DeviceConditions.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/DeviceConditions.java
@@ -5,6 +5,7 @@ package org.chromium.chrome.browser; import android.annotation.TargetApi; +import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -14,6 +15,7 @@ import android.os.Build; import android.os.PowerManager; +import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.VisibleForTesting; import org.chromium.net.ConnectionType; import org.chromium.net.NetworkChangeNotifier; @@ -27,6 +29,7 @@ private boolean mPowerConnected; private int mBatteryPercentage; private boolean mPowerSaveOn; + private boolean mScreenOnAndUnlocked; // Network related variables. private @ConnectionType int mNetConnectionType = ConnectionType.CONNECTION_UNKNOWN; @@ -42,12 +45,13 @@ */ @VisibleForTesting public DeviceConditions(boolean powerConnected, int batteryPercentage, int netConnectionType, - boolean powerSaveOn, boolean activeNetworkMetered) { + boolean powerSaveOn, boolean activeNetworkMetered, boolean screenOnAndUnlocked) { mPowerConnected = powerConnected; mBatteryPercentage = batteryPercentage; mPowerSaveOn = powerSaveOn; mNetConnectionType = netConnectionType; mActiveNetworkMetered = activeNetworkMetered; + mScreenOnAndUnlocked = screenOnAndUnlocked; } @VisibleForTesting @@ -64,7 +68,7 @@ return new DeviceConditions(isCurrentlyPowerConnected(context, batteryStatus), getCurrentBatteryPercentage(context, batteryStatus), getCurrentNetConnectionType(context), isCurrentlyInPowerSaveMode(context), - isCurrentActiveNetworkMetered(context)); + isCurrentActiveNetworkMetered(context), isCurrentlyScreenOnAndUnlocked(context)); } /** @return Whether the device is connected to a power source. */ @@ -173,6 +177,16 @@ return cm.isActiveNetworkMetered(); } + /** + * @return Whether the screen is currently on and unlocked. + */ + public static boolean isCurrentlyScreenOnAndUnlocked(Context context) { + KeyguardManager keyguardManager = + (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); + return keyguardManager != null && !keyguardManager.isKeyguardLocked() + && ApiCompatibilityUtils.isInteractive(context); + } + private static Intent getBatteryStatus(Context context) { IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); // Note this is a sticky intent, so we aren't really registering a receiver, just getting @@ -232,4 +246,9 @@ public boolean isActiveNetworkMetered() { return mActiveNetworkMetered; } + + /** Returns whether the screen is on and unlocked. */ + public boolean isScreenOnAndUnlocked() { + return mScreenOnAndUnlocked; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/SingleTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/SingleTabActivity.java index 845ffde..aa9378bd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/SingleTabActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/SingleTabActivity.java
@@ -50,12 +50,12 @@ } @Override - protected Pair<TabDelegate, TabDelegate> createTabCreators() { - return Pair.create(createTabDelegate(false), createTabDelegate(true)); + protected Pair<? extends TabCreator, ? extends TabCreator> createTabCreators() { + return Pair.create(createTabCreator(false), createTabCreator(true)); } /** Creates TabDelegates for opening new Tabs. */ - protected TabDelegate createTabDelegate(boolean incognito) { + protected TabCreator createTabCreator(boolean incognito) { return new TabDelegate(incognito); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java index 8681b2b..2c291d9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
@@ -274,7 +274,8 @@ int tint = mIncognito ? R.color.compositor_background_tab_bg_incognito : R.color.compositor_background_tab_bg; if (foreground) { - tint = mIncognito ? R.color.modern_grey_800 : R.color.default_bg_color_elev_3; + tint = mIncognito ? R.color.default_bg_color_dark_elev_3 + : R.color.default_bg_color_elev_3; } return mContext.getResources().getColor(tint); @@ -288,7 +289,8 @@ int tint = mIncognito ? R.color.compositor_background_tab_outline_incognito : R.color.compositor_background_tab_outline; if (foreground) { - tint = mIncognito ? R.color.modern_grey_800 : R.color.default_bg_color_elev_3; + tint = mIncognito ? R.color.default_bg_color_dark_elev_3 + : R.color.default_bg_color_elev_3; } return mContext.getResources().getColor(tint);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java index 65741685..3130563 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java
@@ -13,7 +13,6 @@ import org.chromium.base.ContextUtils; import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.metrics.CachedMetrics; import org.chromium.chrome.R; import org.chromium.chrome.browser.notifications.NotificationConstants; import org.chromium.chrome.browser.notifications.NotificationUmaTracker; @@ -41,9 +40,8 @@ dialIntent = new Intent(Intent.ACTION_DIAL); } dialIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - ContextUtils.getApplicationContext().startActivity(dialIntent); - new CachedMetrics.BooleanHistogramSample("Sharing.ClickToCallDialIntent") - .record(TextUtils.isEmpty(phoneNumber)); + context.startActivity(dialIntent); + ClickToCallUma.recordDialerShown(TextUtils.isEmpty(phoneNumber)); } } @@ -54,6 +52,7 @@ */ @CalledByNative private static void showNotification(String phoneNumber) { + ClickToCallUma.recordMessageReceived(); Context context = ContextUtils.getApplicationContext(); PendingIntentProvider contentIntent = PendingIntentProvider.getBroadcast(context, /*requestCode=*/0,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallUma.java b/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallUma.java new file mode 100644 index 0000000..0a0fe0b --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallUma.java
@@ -0,0 +1,122 @@ +// 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.sharing.click_to_call; + +import android.content.Context; +import android.os.Handler; +import android.os.SystemClock; +import android.support.annotation.IntDef; +import android.support.annotation.MainThread; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; + +import org.chromium.base.ApplicationStatus; +import org.chromium.base.ContextUtils; +import org.chromium.base.metrics.CachedMetrics; +import org.chromium.chrome.browser.DeviceConditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Helper Class for Click to Call UMA Collection. + */ +public class ClickToCallUma { + // Keep in sync with the ClickToCallDeviceState enum in enums.xml. + @IntDef({ClickToCallDeviceState.SCREEN_OFF_BACKGROUND, + ClickToCallDeviceState.SCREEN_ON_BACKGROUND, + ClickToCallDeviceState.SCREEN_OFF_FOREGROUND, + ClickToCallDeviceState.SCREEN_ON_FOREGROUND}) + @Retention(RetentionPolicy.SOURCE) + private @interface ClickToCallDeviceState { + int SCREEN_OFF_BACKGROUND = 0; + int SCREEN_ON_BACKGROUND = 1; + int SCREEN_OFF_FOREGROUND = 2; + int SCREEN_ON_FOREGROUND = 3; + int NUM_ENTRIES = 4; + } + + private static @ClickToCallDeviceState int getDeviceState(Context context) { + boolean isScreenOn = DeviceConditions.isCurrentlyScreenOnAndUnlocked(context); + boolean isInForeground = ApplicationStatus.hasVisibleActivities(); + + if (isInForeground) { + return isScreenOn ? ClickToCallDeviceState.SCREEN_ON_FOREGROUND + : ClickToCallDeviceState.SCREEN_OFF_FOREGROUND; + } else { + return isScreenOn ? ClickToCallDeviceState.SCREEN_ON_BACKGROUND + : ClickToCallDeviceState.SCREEN_OFF_BACKGROUND; + } + } + + /** + * Listens for outgoing phone calls for TIMEOUT_MS and adds metrics if there was one within that + * time frame. This is only used to measure successful usages of the Click to Call feature and + * does not contain any data other than the time it took from opening the dialer to a call being + * initiated. + */ + private static final class CallMetricListener extends PhoneStateListener { + // Maximum time we wait for an outgoing call to be made. + private static final long TIMEOUT_MS = 30000; + + // Current instance of a registered listener, or null if none running. + private static CallMetricListener sListener; + + private final Handler mHandler = new Handler(); + private final Runnable mTimeoutRunnable = this::stopMetric; + private final long mDialerOpenTime = SystemClock.uptimeMillis(); + private final TelephonyManager mTelephonyManager; + + private CallMetricListener(Context context) { + mTelephonyManager = + (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE); + mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS); + } + + @Override + public void onCallStateChanged(int state, String number) { + // Note: |number| will always be empty as we don't have permissions to read it. + if (sListener != this || state != TelephonyManager.CALL_STATE_OFFHOOK) return; + + // Record successful Click to Call journey and the time it took to initiate a call. + recordCallMade(SystemClock.uptimeMillis() - mDialerOpenTime); + + stopMetric(); + } + + @MainThread + public static void startMetric(Context context) { + if (sListener != null) sListener.stopMetric(); + sListener = new CallMetricListener(context); + } + + @MainThread + private void stopMetric() { + if (sListener != this) return; + mHandler.removeCallbacks(mTimeoutRunnable); + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE); + sListener = null; + } + } + + private static void recordCallMade(long timeFromDialerToCallMs) { + new CachedMetrics.MediumTimesHistogramSample("Sharing.ClickToCallPhoneCall") + .record(timeFromDialerToCallMs); + } + + public static void recordDialerShown(boolean emptyPhoneNumber) { + CallMetricListener.startMetric(ContextUtils.getApplicationContext()); + new CachedMetrics.BooleanHistogramSample("Sharing.ClickToCallDialIntent") + .record(emptyPhoneNumber); + } + + public static void recordMessageReceived() { + new CachedMetrics + .EnumeratedHistogramSample( + "Sharing.ClickToCallReceiveDeviceState", ClickToCallDeviceState.NUM_ENTRIES) + .record(getDeviceState(ContextUtils.getApplicationContext())); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sms/SmsReceiverDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/sms/SmsReceiverDialog.java index 66c8ba7..daef54a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sms/SmsReceiverDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sms/SmsReceiverDialog.java
@@ -23,6 +23,7 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.NativeMethods; import org.chromium.chrome.R; +import org.chromium.content_public.browser.sms.Event; import org.chromium.ui.base.WindowAndroid; /** @@ -38,8 +39,10 @@ private Dialog mDialog; private ProgressBar mProgressBar; private ImageView mDoneIcon; + private ImageView mErrorIcon; private TextView mStatus; - private Button mConfirmButton; + private Button mCancelButton; + private Button mConfirmOrTryAgainButton; private String mOrigin; @VisibleForTesting @@ -67,18 +70,21 @@ mDoneIcon = (ImageView) dialogContainer.findViewById(R.id.done_icon); + mErrorIcon = (ImageView) dialogContainer.findViewById(R.id.error_icon); + mStatus = (TextView) dialogContainer.findViewById(R.id.status); - Button cancelButton = (Button) dialogContainer.findViewById(R.id.cancel_button); - cancelButton.setOnClickListener(v -> { + mCancelButton = (Button) dialogContainer.findViewById(R.id.cancel_button); + mCancelButton.setOnClickListener(v -> { assert mNativeSmsDialogAndroid != 0; - SmsReceiverDialogJni.get().onCancel(mNativeSmsDialogAndroid); + SmsReceiverDialogJni.get().onEvent(mNativeSmsDialogAndroid, Event.CANCEL); }); - mConfirmButton = (Button) dialogContainer.findViewById(R.id.confirm_button); - mConfirmButton.setOnClickListener(v -> { + mConfirmOrTryAgainButton = + (Button) dialogContainer.findViewById(R.id.confirm_or_try_again_button); + mConfirmOrTryAgainButton.setOnClickListener(v -> { assert mNativeSmsDialogAndroid != 0; - SmsReceiverDialogJni.get().onConfirm(mNativeSmsDialogAndroid); + SmsReceiverDialogJni.get().onEvent(mNativeSmsDialogAndroid, Event.CONFIRM); }); mDialog = new Dialog(mActivity); @@ -131,7 +137,23 @@ mProgressBar.setVisibility(View.GONE); mDoneIcon.setVisibility(View.VISIBLE); mStatus.setText(mActivity.getText(R.string.sms_dialog_status_sms_received)); - mConfirmButton.setEnabled(true); + mConfirmOrTryAgainButton.setEnabled(true); + } + + @CalledByNative + void smsTimeout() { + if (DEBUG) Log.d(TAG, "SmsReceiverDialog.smsTimeout()"); + + mProgressBar.setVisibility(View.GONE); + mErrorIcon.setVisibility(View.VISIBLE); + mStatus.setText(mActivity.getText(R.string.sms_dialog_status_timeout)); + mCancelButton.setVisibility(View.GONE); + mConfirmOrTryAgainButton.setText(mActivity.getText(R.string.try_again)); + mConfirmOrTryAgainButton.setEnabled(true); + mConfirmOrTryAgainButton.setOnClickListener(v -> { + assert mNativeSmsDialogAndroid != 0; + SmsReceiverDialogJni.get().onEvent(mNativeSmsDialogAndroid, Event.TIMEOUT); + }); } /** @@ -149,7 +171,6 @@ @NativeMethods interface Natives { - void onCancel(long nativeSmsDialogAndroid); - void onConfirm(long nativeSmsDialogAndroid); + void onEvent(long nativeSmsDialogAndroid, int eventType); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/SingleTabModel.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/SingleTabModel.java index b1140bd..22e08e19 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/SingleTabModel.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/SingleTabModel.java
@@ -77,6 +77,7 @@ @Override public int indexOf(Tab tab) { + if (tab == null) return INVALID_TAB_INDEX; return mTab != null && mTab.getId() == tab.getId() ? 0 : INVALID_TAB_INDEX; } @@ -190,7 +191,9 @@ } @Override - public void addTab(Tab tab, int index, @TabLaunchType int type) {} + public void addTab(Tab tab, int index, @TabLaunchType int type) { + setTab(tab); + } @Override public void removeTab(Tab tab) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java index 8dfec2e..5a08a019 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java
@@ -1,26 +1,15 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// 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.tabmodel; import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.browser.tab.TabAttributeKeys; -import org.chromium.chrome.browser.tab.TabAttributes; /** * This class acts as a controller for determining where tabs should be inserted - * into a tab strip model. See tab_strip_model_order_controller.cc and - * tab_strip_model.cc + * into a tab strip model. */ -public class TabModelOrderController { - private static final int NO_TAB = -1; - private final TabModelSelector mTabModelSelector; - - public TabModelOrderController(TabModelSelector modelSelector) { - mTabModelSelector = modelSelector; - } - +public interface TabModelOrderController { /** * Determine the insertion index of the next tab. If it's not the result of * a link being pressed, the provided index will be returned. @@ -29,20 +18,7 @@ * @param position The provided position. * @return Where to insert the tab. */ - public int determineInsertionIndex(@TabLaunchType int type, int position, Tab newTab) { - if (type == TabLaunchType.FROM_BROWSER_ACTIONS) return -1; - if (linkClicked(type)) { - position = determineInsertionIndex(type, newTab); - } - - if (willOpenInForeground(type, newTab.isIncognito())) { - // Forget any existing relationships, we don't want to make things - // too confusing by having multiple groups active at the same time. - forgetAllOpeners(); - } - - return position; - } + int determineInsertionIndex(@TabLaunchType int type, int position, Tab newTab); /** * Determine the insertion index of the next tab. @@ -50,88 +26,7 @@ * @param type The launch type of the new tab. * @return Where to insert the tab. */ - public int determineInsertionIndex(@TabLaunchType int type, Tab newTab) { - TabModel currentModel = mTabModelSelector.getCurrentModel(); - - if (sameModelType(currentModel, newTab)) { - Tab currentTab = TabModelUtils.getCurrentTab(currentModel); - if (currentTab == null) { - assert (currentModel.getCount() == 0); - return 0; - } - int currentId = currentTab.getId(); - int currentIndex = TabModelUtils.getTabIndexById(currentModel, currentId); - - if (willOpenInForeground(type, newTab.isIncognito())) { - // If the tab was opened in the foreground, insert it adjacent to its parent tab if - // that exists and that tab is not the current selected tab, else insert the tab - // adjacent to the current tab that opened that link. - Tab parentTab = TabModelUtils.getTabById(currentModel, newTab.getParentId()); - if (parentTab != null && currentTab != parentTab) { - int parentTabIndex = - TabModelUtils.getTabIndexById(currentModel, parentTab.getId()); - return parentTabIndex + 1; - } - return currentIndex + 1; - } else { - // If the tab was opened in the background, position at the end of - // it's 'group'. - int index = getIndexOfLastTabOpenedBy(currentId, currentIndex); - if (index != NO_TAB) { - return index + 1; - } else { - return currentIndex + 1; - } - } - } else { - // If the tab is opening in the other model type, just put it at the end. - return mTabModelSelector.getModel(newTab.isIncognito()).getCount(); - } - } - - /** - * Returns the index of the last tab in the model opened by the specified - * opener, starting at startIndex. To clarify, the tabs are traversed in the - * descending order of their position in the model. This means that the tab - * furthest in the stack with the given opener id will be returned. - * - * @param openerId The opener of interest. - * @param startIndex The start point of the search. - * @return The last tab if found, NO_TAB otherwise. - */ - private int getIndexOfLastTabOpenedBy(int openerId, int startIndex) { - TabModel currentModel = mTabModelSelector.getCurrentModel(); - int count = currentModel.getCount(); - for (int i = count - 1; i >= startIndex; i--) { - Tab tab = currentModel.getTabAt(i); - if (tab.getParentId() == openerId - && TabAttributes.from(tab).get(TabAttributeKeys.GROUPED_WITH_PARENT, true)) { - return i; - } - } - return NO_TAB; - } - - /** - * Clear the opener attribute on all tabs in the model. - */ - void forgetAllOpeners() { - TabModel currentModel = mTabModelSelector.getCurrentModel(); - int count = currentModel.getCount(); - for (int i = 0; i < count; i++) { - TabAttributes.from(currentModel.getTabAt(i)) - .set(TabAttributeKeys.GROUPED_WITH_PARENT, false); - } - } - - /** - * Determine if a launch type is the result of linked being clicked. - */ - static boolean linkClicked(@TabLaunchType int type) { - return type == TabLaunchType.FROM_LINK - || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND - || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND; - } + int determineInsertionIndex(@TabLaunchType int type, Tab newTab); /** * Determine if a launch type will result in the tab being opened in the @@ -140,19 +35,5 @@ * @param isNewTabIncognito True if the new opened tab is incognito. * @return True if the tab will be in the foreground */ - public boolean willOpenInForeground(@TabLaunchType int type, boolean isNewTabIncognito) { - // Restore is handling the active index by itself. - if (type == TabLaunchType.FROM_RESTORE || type == TabLaunchType.FROM_BROWSER_ACTIONS) { - return false; - } - return type != TabLaunchType.FROM_LONGPRESS_BACKGROUND - || (!mTabModelSelector.isIncognitoSelected() && isNewTabIncognito); - } - - /** - * @return {@code true} If both tabs have the same model type, {@code false} otherwise. - */ - static boolean sameModelType(TabModel model, Tab tab) { - return model.isIncognito() == tab.isIncognito(); - } + boolean willOpenInForeground(@TabLaunchType int type, boolean isNewTabIncognito); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java new file mode 100644 index 0000000..6d15eb6 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
@@ -0,0 +1,138 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tabmodel; + +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabAttributeKeys; +import org.chromium.chrome.browser.tab.TabAttributes; + +/** + * Implementation of the TabModelOrderController based off of tab_strip_model_order_controller.cc + * and tab_strip_model.cc + */ +public class TabModelOrderControllerImpl implements TabModelOrderController { + private static final int NO_TAB = -1; + private final TabModelSelector mTabModelSelector; + + public TabModelOrderControllerImpl(TabModelSelector modelSelector) { + mTabModelSelector = modelSelector; + } + + @Override + public int determineInsertionIndex(@TabLaunchType int type, int position, Tab newTab) { + if (type == TabLaunchType.FROM_BROWSER_ACTIONS) return -1; + if (linkClicked(type)) { + position = determineInsertionIndex(type, newTab); + } + + if (willOpenInForeground(type, newTab.isIncognito())) { + // Forget any existing relationships, we don't want to make things + // too confusing by having multiple groups active at the same time. + forgetAllOpeners(); + } + + return position; + } + + @Override + public int determineInsertionIndex(@TabLaunchType int type, Tab newTab) { + TabModel currentModel = mTabModelSelector.getCurrentModel(); + + if (sameModelType(currentModel, newTab)) { + Tab currentTab = TabModelUtils.getCurrentTab(currentModel); + if (currentTab == null) { + assert (currentModel.getCount() == 0); + return 0; + } + int currentId = currentTab.getId(); + int currentIndex = TabModelUtils.getTabIndexById(currentModel, currentId); + + if (willOpenInForeground(type, newTab.isIncognito())) { + // If the tab was opened in the foreground, insert it adjacent to its parent tab if + // that exists and that tab is not the current selected tab, else insert the tab + // adjacent to the current tab that opened that link. + Tab parentTab = TabModelUtils.getTabById(currentModel, newTab.getParentId()); + if (parentTab != null && currentTab != parentTab) { + int parentTabIndex = + TabModelUtils.getTabIndexById(currentModel, parentTab.getId()); + return parentTabIndex + 1; + } + return currentIndex + 1; + } else { + // If the tab was opened in the background, position at the end of + // it's 'group'. + int index = getIndexOfLastTabOpenedBy(currentId, currentIndex); + if (index != NO_TAB) { + return index + 1; + } else { + return currentIndex + 1; + } + } + } else { + // If the tab is opening in the other model type, just put it at the end. + return mTabModelSelector.getModel(newTab.isIncognito()).getCount(); + } + } + + /** + * Returns the index of the last tab in the model opened by the specified + * opener, starting at startIndex. To clarify, the tabs are traversed in the + * descending order of their position in the model. This means that the tab + * furthest in the stack with the given opener id will be returned. + * + * @param openerId The opener of interest. + * @param startIndex The start point of the search. + * @return The last tab if found, NO_TAB otherwise. + */ + private int getIndexOfLastTabOpenedBy(int openerId, int startIndex) { + TabModel currentModel = mTabModelSelector.getCurrentModel(); + int count = currentModel.getCount(); + for (int i = count - 1; i >= startIndex; i--) { + Tab tab = currentModel.getTabAt(i); + if (tab.getParentId() == openerId + && TabAttributes.from(tab).get(TabAttributeKeys.GROUPED_WITH_PARENT, true)) { + return i; + } + } + return NO_TAB; + } + + /** + * Clear the opener attribute on all tabs in the model. + */ + void forgetAllOpeners() { + TabModel currentModel = mTabModelSelector.getCurrentModel(); + int count = currentModel.getCount(); + for (int i = 0; i < count; i++) { + TabAttributes.from(currentModel.getTabAt(i)) + .set(TabAttributeKeys.GROUPED_WITH_PARENT, false); + } + } + + /** + * Determine if a launch type is the result of linked being clicked. + */ + static boolean linkClicked(@TabLaunchType int type) { + return type == TabLaunchType.FROM_LINK || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND + || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND; + } + + @Override + public boolean willOpenInForeground(@TabLaunchType int type, boolean isNewTabIncognito) { + // Restore is handling the active index by itself. + if (type == TabLaunchType.FROM_RESTORE || type == TabLaunchType.FROM_BROWSER_ACTIONS) { + return false; + } + return type != TabLaunchType.FROM_LONGPRESS_BACKGROUND + || (!mTabModelSelector.isIncognitoSelected() && isNewTabIncognito); + } + + /** + * @return {@code true} If both tabs have the same model type, {@code false} otherwise. + */ + static boolean sameModelType(TabModel model, Tab tab) { + return model.isIncognito() == tab.isIncognito(); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java index 4de1cce0..1d2506a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
@@ -79,7 +79,7 @@ mIsTabbedActivityForSync = isTabbedActivity; mTabSaver = new TabPersistentStore( persistencePolicy, this, mTabCreatorManager, persistentStoreObserver); - mOrderController = new TabModelOrderController(this); + mOrderController = new TabModelOrderControllerImpl(this); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java index fc79a59..1ec7ed2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -49,7 +49,6 @@ import org.chromium.chrome.browser.tab.TabObserver; import org.chromium.chrome.browser.tab.TabObserverRegistrar; import org.chromium.chrome.browser.tab.TabState; -import org.chromium.chrome.browser.tabmodel.document.TabDelegate; import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer; import org.chromium.chrome.browser.util.ColorUtils; import org.chromium.chrome.browser.widget.TintedDrawable; @@ -834,7 +833,7 @@ } @Override - protected TabDelegate createTabDelegate(boolean incognito) { + protected TabCreator createTabCreator(boolean incognito) { return new WebappTabDelegate(incognito, mWebappInfo); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java index b39eea65..92fe4f1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
@@ -40,8 +40,6 @@ .Callback2<Integer, MakeCredentialAuthenticatorResponse> mMakeCredentialCallback; private org.chromium.mojo.bindings.Callbacks .Callback2<Integer, GetAssertionAuthenticatorResponse> mGetAssertionCallback; - private org.chromium.mojo.bindings.Callbacks - .Callback1<Boolean> mIsUserVerifyingPlatformAuthenticatorAvailableCallback; /** * Builds the Authenticator service implementation.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/Fido2ApiHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/Fido2ApiHandler.java index c7ba264..2a6670f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/Fido2ApiHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/Fido2ApiHandler.java
@@ -50,7 +50,4 @@ protected void getAssertion(PublicKeyCredentialRequestOptions options, RenderFrameHost frameHost, HandlerResponseCallback callback) {} - - protected void isUserVerifyingPlatformAuthenticatorAvailable( - RenderFrameHost frameHost, HandlerResponseCallback callback) {} }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java index f90efbe6..fbd9bb1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java
@@ -24,12 +24,6 @@ public void onSignResponse(Integer status, GetAssertionAuthenticatorResponse response){}; /** - * Callback for handling response from a request to call - * isUserVerifyingPlatformAuthenticatorAvailable. - */ - public void onIsUserVerifyingPlatformAuthenticatorAvailableResponse(boolean isUVPAA){}; - - /** * Callback for handling any errors from either register or sign requests. */ public void onError(Integer status){};
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java index 546be1fb..c87b5f88 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
@@ -199,8 +199,7 @@ public void setUseLightPulseColor(Resources resources, boolean useLightPulseColor) { @ColorInt int color = ApiCompatibilityUtils.getColor(resources, - useLightPulseColor ? R.color.modern_secondary_color - : R.color.default_icon_color_blue); + useLightPulseColor ? R.color.modern_blue_300 : R.color.default_icon_color_blue); if (mState.color == color) return; int alpha = getAlpha();
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd index 2a6cc23..bba9ee9b 100644 --- a/chrome/android/java/strings/android_chrome_strings.grd +++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3966,8 +3966,8 @@ <message name="IDS_SMS_DIALOG_STATUS_SMS_RECEIVED" desc="Message shown when Chrome has received an SMS on the user's behalf"> Text message received </message> - <message name="IDS_SMS_DIALOG_CONFIRM_BUTTON_TEXT" desc="Label on the button that users can click to confirm SMS verification by passing the incoming SMS to the site."> - Confirm + <message name="IDS_SMS_DIALOG_STATUS_TIMEOUT" desc="Message shown when SMS verification timed out."> + Something went wrong </message> <message name="IDS_TEST_DUMMY_MODULE_TITLE"
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_CONFIRM_BUTTON_TEXT.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_CONFIRM_BUTTON_TEXT.png.sha1 deleted file mode 100644 index 4196c98..0000000 --- a/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_CONFIRM_BUTTON_TEXT.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -3851b10baa9e6cd314477667cdac69e549ba2e52 \ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_SMS_RECEIVED.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_SMS_RECEIVED.png.sha1 index 4196c98..b9364de 100644 --- a/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_SMS_RECEIVED.png.sha1 +++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_SMS_RECEIVED.png.sha1
@@ -1 +1 @@ -3851b10baa9e6cd314477667cdac69e549ba2e52 \ No newline at end of file +3851b10baa9e6cd314477667cdac69e549ba2e52
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_TIMEOUT.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_TIMEOUT.png.sha1 new file mode 100644 index 0000000..930e31f --- /dev/null +++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_TIMEOUT.png.sha1
@@ -0,0 +1 @@ +06b38a1c7e0379eb1a4407829baf8b8300c74744 \ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_WAITING.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_WAITING.png.sha1 index eb85055..c97e8c0 100644 --- a/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_WAITING.png.sha1 +++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_STATUS_WAITING.png.sha1
@@ -1 +1 @@ -9a38cd3599c74c36b46f3bf4edf565fa8280eb9d \ No newline at end of file +9a38cd3599c74c36b46f3bf4edf565fa8280eb9d
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_TITLE.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_TITLE.png.sha1 index 5776234..c97e8c0 100644 --- a/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_TITLE.png.sha1 +++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_SMS_DIALOG_TITLE.png.sha1
@@ -1 +1 @@ -ef2666b60a9adfb774b327ecec89f1ef090a78a4 \ No newline at end of file +9a38cd3599c74c36b46f3bf4edf565fa8280eb9d
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ServicificationBackgroundServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ServicificationBackgroundServiceTest.java index 44da274..80b96eb 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ServicificationBackgroundServiceTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ServicificationBackgroundServiceTest.java
@@ -19,6 +19,7 @@ import org.chromium.base.Log; import org.chromium.base.StrictModeContext; import org.chromium.base.metrics.RecordHistogram; +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.browser.init.ServiceManagerStartupUtils; @@ -118,6 +119,7 @@ @Test @LargeTest @Feature({"ServicificationStartup"}) + @CommandLineFlags.Add("force-fieldtrials=*Foo/Bar") public void testHistogramsPersistedWithServiceManagerOnlyStart() { createBrowserMetricsSpareFile(); Assert.assertTrue(mSpareFile.exists());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ViewHighlighterTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ViewHighlighterTestUtils.java new file mode 100644 index 0000000..bc27f18 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ViewHighlighterTestUtils.java
@@ -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. + +package org.chromium.chrome.browser; + +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.view.View; + +import org.chromium.chrome.browser.widget.PulseDrawable; + +/** + * Allows for testing of views which are highlightable via ViewHighlighter. + */ +public class ViewHighlighterTestUtils { + /** + * Returns true if the provided view is currently being highlighted. + * Please note that this function may not be the same as !checkHighlightOff. + * + * @param view The view which you'd like to check for highlighting. + * @return True if the view is currently being highlighted. + */ + public static boolean checkHighlightOn(View view) { + if (!(view.getBackground() instanceof LayerDrawable)) return false; + LayerDrawable layerDrawable = (LayerDrawable) view.getBackground(); + Drawable drawable = layerDrawable.getDrawable(layerDrawable.getNumberOfLayers() - 1); + if (!(drawable instanceof PulseDrawable)) return false; + PulseDrawable pulse = (PulseDrawable) drawable; + return pulse.isRunning() && pulse.isVisible(); + } + + /** + * Returns true if the provided view is not currently being highlighted. + * Please note that this function may not be the same as !checkHighlightOn. + * + * @param view The view which you'd like to check for highlighting. + * @return True if view is not currently being highlighted. + */ + public static boolean checkHighlightOff(View view) { + return !(view.getBackground() instanceof LayerDrawable); + } +} \ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java index eb54238..ec4ea21 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
@@ -9,6 +9,9 @@ import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightOff; +import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightOn; + import android.support.test.filters.MediumTest; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.View; @@ -604,6 +607,144 @@ "Expected \"Other Bookmarks\" folder to appear!", 2, adapter.getItemCount()); } + @Test + @MediumTest + public void testShowInFolder_NoScroll() throws Exception { + addFolder(TEST_FOLDER_TITLE); + forceSyncHeaderState(); + openBookmarkManager(); + + // Enter search mode. + View searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id); + TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick); + CriteriaHelper.pollUiThread( + () -> mManager.getToolbarForTests().isSearching(), "Expected to enter search mode"); + + // Click "Show in folder". + View testFolder = mItemsContainer.findViewHolderForAdapterPosition(0).itemView; + View more = testFolder.findViewById(R.id.more); + Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE, + ((BookmarkFolderRow) testFolder).getTitle()); + TestThreadUtils.runOnUiThreadBlocking(more::performClick); + onView(withText("Show in folder")).perform(click()); + + Assert.assertTrue( + "Expected bookmark row to be highlighted after clicking \"show in folder\"", + checkHighlightOn(testFolder)); + + // Enter search mode again. + searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id); + TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick); + CriteriaHelper.pollUiThread( + () -> mManager.getToolbarForTests().isSearching(), "Expected to enter search mode"); + + Assert.assertTrue("Expected bookmark row to not be highlighted " + + "after entering search mode", + checkHighlightOff(testFolder)); + + // Click "Show in folder" again. + TestThreadUtils.runOnUiThreadBlocking(more::performClick); + onView(withText("Show in folder")).perform(click()); + + // Check that the highlight is on. + Assert.assertTrue("Expected highlight to successfully come back on" + + " after clicking \"show in folder\" a 2nd time", + checkHighlightOn(testFolder)); + } + + @Test + @MediumTest + public void testShowInFolder_Scroll() throws Exception { + addFolder(TEST_FOLDER_TITLE); // Index 8 + addBookmark(TEST_TITLE_A, TEST_URL_A); + addBookmark(TEST_PAGE_TITLE_FOO, "http://foo.com"); + addFolder(TEST_PAGE_TITLE_GOOGLE2); + addFolder("B"); + addFolder("C"); + addFolder("D"); + addFolder("E"); // Index 1 + forceSyncHeaderState(); + openBookmarkManager(); + + // Enter search mode. + View searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id); + TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick); + CriteriaHelper.pollUiThread( + () -> mManager.getToolbarForTests().isSearching(), "Expected to enter search mode"); + TestThreadUtils.runOnUiThreadBlocking( + () -> mManager.onSearchTextChanged(TEST_FOLDER_TITLE)); + RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer); + + // This should be the only (& therefore 0-indexed) item. + View testFolderInSearch = mItemsContainer.findViewHolderForAdapterPosition(0).itemView; + View more = testFolderInSearch.findViewById(R.id.more); + Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE, + ((BookmarkFolderRow) testFolderInSearch).getTitle()); + + // Show in folder. + TestThreadUtils.runOnUiThreadBlocking(more::performClick); + onView(withText("Show in folder")).perform(click()); + + // This should be in the 8th position now. + ViewHolder testFolderInList = mItemsContainer.findViewHolderForAdapterPosition(8); + Assert.assertFalse( + "Expected list to scroll bookmark item into view", testFolderInList == null); + Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE, + ((BookmarkFolderRow) testFolderInList.itemView).getTitle()); + Assert.assertTrue("Expected bookmark item to be highlighted after scrolling to it.", + checkHighlightOn(testFolderInList.itemView)); + } + + @Test + @MediumTest + public void testShowInFolder_OpenOtherFolder() throws Exception { + BookmarkId testId = addFolder(TEST_FOLDER_TITLE); + TestThreadUtils.runOnUiThreadBlocking( + () -> mBookmarkModel.addBookmark(testId, 0, TEST_TITLE_A, TEST_URL_A)); + forceSyncHeaderState(); + openBookmarkManager(); + + // Enter search mode. + View searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id); + TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick); + CriteriaHelper.pollUiThread( + () -> mManager.getToolbarForTests().isSearching(), "Expected to enter search mode"); + TestThreadUtils.runOnUiThreadBlocking(() -> mManager.onSearchTextChanged(TEST_URL_A)); + RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer); + + // This should be the only (& therefore 0-indexed) item. + View itemAInSearch = mItemsContainer.findViewHolderForAdapterPosition(0).itemView; + View more = itemAInSearch.findViewById(R.id.more); + Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A, + ((BookmarkItemRow) itemAInSearch).getTitle()); + + // Show in folder. + TestThreadUtils.runOnUiThreadBlocking(more::performClick); + onView(withText("Show in folder")).perform(click()); + + // Make sure that we're in the right folder (index 1 because of promo). + View itemA = mItemsContainer.findViewHolderForAdapterPosition(1).itemView; + Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A, + ((BookmarkItemRow) itemA).getTitle()); + Assert.assertTrue( + "Expected bookmark item to be highlighted after opening it in new folder.", + checkHighlightOn(itemA)); + + // Open mobile bookmarks folder, then go back to the subfolder. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mManager.openFolder(mBookmarkModel.getMobileFolderId()); + mManager.openFolder(testId); + }); + RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer); + + itemA = mItemsContainer.findViewHolderForAdapterPosition(1).itemView; + Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A, + ((BookmarkItemRow) itemA).getTitle()); + Assert.assertTrue("Expected bookmark item to not be highlighted after " + + "exiting and re-entering folder.", + checkHighlightOff(itemA)); + } + @Override protected void openBookmarkManager() throws InterruptedException { super.openBookmarkManager();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedConfigurationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedConfigurationTest.java index 0e25918..4161ee5f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedConfigurationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedConfigurationTest.java
@@ -46,6 +46,8 @@ FeedConfiguration.FEED_UI_ENABLED_DEFAULT, FeedConfiguration.getFeedUiEnabled()); Assert.assertEquals(FeedConfiguration.INITIAL_NON_CACHED_PAGE_SIZE_DEFAULT, FeedConfiguration.getInitialNonCachedPageSize()); + Assert.assertEquals(FeedConfiguration.LIMIT_PAGE_UPDATES_IN_HEAD_DEFAULT, + FeedConfiguration.getLimitPageUpdatesInHead()); Assert.assertEquals(FeedConfiguration.LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS_DEFAULT, FeedConfiguration.getLoggingImmediateContentThresholdMs()); Assert.assertEquals(FeedConfiguration.MANAGE_INTERESTS_ENABLED_DEFAULT, @@ -142,6 +144,16 @@ @Feature({"Feed"}) @CommandLineFlags. Add({"enable-features=InterestFeedContentSuggestions<Trial", "force-fieldtrials=Trial/Group", + "force-fieldtrial-params=Trial.Group:limit_page_updates_in_head/true"}) + public void + testLimitPageUpdatesInHead() { + Assert.assertTrue(FeedConfiguration.getLimitPageUpdatesInHead()); + } + + @Test + @Feature({"Feed"}) + @CommandLineFlags. + Add({"enable-features=InterestFeedContentSuggestions<Trial", "force-fieldtrials=Trial/Group", "force-fieldtrial-params=Trial.Group:logging_immediate_content_threshold_ms/5000"}) public void testLoggingImmediateContentThresholdMs() { @@ -294,6 +306,8 @@ Assert.assertFalse(configuration.getValueOrDefault(ConfigKey.FEED_UI_ENABLED, true)); Assert.assertEquals((long) FeedConfiguration.INITIAL_NON_CACHED_PAGE_SIZE_DEFAULT, configuration.getValueOrDefault(ConfigKey.INITIAL_NON_CACHED_PAGE_SIZE, 0)); + Assert.assertFalse( + configuration.getValueOrDefault(ConfigKey.LIMIT_PAGE_UPDATES_IN_HEAD, true)); Assert.assertEquals((long) FeedConfiguration.LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS_DEFAULT, configuration.getValueOrDefault( ConfigKey.LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS, 0l));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sms/SmsReceiverDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sms/SmsReceiverDialogTest.java index dcf7d5e..9233568 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/sms/SmsReceiverDialogTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sms/SmsReceiverDialogTest.java
@@ -27,6 +27,7 @@ import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.content_public.browser.sms.Event; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestTouchUtils; import org.chromium.ui.base.ActivityWindowAndroid; @@ -49,16 +50,24 @@ final private CallbackHelper mCancelButtonClickedCallback = new CallbackHelper(); final private CallbackHelper mConfirmButtonClickedCallback = new CallbackHelper(); + final private CallbackHelper mTryAgainButtonClickedCallback = new CallbackHelper(); private class TestSmsReceiverDialogJni implements SmsReceiverDialog.Natives { @Override - public void onCancel(long nativeSmsDialogAndroid) { - mCancelButtonClickedCallback.notifyCalled(); - } - - @Override - public void onConfirm(long nativeSmsDialogAndroid) { - mConfirmButtonClickedCallback.notifyCalled(); + public void onEvent(long nativeSmsDialogAndroid, int eventType) { + switch (eventType) { + case Event.CANCEL: + mCancelButtonClickedCallback.notifyCalled(); + return; + case Event.CONFIRM: + mConfirmButtonClickedCallback.notifyCalled(); + return; + case Event.TIMEOUT: + mTryAgainButtonClickedCallback.notifyCalled(); + return; + default: + assert false : "|eventType| is invalid"; + } } } @@ -81,45 +90,109 @@ @Test @LargeTest - public void testCancelButtonAndConfirmButton() { + public void testSmsCancel() throws Throwable { Dialog dialog = mSmsDialog.getDialogForTesting(); Button cancelButton = (Button) dialog.findViewById(R.id.cancel_button); Assert.assertTrue(cancelButton.isEnabled()); - Button confirmButton = (Button) dialog.findViewById(R.id.confirm_button); - Assert.assertFalse(confirmButton.isEnabled()); - - TestThreadUtils.runOnUiThreadBlocking(mSmsDialog::smsReceived); - Assert.assertTrue(confirmButton.isEnabled()); - } - - @Test - @LargeTest - public void testClickCancelButton() throws Throwable { - Dialog dialog = mSmsDialog.getDialogForTesting(); - Button cancelButton = (Button) dialog.findViewById(R.id.cancel_button); - + // Simulates the user clicking the "Cancel" button. TestTouchUtils.performClickOnMainSync( InstrumentationRegistry.getInstrumentation(), cancelButton); - mCancelButtonClickedCallback.waitForCallback(0, 1); } @Test @LargeTest - public void testClickConfirmButton() throws Throwable { + public void testSmsReceivedUserClickingCancelButton() throws Throwable { Dialog dialog = mSmsDialog.getDialogForTesting(); - Button confirmButton = (Button) dialog.findViewById(R.id.confirm_button); + ProgressBar progressBar = (ProgressBar) dialog.findViewById(R.id.progress); + ImageView doneIcon = (ImageView) dialog.findViewById(R.id.done_icon); + ImageView errorIcon = (ImageView) dialog.findViewById(R.id.error_icon); + TextView status = (TextView) dialog.findViewById(R.id.status); + Button cancelButton = (Button) dialog.findViewById(R.id.cancel_button); + Button confirmButton = (Button) dialog.findViewById(R.id.confirm_or_try_again_button); + + Assert.assertEquals(View.VISIBLE, progressBar.getVisibility()); + Assert.assertEquals(View.GONE, doneIcon.getVisibility()); + Assert.assertEquals(View.GONE, errorIcon.getVisibility()); + Assert.assertEquals( + mActivityTestRule.getActivity().getString(R.string.sms_dialog_status_waiting), + status.getText().toString()); + Assert.assertTrue(cancelButton.isEnabled()); + Assert.assertEquals(mActivityTestRule.getActivity().getString(R.string.confirm), + confirmButton.getText().toString()); + Assert.assertFalse(confirmButton.isEnabled()); + + // Simulates the SMS being received. + TestThreadUtils.runOnUiThreadBlocking(mSmsDialog::smsReceived); + + Assert.assertEquals(View.GONE, progressBar.getVisibility()); + Assert.assertEquals(View.VISIBLE, doneIcon.getVisibility()); + Assert.assertEquals(View.GONE, errorIcon.getVisibility()); + Assert.assertEquals( + mActivityTestRule.getActivity().getString(R.string.sms_dialog_status_sms_received), + status.getText().toString()); + Assert.assertTrue(cancelButton.isEnabled()); + Assert.assertTrue(confirmButton.isEnabled()); + + // Simulates the user clicking the "Cancel" button. + TestTouchUtils.performClickOnMainSync( + InstrumentationRegistry.getInstrumentation(), cancelButton); + mCancelButtonClickedCallback.waitForCallback(0, 1); + } + + @Test + @LargeTest + public void testSmsReceivedUserClickingConfirmButton() throws Throwable { + Dialog dialog = mSmsDialog.getDialogForTesting(); + + Button confirmButton = (Button) dialog.findViewById(R.id.confirm_or_try_again_button); + + // Simulates the SMS being received. + TestThreadUtils.runOnUiThreadBlocking(mSmsDialog::smsReceived); + + // Simulates the user clicking the "Confirm" button. TestTouchUtils.performClickOnMainSync( InstrumentationRegistry.getInstrumentation(), confirmButton); - mConfirmButtonClickedCallback.waitForCallback(0, 1); } @Test @LargeTest + public void testSmsTimeout() throws Throwable { + Dialog dialog = mSmsDialog.getDialogForTesting(); + + ProgressBar progressBar = (ProgressBar) dialog.findViewById(R.id.progress); + ImageView doneIcon = (ImageView) dialog.findViewById(R.id.done_icon); + ImageView errorIcon = (ImageView) dialog.findViewById(R.id.error_icon); + TextView status = (TextView) dialog.findViewById(R.id.status); + Button cancelButton = (Button) dialog.findViewById(R.id.cancel_button); + Button tryAgainButton = (Button) dialog.findViewById(R.id.confirm_or_try_again_button); + + // Simulates receiving the SMS having timed out. + TestThreadUtils.runOnUiThreadBlocking(mSmsDialog::smsTimeout); + + Assert.assertEquals(View.GONE, progressBar.getVisibility()); + Assert.assertEquals(View.GONE, doneIcon.getVisibility()); + Assert.assertEquals(View.VISIBLE, errorIcon.getVisibility()); + Assert.assertEquals( + mActivityTestRule.getActivity().getString(R.string.sms_dialog_status_timeout), + status.getText().toString()); + Assert.assertEquals(View.GONE, cancelButton.getVisibility()); + Assert.assertEquals(mActivityTestRule.getActivity().getString(R.string.try_again), + tryAgainButton.getText().toString()); + Assert.assertTrue(tryAgainButton.isEnabled()); + + // Simulates the user clicking the "Try again" button. + TestTouchUtils.performClickOnMainSync( + InstrumentationRegistry.getInstrumentation(), tryAgainButton); + mTryAgainButtonClickedCallback.waitForCallback(0, 1); + } + + @Test + @LargeTest public void testStatusRowChangesWhenMessageReceived() { Dialog dialog = mSmsDialog.getDialogForTesting();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorObserverTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorObserverTestRule.java index 0166839..93123279 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorObserverTestRule.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorObserverTestRule.java
@@ -75,7 +75,7 @@ } }; - TabModelOrderController orderController = new TabModelOrderController(mSelector); + TabModelOrderController orderController = new TabModelOrderControllerImpl(mSelector); TabContentManager tabContentManager = new TabContentManager(InstrumentationRegistry.getTargetContext(), null, false); TabPersistencePolicy persistencePolicy = new TabbedModeTabPersistencePolicy(0, false);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java index bc35eb8..9ebf3a24 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
@@ -179,7 +179,7 @@ mTabCreatorManager, mTabPersistentStoreObserver); } }); - mTabModelOrderController = new TabModelOrderController(this); + mTabModelOrderController = new TabModelOrderControllerImpl(this); Callable<TabModelImpl> callable = new Callable<TabModelImpl>() { @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkActivityTest.java new file mode 100644 index 0000000..51b00d80 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkActivityTest.java
@@ -0,0 +1,257 @@ +// 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.webapps; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Build; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.ContextUtils; +import org.chromium.base.metrics.RecordHistogram; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Feature; +import org.chromium.blink_public.platform.WebDisplayMode; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.ChromeSwitches; +import org.chromium.chrome.browser.ChromeTabbedActivity; +import org.chromium.chrome.browser.DeferredStartupHandler; +import org.chromium.chrome.browser.ShortcutHelper; +import org.chromium.chrome.browser.ShortcutSource; +import org.chromium.chrome.browser.customtabs.CustomTabActivity; +import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; +import org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroid; +import org.chromium.chrome.browser.touchless.TouchlessDelegate; +import org.chromium.chrome.browser.util.FeatureUtilities; +import org.chromium.chrome.browser.util.IntentUtils; +import org.chromium.chrome.test.ChromeActivityTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.util.ChromeTabUtils; +import org.chromium.content_public.browser.test.NativeLibraryTestRule; +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.JavaScriptUtils; +import org.chromium.content_public.common.ScreenOrientationValues; +import org.chromium.webapk.lib.common.WebApkConstants; + +/** Tests for WebApkActivity. */ +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public final class WebApkActivityTest { + private static final String TEST_WEBAPK_PACKAGE_NAME = "org.chromium.webapk.for.testing"; + private static final String TEST_WEBAPK_ID = + WebApkConstants.WEBAPK_ID_PREFIX + TEST_WEBAPK_PACKAGE_NAME; + + public final WebApkActivityTestRule mActivityTestRule = new WebApkActivityTestRule(); + public final NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule(); + + @Before + public void setUp() { + WebApkUpdateManager.setUpdatesEnabledForTesting(false); + mActivityTestRule.getEmbeddedTestServerRule().setServerUsesHttps(true); + + // WebAPK is not installed. Ensure that WebappRegistry#unregisterOldWebapps() does not + // delete the WebAPK's shared preferences. + SharedPreferences sharedPrefs = ContextUtils.getApplicationContext().getSharedPreferences( + WebappRegistry.REGISTRY_FILE_NAME, Context.MODE_PRIVATE); + sharedPrefs.edit() + .putLong(WebappRegistry.KEY_LAST_CLEANUP, System.currentTimeMillis()) + .apply(); + } + + /** + * Test that navigating a WebAPK to a URL which is outside of the WebAPK's scope shows the + * toolbar. + */ + @Test + @LargeTest + @Feature({"WebApk"}) + public void testLaunchAndNavigateOutsideScope() throws Exception { + WebApkActivity webApkActivity = mActivityTestRule.startWebApkActivity(createWebApkInfo( + getTestServerUrl("scope_a/page_1.html"), getTestServerUrl("scope_a/"))); + WebappActivityTestRule.assertToolbarShowState(webApkActivity, false); + + // We navigate outside scope and expect CCT toolbar to show on top of WebApkActivity. + String outOfScopeUrl = getTestServerUrl("manifest_test_page.html"); + mActivityTestRule.runJavaScriptCodeInCurrentTab( + "window.top.location = '" + outOfScopeUrl + "'"); + + ChromeTabUtils.waitForTabPageLoaded(webApkActivity.getActivityTab(), outOfScopeUrl); + WebappActivityTestRule.assertToolbarShowState(webApkActivity, true); + } + + /** + * Test launching a WebAPK. Test that opening a url within scope through window.open() will open + * a CCT. + */ + @Test + @LargeTest + @Feature({"WebApk"}) + public void testLaunchAndOpenNewWindowInScope() throws Exception { + String scopeUrl = getTestServerUrl("scope_a/"); + String inScopeUrl = getTestServerUrl("scope_a/page_1.html"); + WebApkActivity webApkActivity = + mActivityTestRule.startWebApkActivity(createWebApkInfo(inScopeUrl, scopeUrl)); + + WebappActivityTestRule.jsWindowOpen(mActivityTestRule.getActivity(), inScopeUrl); + + CustomTabActivity customTabActivity = + ChromeActivityTestRule.waitFor(CustomTabActivity.class); + ChromeTabUtils.waitForTabPageLoaded(customTabActivity.getActivityTab(), inScopeUrl); + Assert.assertTrue( + "Sending to external handlers needs to be enabled for redirect back (e.g. OAuth).", + IntentUtils.safeGetBooleanExtra(customTabActivity.getIntent(), + CustomTabIntentDataProvider.EXTRA_SEND_TO_EXTERNAL_DEFAULT_HANDLER, false)); + } + + /** + * Test launching a WebAPK. Test that opening a url off scope through window.open() will open a + * CCT, and in scope urls will stay in the CCT. + */ + @Test + @LargeTest + @Feature({"WebApk"}) + public void testLaunchAndNavigationInNewWindowOffandInScope() throws Exception { + String scopeUrl = getTestServerUrl("scope_a/"); + String inScopeUrl = getTestServerUrl("scope_a/page_1.html"); + String offScopeUrl = getTestServerUrl("scope_b/scope_b.html"); + WebApkActivity webApkActivity = + mActivityTestRule.startWebApkActivity(createWebApkInfo(inScopeUrl, scopeUrl)); + + WebappActivityTestRule.jsWindowOpen(mActivityTestRule.getActivity(), offScopeUrl); + CustomTabActivity customTabActivity = + ChromeActivityTestRule.waitFor(CustomTabActivity.class); + ChromeTabUtils.waitForTabPageLoaded(customTabActivity.getActivityTab(), offScopeUrl); + + JavaScriptUtils.executeJavaScriptAndWaitForResult( + customTabActivity.getActivityTab().getWebContents(), + String.format("window.location.href='%s'", inScopeUrl)); + ChromeTabUtils.waitForTabPageLoaded(customTabActivity.getActivityTab(), inScopeUrl); + } + + /** + * Test that on first launch: + * - the "WebApk.LaunchInterval" histogram is not recorded (because there is no previous launch + * to compute the interval from). + * - the "last used" time is updated (to compute future "launch intervals"). + */ + @Test + @LargeTest + @Feature({"WebApk"}) + public void testLaunchIntervalHistogramNotRecordedOnFirstLaunch() throws Exception { + android.util.Log.e("ABCD", "Start"); + final String histogramName = "WebApk.LaunchInterval"; + WebApkActivity webApkActivity = mActivityTestRule.startWebApkActivity(createWebApkInfo( + getTestServerUrl("manifest_test_page.html"), getTestServerUrl("/"))); + + CriteriaHelper.pollUiThread(new Criteria("Deferred startup never completed") { + @Override + public boolean isSatisfied() { + return DeferredStartupHandler.getInstance().isDeferredStartupCompleteForApp() + && WebappRegistry.getInstance().getWebappDataStorage(TEST_WEBAPK_ID) + != null; + } + }); + Assert.assertEquals(0, RecordHistogram.getHistogramTotalCountForTesting(histogramName)); + WebappDataStorage storage = + WebappRegistry.getInstance().getWebappDataStorage(TEST_WEBAPK_ID); + Assert.assertNotEquals(WebappDataStorage.TIMESTAMP_INVALID, storage.getLastUsedTimeMs()); + android.util.Log.e("ABCD", "Start2"); + } + + /** Test that the "WebApk.LaunchInterval" histogram is recorded on susbequent launches. */ + @Test + @LargeTest + @Feature({"WebApk"}) + public void testLaunchIntervalHistogramRecordedOnSecondLaunch() throws Exception { + mNativeLibraryTestRule.loadNativeLibraryNoBrowserProcess(); + + final String histogramName = "WebApk.LaunchInterval2"; + final String packageName = "org.chromium.webapk.test"; + + WebappDataStorage storage = registerWithStorage(TEST_WEBAPK_ID); + storage.setHasBeenLaunched(); + storage.updateLastUsedTime(); + Assert.assertEquals(0, RecordHistogram.getHistogramTotalCountForTesting(histogramName)); + + WebApkActivity webApkActivity = mActivityTestRule.startWebApkActivity(createWebApkInfo( + getTestServerUrl("manifest_test_page.html"), getTestServerUrl("/"))); + + CriteriaHelper.pollUiThread(new Criteria("Deferred startup never completed") { + @Override + public boolean isSatisfied() { + return DeferredStartupHandler.getInstance().isDeferredStartupCompleteForApp(); + } + }); + + Assert.assertEquals(1, RecordHistogram.getHistogramTotalCountForTesting(histogramName)); + } + + /** + * Test the L+ logic in {@link TabWebContentsDelegateAndroid#activateContents} for bringing + * WebAPK to the foreground. + */ + @LargeTest + @Test + public void testActivateWebApkLPlus() throws Exception { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; + + // Launch WebAPK. + WebApkActivity webApkActivity = mActivityTestRule.startWebApkActivity(createWebApkInfo( + getTestServerUrl("manifest_test_page.html"), getTestServerUrl("/"))); + + Class<? extends ChromeActivity> mainClass = FeatureUtilities.isNoTouchModeEnabled() + ? TouchlessDelegate.getNoTouchActivityClass() + : ChromeTabbedActivity.class; + + // Move WebAPK to the background by launching Chrome. + Intent intent = new Intent(InstrumentationRegistry.getTargetContext(), mainClass); + intent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | ApiCompatibilityUtils.getActivityNewDocumentFlag()); + InstrumentationRegistry.getTargetContext().startActivity(intent); + ChromeActivityTestRule.waitFor(mainClass); + + TabWebContentsDelegateAndroid tabDelegate = + webApkActivity.getActivityTab().getTabWebContentsDelegateAndroid(); + tabDelegate.activateContents(); + + // WebApkActivity should have been brought back to the foreground. + ChromeActivityTestRule.waitFor(WebApkActivity.class); + } + + private WebApkInfo createWebApkInfo(String startUrl, String scopeUrl) { + return WebApkInfo.create(TEST_WEBAPK_ID, startUrl, scopeUrl, null /* primaryIcon */, + null /* badgeIcon */, null /* splashIcon */, "" /* name */, "" /* short_name */, + WebDisplayMode.STANDALONE, ScreenOrientationValues.DEFAULT, ShortcutSource.UNKNOWN, + ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING /* themeColor */, + ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING /* backgroundColor */, + 0 /* defaultBackgroundColor */, false /* isPrimaryIconMaskable */, + TEST_WEBAPK_PACKAGE_NAME, 10 /* packageVersion */, "" /* manifestURL */, + "" /* manifestStartURL */, WebApkInfo.WebApkDistributor.BROWSER, null, null, + null /*shareTargetActivityName*/, false /* forceNavigation */, + false /* isSplashProvidedByWebApk */, null /* shareData */, 1 /* apkVersionCode */); + } + + private String getTestServerUrl(String relativeUrl) { + return mActivityTestRule.getEmbeddedTestServerRule().getServer().getURL( + "/chrome/test/data/banners/" + relativeUrl); + } + + /** Register WebAPK with WebappDataStorage */ + private WebappDataStorage registerWithStorage(final String webappId) throws Exception { + TestFetchStorageCallback callback = new TestFetchStorageCallback(); + WebappRegistry.getInstance().register(webappId, callback); + callback.waitForCallback(0); + return WebappRegistry.getInstance().getWebappDataStorage(webappId); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkActivityTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkActivityTestRule.java new file mode 100644 index 0000000..77703d1 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkActivityTestRule.java
@@ -0,0 +1,71 @@ +// 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.webapps; + +import android.content.Intent; +import android.support.test.InstrumentationRegistry; + +import org.junit.Assert; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.test.util.ScalableTimeout; +import org.chromium.chrome.browser.ShortcutHelper; +import org.chromium.chrome.test.ChromeActivityTestRule; +import org.chromium.chrome.test.util.ChromeTabUtils; +import org.chromium.content_public.browser.test.util.Criteria; +import org.chromium.content_public.browser.test.util.CriteriaHelper; +import org.chromium.webapk.lib.common.WebApkConstants; + +/** Custom {@link ChromeActivityTestRule} for tests using {@link WebApkActivity}. */ +public class WebApkActivityTestRule extends ChromeActivityTestRule<WebApkActivity> { + /** Time in milliseconds to wait for page to be loaded. */ + private static final long STARTUP_TIMEOUT = ScalableTimeout.scaleTimeout(10000); + + public WebApkActivityTestRule() { + super(WebApkActivity.class); + } + + /** + * Launches WebApkActivity and waits for the page to have finished loading and for the splash + * screen to be hidden. + */ + public WebApkActivity startWebApkActivity(WebApkInfo webApkInfo) throws InterruptedException { + Intent intent = createIntent(webApkInfo); + + WebappActivity.addWebappInfo(webApkInfo.id(), webApkInfo); + final WebApkActivity webApkActivity = + (WebApkActivity) InstrumentationRegistry.getInstrumentation().startActivitySync( + intent); + setActivity(webApkActivity); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + CriteriaHelper.pollInstrumentationThread(new Criteria() { + @Override + public boolean isSatisfied() { + return webApkActivity.getActivityTab() != null; + } + }, STARTUP_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL); + + ChromeTabUtils.waitForTabPageLoaded( + webApkActivity.getActivityTab(), webApkInfo.uri().toString()); + WebappActivityTestRule.waitUntilSplashHides(webApkActivity); + + // Launching the WebAPK should have popped the WebApkInfo. + Assert.assertNull(WebappActivity.popWebappInfo(webApkInfo.id())); + + return webApkActivity; + } + + private Intent createIntent(WebApkInfo webApkInfo) { + Intent intent = + new Intent(InstrumentationRegistry.getTargetContext(), WebApkActivity0.class); + intent.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, webApkInfo.webApkPackageName()); + intent.putExtra(ShortcutHelper.EXTRA_ID, webApkInfo.id()); + intent.putExtra(ShortcutHelper.EXTRA_URL, webApkInfo.uri().toString()); + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | ApiCompatibilityUtils.getActivityNewDocumentFlag()); + return intent; + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java index b757859..cbc1dec 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java
@@ -16,31 +16,14 @@ import org.junit.rules.RuleChain; import org.junit.runner.RunWith; -import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.CommandLine; -import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Feature; -import org.chromium.base.test.util.ScalableTimeout; -import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.ChromeTabbedActivity; -import org.chromium.chrome.browser.DeferredStartupHandler; -import org.chromium.chrome.browser.ShortcutHelper; -import org.chromium.chrome.browser.customtabs.CustomTabActivity; -import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; -import org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroid; import org.chromium.chrome.browser.test.MockCertVerifierRuleAndroid; -import org.chromium.chrome.browser.touchless.TouchlessDelegate; -import org.chromium.chrome.browser.util.FeatureUtilities; -import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.chrome.test.util.ChromeTabUtils; import org.chromium.content_public.browser.test.NativeLibraryTestRule; -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.JavaScriptUtils; import org.chromium.content_public.common.ContentSwitches; import org.chromium.webapk.lib.client.WebApkValidator; import org.chromium.webapk.lib.common.WebApkConstants; @@ -63,54 +46,6 @@ .around(mNativeLibraryTestRule) .around(mCertVerifierRule); - private static final long STARTUP_TIMEOUT = ScalableTimeout.scaleTimeout(10000); - - public void startWebApkActivity(String webApkPackageName, final String startUrl) - throws InterruptedException { - Intent intent = - new Intent(InstrumentationRegistry.getTargetContext(), WebApkActivity0.class); - intent.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, webApkPackageName); - intent.putExtra(ShortcutHelper.EXTRA_URL, startUrl); - intent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | ApiCompatibilityUtils.getActivityNewDocumentFlag()); - - WebApkActivity webApkActivity = - (WebApkActivity) InstrumentationRegistry.getInstrumentation().startActivitySync( - intent); - mActivityTestRule.setActivity(webApkActivity); - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - - CriteriaHelper.pollInstrumentationThread(new Criteria() { - @Override - public boolean isSatisfied() { - return mActivityTestRule.getActivity().getActivityTab() != null; - } - }, STARTUP_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL); - - ChromeTabUtils.waitForTabPageLoaded( - mActivityTestRule.getActivity().getActivityTab(), startUrl); - } - - /** Waits for the splash screen to be hidden. */ - public void waitUntilSplashscreenHides() { - CriteriaHelper.pollInstrumentationThread(new Criteria() { - @Override - public boolean isSatisfied() { - return mActivityTestRule.getActivity() - .getSplashControllerForTests() - .wasSplashScreenHiddenForTests(); - } - }); - } - - /** Register WebAPK with WebappDataStorage */ - private WebappDataStorage registerWithStorage(final String webappId) throws Exception { - TestFetchStorageCallback callback = new TestFetchStorageCallback(); - WebappRegistry.getInstance().register(webappId, callback); - callback.waitForCallback(0); - return WebappRegistry.getInstance().getWebappDataStorage(webappId); - } - /** Returns URL for the passed-in host which maps to a page on the EmbeddedTestServer. */ private String getUrlForHost(String host) { return "https://" + host + "/defaultresponse"; @@ -148,158 +83,4 @@ WebApkActivity lastActivity = mActivityTestRule.getActivity(); Assert.assertEquals(pwaRocksUrl, lastActivity.getWebappInfo().uri().toString()); } - - /** - * Test launching a WebAPK. Test that loading the start page works and that the splashscreen - * eventually hides. - */ - @Test - @LargeTest - @Feature({"WebApk"}) - public void testLaunchAndNavigateOffOrigin() throws Exception { - startWebApkActivity("org.chromium.webapk.test", getUrlForHost("pwa.rocks")); - waitUntilSplashscreenHides(); - WebApkActivity webApkActivity = mActivityTestRule.getActivity(); - WebappActivityTestRule.assertToolbarShowState(webApkActivity, false); - - // We navigate outside origin and expect CCT toolbar to show on top of WebApkActivity. - String googleUrl = getUrlForHost("www.google.com"); - mActivityTestRule.runJavaScriptCodeInCurrentTab( - "window.top.location = '" + googleUrl + "'"); - - ChromeTabUtils.waitForTabPageLoaded(webApkActivity.getActivityTab(), googleUrl); - WebappActivityTestRule.assertToolbarShowState(webApkActivity, true); - } - - /** - * Test launching a WebAPK. Test that open a url within scope through window.open() will open a - * CCT. - */ - @Test - @LargeTest - @Feature({"WebApk"}) - public void testLaunchAndOpenNewWindowInOrigin() throws Exception { - String pwaRocksUrl = getUrlForHost("pwa.rocks"); - startWebApkActivity("org.chromium.webapk.test", pwaRocksUrl); - waitUntilSplashscreenHides(); - - WebappActivityTestRule.jsWindowOpen(mActivityTestRule.getActivity(), pwaRocksUrl); - - CustomTabActivity customTabActivity = - ChromeActivityTestRule.waitFor(CustomTabActivity.class); - ChromeTabUtils.waitForTabPageLoaded(customTabActivity.getActivityTab(), pwaRocksUrl); - Assert.assertTrue( - "Sending to external handlers needs to be enabled for redirect back (e.g. OAuth).", - IntentUtils.safeGetBooleanExtra(customTabActivity.getIntent(), - CustomTabIntentDataProvider.EXTRA_SEND_TO_EXTERNAL_DEFAULT_HANDLER, false)); - } - - /** - * Test launching a WebAPK. Test that open a url off scope through window.open() will open a - * CCT, and in scope urls will stay in the CCT. - */ - @Test - @LargeTest - @Feature({"WebApk"}) - public void testLaunchAndNavigationInNewWindowOffandInOrigin() throws Exception { - String pwaRocksUrl = getUrlForHost("pwa.rocks"); - String googleUrl = getUrlForHost("www.google.com"); - startWebApkActivity("org.chromium.webapk.test", pwaRocksUrl); - waitUntilSplashscreenHides(); - - WebappActivityTestRule.jsWindowOpen(mActivityTestRule.getActivity(), googleUrl); - CustomTabActivity customTabActivity = - ChromeActivityTestRule.waitFor(CustomTabActivity.class); - ChromeTabUtils.waitForTabPageLoaded(customTabActivity.getActivityTab(), googleUrl); - - JavaScriptUtils.executeJavaScriptAndWaitForResult( - customTabActivity.getActivityTab().getWebContents(), - String.format("window.location.href='%s'", pwaRocksUrl)); - ChromeTabUtils.waitForTabPageLoaded(customTabActivity.getActivityTab(), pwaRocksUrl); - } - - /** - * Test that on first launch: - * - the "WebApk.LaunchInterval" histogram is not recorded (because there is no prevous launch - * to compute the interval from). - * - the "last used" time is updated (to compute future "launch intervals"). - */ - @Test - @LargeTest - @Feature({"WebApk"}) - public void testLaunchIntervalHistogramNotRecordedOnFirstLaunch() throws Exception { - final String histogramName = "WebApk.LaunchInterval"; - final String packageName = "org.chromium.webapk.test"; - startWebApkActivity(packageName, getUrlForHost("pwa.rocks")); - - CriteriaHelper.pollUiThread(new Criteria("Deferred startup never completed") { - @Override - public boolean isSatisfied() { - return DeferredStartupHandler.getInstance().isDeferredStartupCompleteForApp(); - } - }); - Assert.assertEquals(0, RecordHistogram.getHistogramTotalCountForTesting(histogramName)); - WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage( - WebApkConstants.WEBAPK_ID_PREFIX + packageName); - Assert.assertNotEquals(WebappDataStorage.TIMESTAMP_INVALID, storage.getLastUsedTimeMs()); - } - - /** Test that the "WebApk.LaunchInterval" histogram is recorded on susbequent launches. */ - @Test - @LargeTest - @Feature({"WebApk"}) - public void testLaunchIntervalHistogramRecordedOnSecondLaunch() throws Exception { - mNativeLibraryTestRule.loadNativeLibraryNoBrowserProcess(); - - final String histogramName = "WebApk.LaunchInterval2"; - final String packageName = "org.chromium.webapk.test"; - - WebappDataStorage storage = - registerWithStorage(WebApkConstants.WEBAPK_ID_PREFIX + packageName); - storage.setHasBeenLaunched(); - storage.updateLastUsedTime(); - Assert.assertEquals(0, RecordHistogram.getHistogramTotalCountForTesting(histogramName)); - - startWebApkActivity(packageName, getUrlForHost("pwa.rocks")); - - CriteriaHelper.pollUiThread(new Criteria("Deferred startup never completed") { - @Override - public boolean isSatisfied() { - return DeferredStartupHandler.getInstance().isDeferredStartupCompleteForApp(); - } - }); - - Assert.assertEquals(1, RecordHistogram.getHistogramTotalCountForTesting(histogramName)); - } - - /** - * Test that {@link TabWebContentsDelegateAndroid#activateContents} brings a WebAPK to the - * foreground. - */ - @LargeTest - @Test - public void testActivateWebApk() throws Exception { - // Launch WebAPK. - startWebApkActivity("org.chromium.webapk.test", getUrlForHost("pwa.rocks")); - waitUntilSplashscreenHides(); - - Class<? extends ChromeActivity> mainClass = FeatureUtilities.isNoTouchModeEnabled() - ? TouchlessDelegate.getNoTouchActivityClass() - : ChromeTabbedActivity.class; - - // Move WebAPK to the background by launching Chrome. - Intent intent = new Intent(InstrumentationRegistry.getTargetContext(), mainClass); - intent.setFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | ApiCompatibilityUtils.getActivityNewDocumentFlag()); - InstrumentationRegistry.getTargetContext().startActivity(intent); - ChromeActivityTestRule.waitFor(mainClass); - - WebApkActivity webApkActivity = mActivityTestRule.getActivity(); - TabWebContentsDelegateAndroid tabDelegate = - webApkActivity.getActivityTab().getTabWebContentsDelegateAndroid(); - tabDelegate.activateContents(); - - // WebApkActivity should have been brought back to the foreground. - ChromeActivityTestRule.waitFor(WebApkActivity.class); - } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java index 250f22b..a64b7a0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java
@@ -247,10 +247,14 @@ * Waits for the splash screen to be hidden. */ public void waitUntilSplashscreenHides() { + waitUntilSplashHides(getActivity()); + } + + public static void waitUntilSplashHides(WebappActivity activity) { CriteriaHelper.pollInstrumentationThread(new Criteria() { @Override public boolean isSatisfied() { - return !isSplashScreenVisible(); + return activity.getSplashControllerForTests().wasSplashScreenHiddenForTests(); } }, STARTUP_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ViewHighlighterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ViewHighlighterTest.java index 8c9c44c..c0d02ff 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ViewHighlighterTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ViewHighlighterTest.java
@@ -7,8 +7,6 @@ import android.content.Context; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.rule.UiThreadTestRule; @@ -22,6 +20,7 @@ import org.junit.runner.RunWith; import org.chromium.chrome.R; +import org.chromium.chrome.browser.ViewHighlighterTestUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; /** @@ -83,17 +82,21 @@ checkHighlightOn(tintedImageButton); } - private void checkHighlightOn(View view) { - Assert.assertTrue(view.getBackground() instanceof LayerDrawable); - LayerDrawable layerDrawable = (LayerDrawable) view.getBackground(); - Drawable drawable = layerDrawable.getDrawable(layerDrawable.getNumberOfLayers() - 1); - Assert.assertTrue(drawable instanceof PulseDrawable); - PulseDrawable pulse = (PulseDrawable) drawable; - Assert.assertTrue(pulse.isRunning()); - Assert.assertTrue(pulse.isVisible()); + /** + * Assert that the provided view is highlighted. + * + * @param view The view of interest. + */ + private static void checkHighlightOn(View view) { + Assert.assertTrue(ViewHighlighterTestUtils.checkHighlightOn(view)); } - private void checkHighlightOff(View view) { - Assert.assertFalse(view.getBackground() instanceof LayerDrawable); + /** + * Assert that the provided view is not highlighted. + * + * @param view The view of interest. + */ + private static void checkHighlightOff(View view) { + Assert.assertTrue(ViewHighlighterTestUtils.checkHighlightOff(view)); } -} +} \ No newline at end of file
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ShadowDeviceConditions.java b/chrome/android/junit/src/org/chromium/chrome/browser/ShadowDeviceConditions.java index 5ec6a380..c8ad496 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ShadowDeviceConditions.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ShadowDeviceConditions.java
@@ -59,4 +59,9 @@ public static boolean isCurrentActiveNetworkMetered(Context context) { return sDeviceConditions.isActiveNetworkMetered(); } + + @Implementation + public static boolean isCurrentlyScreenOnAndUnlocked(Context context) { + return sDeviceConditions.isScreenOnAndUnlocked(); + } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBackgroundTaskUnitTest.java index 504878b..038e68fe 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBackgroundTaskUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBackgroundTaskUnitTest.java
@@ -114,9 +114,10 @@ boolean powerSaveModeOn = true; int highBatteryLevel = 75; boolean metered = true; + boolean screenOnAndUnlocked = true; - DeviceConditions deviceConditions = new DeviceConditions( - !powerConnected, highBatteryLevel, connectionType, !powerSaveModeOn, !metered); + DeviceConditions deviceConditions = new DeviceConditions(!powerConnected, highBatteryLevel, + connectionType, !powerSaveModeOn, !metered, screenOnAndUnlocked); ShadowDeviceConditions.setCurrentConditions(deviceConditions); }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java index 539a5f07..4aabd85 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java
@@ -62,6 +62,7 @@ private static final boolean POWER_CONNECTED = true; private static final boolean POWER_SAVE_MODE_ON = true; private static final boolean METERED = true; + private static final boolean SCREEN_ON_AND_UNLOCKED = true; private static final int MINIMUM_BATTERY_LEVEL = 33; private static final String IS_LOW_END_DEVICE_SWITCH = "--" + BaseSwitches.ENABLE_LOW_END_DEVICE_MODE; @@ -74,7 +75,8 @@ private TriggerConditions mTriggerConditions = new TriggerConditions(!REQUIRE_POWER, MINIMUM_BATTERY_LEVEL, REQUIRE_UNMETERED); private DeviceConditions mDeviceConditions = new DeviceConditions(!POWER_CONNECTED, - MINIMUM_BATTERY_LEVEL + 5, ConnectionType.CONNECTION_3G, !POWER_SAVE_MODE_ON, !METERED); + MINIMUM_BATTERY_LEVEL + 5, ConnectionType.CONNECTION_3G, !POWER_SAVE_MODE_ON, !METERED, + SCREEN_ON_AND_UNLOCKED); private Activity mTestActivity; @Mock @@ -134,9 +136,9 @@ @Feature({"OfflinePages"}) public void testCheckConditions_BatteryConditions_LowBattery_NoPower() { // Setup low battery conditions with no power connected. - DeviceConditions deviceConditionsLowBattery = - new DeviceConditions(!POWER_CONNECTED, MINIMUM_BATTERY_LEVEL - 1, - ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + DeviceConditions deviceConditionsLowBattery = new DeviceConditions(!POWER_CONNECTED, + MINIMUM_BATTERY_LEVEL - 1, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, + !METERED, SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsLowBattery); // Verify that conditions for processing are not met. @@ -159,9 +161,9 @@ @Feature({"OfflinePages"}) public void testCheckConditions_BatteryConditions_LowBattery_WithPower() { // Set battery percentage below minimum level, but connect power. - DeviceConditions deviceConditionsPowerConnected = - new DeviceConditions(POWER_CONNECTED, MINIMUM_BATTERY_LEVEL - 1, - ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + DeviceConditions deviceConditionsPowerConnected = new DeviceConditions(POWER_CONNECTED, + MINIMUM_BATTERY_LEVEL - 1, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, + !METERED, SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsPowerConnected); // Now verify that same battery level, with power connected, will pass the conditions.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java index a19e6bb..1200d27e 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java
@@ -195,10 +195,10 @@ } private void setupDeviceOnlineStatus(boolean online) { - DeviceConditions deviceConditions = - new DeviceConditions(false /* POWER_CONNECTED */, 75 /* BATTERY_LEVEL */, - online ? ConnectionType.CONNECTION_WIFI : ConnectionType.CONNECTION_NONE, - false /* POWER_SAVE */, false /* metered */); + DeviceConditions deviceConditions = new DeviceConditions(false /* POWER_CONNECTED */, + 75 /* BATTERY_LEVEL */, + online ? ConnectionType.CONNECTION_WIFI : ConnectionType.CONNECTION_NONE, + false /* POWER_SAVE */, false /* metered */, true /* screenOnAndUnlocked */); ShadowDeviceConditions.setCurrentConditions(deviceConditions); }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java index 71f40f66..d61ec381 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java
@@ -90,6 +90,7 @@ public static final int HIGH_BATTERY_LEVEL = 75; public static final int LOW_BATTERY_LEVEL = 25; public static final boolean METERED = true; + public static final boolean SCREEN_ON_AND_UNLOCKED = true; @Spy private PrefetchBackgroundTask mPrefetchBackgroundTask = new PrefetchBackgroundTask(); @@ -193,9 +194,9 @@ TaskParameters.create(TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID).build(); // Setup battery conditions with no power connected. - DeviceConditions deviceConditions = - new DeviceConditions(!POWER_CONNECTED, HIGH_BATTERY_LEVEL - 1, - ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + DeviceConditions deviceConditions = new DeviceConditions(!POWER_CONNECTED, + HIGH_BATTERY_LEVEL - 1, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, + !METERED, SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditions); mPrefetchBackgroundTask.onStartTask(null, params, new TaskFinishedCallback() { @@ -225,7 +226,8 @@ // Setup battery conditions with no power connected. DeviceConditions deviceConditions = new DeviceConditions(!POWER_CONNECTED, - 0 /* battery level */, ConnectionType.CONNECTION_2G, !POWER_SAVE_MODE_ON, METERED); + 0 /* battery level */, ConnectionType.CONNECTION_2G, !POWER_SAVE_MODE_ON, METERED, + SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditions); mPrefetchBackgroundTask.onStartTask(null, params, new TaskFinishedCallback() { @@ -244,7 +246,8 @@ public void testBatteryLow() throws Exception { // Setup low battery conditions with no power connected. DeviceConditions deviceConditionsLowBattery = new DeviceConditions(!POWER_CONNECTED, - LOW_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, METERED); + LOW_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, METERED, + SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsLowBattery); // Check impact on starting before native loaded. @@ -265,7 +268,8 @@ public void testBatteryHigh() throws Exception { // Setup high battery conditions with no power connected. DeviceConditions deviceConditionsHighBattery = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED, + SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsHighBattery); // Check impact on starting before native loaded. @@ -286,7 +290,8 @@ public void testNoNetwork() throws Exception { // Setup no network conditions. DeviceConditions deviceConditionsNoNetwork = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_NONE, !POWER_SAVE_MODE_ON, !METERED); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_NONE, !POWER_SAVE_MODE_ON, !METERED, + SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsNoNetwork); // Check impact on starting before native loaded. @@ -310,9 +315,9 @@ @Test public void testNoNetworkLimitless() throws Exception { // Setup no network conditions. - DeviceConditions deviceConditionsNoNetwork = - new DeviceConditions(!POWER_CONNECTED, 0 /* battery level */, - ConnectionType.CONNECTION_NONE, !POWER_SAVE_MODE_ON, !METERED); + DeviceConditions deviceConditionsNoNetwork = new DeviceConditions(!POWER_CONNECTED, + 0 /* battery level */, ConnectionType.CONNECTION_NONE, !POWER_SAVE_MODE_ON, + !METERED, SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsNoNetwork); // Check impact on starting before native loaded. @@ -336,7 +341,8 @@ public void testUnmeteredWifiNetwork() throws Exception { // Setup unmetered wifi conditions. DeviceConditions deviceConditionsUnmeteredWifi = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED, + SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsUnmeteredWifi); // Check impact on starting before native loaded. @@ -357,7 +363,8 @@ public void testMeteredWifiNetwork() throws Exception { // Setup metered wifi conditions. DeviceConditions deviceConditionsMeteredWifi = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, METERED); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, METERED, + SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsMeteredWifi); // Check impact on starting before native loaded. @@ -378,7 +385,8 @@ public void test2GNetwork() throws Exception { // Setup metered 2g connection conditions. DeviceConditions deviceConditions2G = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_2G, !POWER_SAVE_MODE_ON, METERED); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_2G, !POWER_SAVE_MODE_ON, METERED, + SCREEN_ON_AND_UNLOCKED); // TODO(petewil): this test name and the condition below do not match. ShadowDeviceConditions.setCurrentConditions(deviceConditions2G); @@ -399,9 +407,9 @@ @Test public void testBluetoothNetwork() throws Exception { // Setup bluetooth connection conditions. - DeviceConditions deviceConditionsBluetooth = - new DeviceConditions(!POWER_CONNECTED, HIGH_BATTERY_LEVEL, - ConnectionType.CONNECTION_BLUETOOTH, !POWER_SAVE_MODE_ON, !METERED); + DeviceConditions deviceConditionsBluetooth = new DeviceConditions(!POWER_CONNECTED, + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_BLUETOOTH, !POWER_SAVE_MODE_ON, + !METERED, SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsBluetooth); // Check impact on starting before native loaded. @@ -425,9 +433,9 @@ TaskParameters.create(TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID).build(); // Conditions should be appropriate for running the task. - DeviceConditions deviceConditions = - new DeviceConditions(POWER_CONNECTED, HIGH_BATTERY_LEVEL - 1, - ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + DeviceConditions deviceConditions = new DeviceConditions(POWER_CONNECTED, + HIGH_BATTERY_LEVEL - 1, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, + !METERED, SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditions); mPrefetchBackgroundTask.onStartTask(null, params, new TaskFinishedCallback() { @@ -447,7 +455,8 @@ public void testPowerSaverOn() throws Exception { // Setup power save mode, battery is high, wifi, not plugged in. DeviceConditions deviceConditionsPowerSave = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, POWER_SAVE_MODE_ON, !METERED); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, POWER_SAVE_MODE_ON, !METERED, + SCREEN_ON_AND_UNLOCKED); ShadowDeviceConditions.setCurrentConditions(deviceConditionsPowerSave); // Check impact on starting before native loaded.
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java index 7edbdb21..09a75c7 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java
@@ -30,7 +30,6 @@ import org.chromium.chrome.browser.tab.TabDelegateFactory; import org.chromium.chrome.browser.tab.TabRedirectHandler; import org.chromium.chrome.browser.tab.TabState; -import org.chromium.chrome.browser.tabmodel.document.TabDelegate; import org.chromium.chrome.browser.util.UrlConstants; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.common.Referrer; @@ -145,6 +144,12 @@ // we need to clear it due to inactivity, we should do it before calling // super#initializeState. if (launchNtpDueToInactivity) resetSavedInstanceState(); + + ((TouchlessTabCreator) getTabCreator(false)) + .setTabModel(getTabModelSelector().getModel(false)); + ((TouchlessTabCreator) getTabCreator(true)) + .setTabModel(getTabModelSelector().getModel(true)); + super.initializeState(); // By this point if we were going to restore a URL from savedInstanceState we would already @@ -284,7 +289,7 @@ } @Override - protected TabDelegate createTabDelegate(boolean incognito) { - return new TouchlessTabDelegate(incognito); + protected TabCreator createTabCreator(boolean incognito) { + return new TouchlessTabCreator(this, getWindowAndroid(), incognito); } }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabCreator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabCreator.java new file mode 100644 index 0000000..3e9fff8 --- /dev/null +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabCreator.java
@@ -0,0 +1,119 @@ +// 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; + +import android.content.Intent; +import android.net.Uri; +import android.provider.Browser; + +import androidx.browser.customtabs.CustomTabsIntent; + +import org.chromium.base.ContextUtils; +import org.chromium.chrome.browser.IntentHandler; +import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; +import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.LaunchSourceType; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabIdManager; +import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager; +import org.chromium.chrome.browser.tabmodel.ChromeTabCreator; +import org.chromium.chrome.browser.tabmodel.TabLaunchType; +import org.chromium.chrome.browser.tabmodel.TabModel; +import org.chromium.chrome.browser.tabmodel.TabModelOrderController; +import org.chromium.chrome.browser.tabmodel.document.AsyncTabCreationParams; +import org.chromium.chrome.browser.tabmodel.document.TabDelegate; +import org.chromium.chrome.browser.util.UrlUtilities; +import org.chromium.content_public.browser.LoadUrlParams; +import org.chromium.content_public.browser.WebContents; +import org.chromium.ui.base.WindowAndroid; + +/** + * Creates Tabs for navigation originating from {@link NoTouchActivity}. + * + * This is the same as the parent class with exception of opening a Custom Tab instead of creating a + * new tab in Chrome for links that open in new tabs. + */ +public class TouchlessTabCreator extends ChromeTabCreator { + private final TabDelegate mAsyncTabDelegate; + private final TabModelOrderController mOrderController; + + public TouchlessTabCreator(NoTouchActivity activity, WindowAndroid window, boolean incognito) { + super(activity, window, incognito); + mAsyncTabDelegate = new TabDelegate(incognito) { + @Override + public void createNewTab( + AsyncTabCreationParams asyncParams, @TabLaunchType int type, int parentId) { + String url = asyncParams.getLoadUrlParams().getUrl(); + + int assignedTabId = TabIdManager.getInstance().generateValidId(Tab.INVALID_TAB_ID); + AsyncTabParamsManager.add(assignedTabId, asyncParams); + + Intent intent = new CustomTabsIntent.Builder().setShowTitle(true).build().intent; + intent.setData(Uri.parse(url)); + intent.putExtra( + CustomTabIntentDataProvider.EXTRA_SEND_TO_EXTERNAL_DEFAULT_HANDLER, true); + intent.putExtra(CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_CHROME, true); + intent.putExtra(CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_WEBAPK, false); + intent.putExtra(CustomTabIntentDataProvider.EXTRA_BROWSER_LAUNCH_SOURCE, + LaunchSourceType.OTHER); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, + ContextUtils.getApplicationContext().getPackageName()); + addAsyncTabExtras( + asyncParams, parentId, false /* isChromeUI */, assignedTabId, intent); + + IntentHandler.startActivityForTrustedIntent(intent); + } + }; + + mOrderController = new TabModelOrderController() { + @Override + public int determineInsertionIndex(int type, int position, Tab newTab) { + return 0; + } + + @Override + public int determineInsertionIndex(int type, Tab newTab) { + return 0; + } + + @Override + public boolean willOpenInForeground(int type, boolean isNewTabIncognito) { + return true; + } + }; + } + + @Override + public boolean createTabWithWebContents( + Tab parent, WebContents webContents, @TabLaunchType int type, String url) { + if (shouldRedirectToCCT(type, url)) { + return mAsyncTabDelegate.createTabWithWebContents(parent, webContents, type, url); + } + return super.createTabWithWebContents(parent, webContents, type, url); + } + + @Override + public Tab createNewTab( + LoadUrlParams loadUrlParams, @TabLaunchType int type, Tab parent, Intent intent) { + if (shouldRedirectToCCT(type, loadUrlParams.getUrl())) { + return mAsyncTabDelegate.createNewTab(loadUrlParams, type, parent); + } + return super.createNewTab(loadUrlParams, type, parent, intent); + } + + private boolean shouldRedirectToCCT(@TabLaunchType int type, String url) { + // Only link clicks should open in CCT, if the browser opens a tab for, say, intent + // handling, we should continue to open in NoTouchActivity. + // However, we should also handle intent URLs as normal, even if they open in a new window. + return isLinkClickLaunchType(type) && !UrlUtilities.validateIntentUrl(url); + } + + private boolean isLinkClickLaunchType(@TabLaunchType int type) { + return type == TabLaunchType.FROM_LINK || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND + || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND; + } + + /* package */ void setTabModel(TabModel model) { + setTabModel(model, mOrderController); + } +}
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java deleted file mode 100644 index 92a7084..0000000 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java +++ /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. -package org.chromium.chrome.browser.touchless; - -import android.content.Intent; -import android.net.Uri; -import android.provider.Browser; - -import org.chromium.base.ContextUtils; -import org.chromium.chrome.browser.IntentHandler; -import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; -import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.LaunchSourceType; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.browser.tab.TabIdManager; -import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager; -import org.chromium.chrome.browser.tabmodel.TabLaunchType; -import org.chromium.chrome.browser.tabmodel.document.AsyncTabCreationParams; -import org.chromium.chrome.browser.tabmodel.document.TabDelegate; -import org.chromium.chrome.browser.util.UrlUtilities; - -import androidx.browser.customtabs.CustomTabsIntent; - -/** - * Asynchronously creates Tabs for navigation originating from {@link NoTouchActivity}. - * - * This is the same as the parent class with exception of opening a Custom Tab instead of creating a - * new tab in Chrome. - */ -public class TouchlessTabDelegate extends TabDelegate { - public TouchlessTabDelegate(boolean incognito) { - super(incognito); - } - - @Override - public void createNewTab( - AsyncTabCreationParams asyncParams, @TabLaunchType int type, int parentId) { - if (!isLinkClickLaunchType(type)) { - // Only link clicks should open in CCT, if the browser opens a tab for, say, intent - // handling, we should continue to open in NoTouchActivity. - super.createNewTab(asyncParams, type, parentId); - return; - } - if (UrlUtilities.validateIntentUrl(asyncParams.getLoadUrlParams().getUrl())) { - // Handle intent URLs as normal. - super.createNewTab(asyncParams, type, parentId); - return; - } - - String url = asyncParams.getLoadUrlParams().getUrl(); - - int assignedTabId = TabIdManager.getInstance().generateValidId(Tab.INVALID_TAB_ID); - AsyncTabParamsManager.add(assignedTabId, asyncParams); - - Intent intent = new CustomTabsIntent.Builder().setShowTitle(true).build().intent; - intent.setData(Uri.parse(url)); - intent.putExtra(CustomTabIntentDataProvider.EXTRA_SEND_TO_EXTERNAL_DEFAULT_HANDLER, true); - intent.putExtra(CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_CHROME, true); - intent.putExtra(CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_WEBAPK, false); - intent.putExtra( - CustomTabIntentDataProvider.EXTRA_BROWSER_LAUNCH_SOURCE, LaunchSourceType.OTHER); - intent.putExtra(Browser.EXTRA_APPLICATION_ID, - ContextUtils.getApplicationContext().getPackageName()); - addAsyncTabExtras(asyncParams, parentId, false /* isChromeUI */, assignedTabId, intent); - - IntentHandler.startActivityForTrustedIntent(intent); - } - - private boolean isLinkClickLaunchType(@TabLaunchType int type) { - return type == TabLaunchType.FROM_LINK || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND - || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND; - } -}
diff --git a/chrome/android/touchless/javatests/src/org/chromium/chrome/browser/touchless/TouchlessNavigationRecorderTest.java b/chrome/android/touchless/javatests/src/org/chromium/chrome/browser/touchless/TouchlessNavigationRecorderTest.java index 087f132..6ec505ff 100644 --- a/chrome/android/touchless/javatests/src/org/chromium/chrome/browser/touchless/TouchlessNavigationRecorderTest.java +++ b/chrome/android/touchless/javatests/src/org/chromium/chrome/browser/touchless/TouchlessNavigationRecorderTest.java
@@ -85,8 +85,10 @@ ChromeTabUtils.waitForTabPageLoaded(mInitialTab, (String) null); - // This will trigger an intent and use PageTransition.FROM_API. - mActivityTestRule.getActivity().getTabCreator(false).launchNTP(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + // This will trigger an intent and use PageTransition.FROM_API. + mActivityTestRule.getActivity().getTabCreator(false).launchNTP(); + }); callback.waitForCallback(0); }
diff --git a/chrome/android/touchless/touchless_java_sources.gni b/chrome/android/touchless/touchless_java_sources.gni index defeeea..05cd5780 100644 --- a/chrome/android/touchless/touchless_java_sources.gni +++ b/chrome/android/touchless/touchless_java_sources.gni
@@ -44,7 +44,7 @@ "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessPreferences.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessRecyclerView.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessSuggestionsBinder.java", - "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java", + "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabCreator.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabObserver.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiCoordinator.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiCoordinatorImpl.java",
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn index b70ca4a..e21dd83 100644 --- a/chrome/android/webapk/shell_apk/BUILD.gn +++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -187,9 +187,6 @@ native_lib_placeholders = [ "libfoo.so" ] if (!is_java_debug) { - if (defined(webapk_proguard_jar_path)) { - proguard_jar_path = webapk_proguard_jar_path - } proguard_enabled = true proguard_configs = [ "//chrome/android/webapk/shell_apk/proguard.flags",
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h index f91d2a2..715865c6 100644 --- a/chrome/app/chrome_command_ids.h +++ b/chrome/app/chrome_command_ids.h
@@ -333,6 +333,8 @@ // Context menu items for Sharing #define IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE 51030 #define IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES 51031 +#define IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE 51032 +#define IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES 51033 // Context menu items in the status tray #define IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND 51100
diff --git a/chrome/app/helper-gpu-entitlements.plist b/chrome/app/helper-gpu-entitlements.plist index a1c430a..d35e43a 100644 --- a/chrome/app/helper-gpu-entitlements.plist +++ b/chrome/app/helper-gpu-entitlements.plist
@@ -2,7 +2,7 @@ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> - <key>com.apple.security.cs.allow-unsigned-executable-memory</key> + <key>com.apple.security.cs.allow-jit</key> <true/> </dict> </plist>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 48fecf1..75c6d6a 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -3367,6 +3367,8 @@ "sharing/click_to_call/click_to_call_ui_controller.h", "sharing/click_to_call/click_to_call_utils.cc", "sharing/click_to_call/click_to_call_utils.h", + "sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc", + "sharing/shared_clipboard/shared_clipboard_context_menu_observer.h", "sharing/shared_clipboard/shared_clipboard_ui_controller.cc", "sharing/shared_clipboard/shared_clipboard_ui_controller.h", "sharing/sharing_dialog.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 7c61660e..b2e6be39 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1096,11 +1096,16 @@ const FeatureEntry::FeatureParam kFilteringPredictionEmptyFilterEnabled[] = { {"filter", ui::input_prediction::kFilterNameEmpty}}; +const FeatureEntry::FeatureParam kFilteringPredictionOneEuroFilterEnabled[] = { + {"filter", ui::input_prediction::kFilterNameOneEuro}}; const FeatureEntry::FeatureVariation kFilteringPredictionFeatureVariations[] = { {ui::input_prediction::kFilterNameEmpty, kFilteringPredictionEmptyFilterEnabled, - base::size(kFilteringPredictionEmptyFilterEnabled), nullptr}}; + base::size(kFilteringPredictionEmptyFilterEnabled), nullptr}, + {ui::input_prediction::kFilterNameOneEuro, + kFilteringPredictionOneEuroFilterEnabled, + base::size(kFilteringPredictionOneEuroFilterEnabled), nullptr}}; #if defined(OS_ANDROID) const FeatureEntry::FeatureParam kBottomOfflineIndicatorEnabled[] = {
diff --git a/chrome/browser/android/servicification_background_service_jni.cc b/chrome/browser/android/servicification_background_service_jni.cc index eacdc3a..6461df9 100644 --- a/chrome/browser/android/servicification_background_service_jni.cc +++ b/chrome/browser/android/servicification_background_service_jni.cc
@@ -52,36 +52,58 @@ std::move(mapped), 0, 0, base::StringPiece(), /* read_only */ true); if (memory_allocator->GetMemoryState() == base::PersistentMemoryAllocator::MEMORY_DELETED) { + LOG(ERROR) << "The memory allocator state shouldn't be MEMORY_DELETED!"; return false; } - if (memory_allocator->IsCorrupt()) - return false; - if (!metrics::PersistentSystemProfile::HasSystemProfile(*memory_allocator)) + if (memory_allocator->IsCorrupt()) { + LOG(ERROR) << "The memory allocator is corrupt!"; return false; + } + + if (!metrics::PersistentSystemProfile::HasSystemProfile(*memory_allocator)) { + LOG(ERROR) << "There isn't a System Profile!"; + return false; + } metrics::SystemProfileProto system_profile_proto; if (!metrics::PersistentSystemProfile::GetSystemProfile( - *memory_allocator, &system_profile_proto)) + *memory_allocator, &system_profile_proto)) { + LOG(ERROR) << "Failed to get the System Profile!"; return false; + } - if (!system_profile_proto.has_os()) + if (!system_profile_proto.has_os()) { + LOG(ERROR) << "The os info isn't set!"; return false; + } const metrics::SystemProfileProto_OS& os = system_profile_proto.os(); - if (!os.has_version()) + if (!os.has_version()) { + LOG(ERROR) << "The os version isn't set!"; return false; + } - if (base::SysInfo::OperatingSystemVersion().compare(os.version()) != 0) + if (base::SysInfo::OperatingSystemVersion().compare(os.version()) != 0) { + LOG(ERROR) << "The os version doesn't match!"; return false; + } std::vector<variations::ActiveGroupId> field_trial_ids; variations::GetFieldTrialActiveGroupIds("", &field_trial_ids); + int expected_size = static_cast<int>(field_trial_ids.size()); - int expeceted_size = static_cast<int>(field_trial_ids.size()); - // The active field trial "PersistentHistograms" is guaranteed in the list. - if (expeceted_size <= 0) + // The active field trial "Foo" is guaranteed in the list. + if (expected_size <= 0) { + LOG(ERROR) << "Expect at least one active field trial!"; return false; + } - return system_profile_proto.field_trial_size() == expeceted_size; + if (system_profile_proto.field_trial_size() != expected_size) { + LOG(ERROR) << "The size of field trials recorded in the System Profile" + << " doesn't match the size of active field trials!"; + return false; + } + + return true; }
diff --git a/chrome/browser/autofill/automated_tests/cache_replayer.cc b/chrome/browser/autofill/automated_tests/cache_replayer.cc index 421361a2..ba42c0c 100644 --- a/chrome/browser/autofill/automated_tests/cache_replayer.cc +++ b/chrome/browser/autofill/automated_tests/cache_replayer.cc
@@ -526,6 +526,7 @@ VLOG(1) << "Did not match any response for " << key; return false; } + VLOG(1) << "Retrieving response for " << key; std::string decompressed_http_response; // Safe to use at() here since we looked for key's presence and there is no // mutation done when there is concurrency.
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc index 896627e..89eb1e0 100644 --- a/chrome/browser/autofill/captured_sites_test_utils.cc +++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -449,6 +449,10 @@ args.push_back(base::StringPrintf("--http_port=%d", kHostHttpPort)); args.push_back(base::StringPrintf("--https_port=%d", kHostHttpsPort)); args.push_back("--serve_response_in_chronological_sequence"); + // Start WPR in quiet mode, removing the extra verbose ServeHTTP interactions + // that are for the the overwhelming majority unhelpful, but for extra + // debugging of a test case, this might make sense to comment out. + args.push_back("--quiet_mode"); args.push_back(base::StringPrintf( "--inject_scripts=%s,%s", FilePathToUTF8(src_dir.AppendASCII("third_party")
diff --git a/chrome/browser/certificate_manager_model.cc b/chrome/browser/certificate_manager_model.cc index b140b5d..c175586 100644 --- a/chrome/browser/certificate_manager_model.cc +++ b/chrome/browser/certificate_manager_model.cc
@@ -36,6 +36,7 @@ #include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h" #include "chrome/browser/chromeos/policy/user_network_configuration_updater.h" #include "chrome/browser/chromeos/policy/user_network_configuration_updater_factory.h" +#include "chromeos/network/onc/certificate_scope.h" #include "chromeos/network/policy_certificate_provider.h" #endif @@ -308,11 +309,13 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); switch (mode_) { case Mode::kPolicyCertsWithoutWebTrust: - RefreshImpl(policy_certs_provider_->GetCertificatesWithoutWebTrust(), + RefreshImpl(policy_certs_provider_->GetCertificatesWithoutWebTrust( + chromeos::onc::CertificateScope::Default()), false /* policy_web_trusted */); break; case Mode::kPolicyCertsWithWebTrust: - RefreshImpl(policy_certs_provider_->GetWebTrustedCertificates(), + RefreshImpl(policy_certs_provider_->GetWebTrustedCertificates( + chromeos::onc::CertificateScope::Default()), true /* policy_web_trusted */); break; default:
diff --git a/chrome/browser/certificate_manager_model_unittest.cc b/chrome/browser/certificate_manager_model_unittest.cc index 5551587..d071c00 100644 --- a/chrome/browser/certificate_manager_model_unittest.cc +++ b/chrome/browser/certificate_manager_model_unittest.cc
@@ -18,6 +18,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/certificate_provider/certificate_provider.h" +#include "chromeos/network/onc/certificate_scope.h" #include "chromeos/network/policy_certificate_provider.h" #endif @@ -202,7 +203,11 @@ observer_list_.RemoveObserver(observer); } - net::CertificateList GetAllServerAndAuthorityCertificates() const override { + net::CertificateList GetAllServerAndAuthorityCertificates( + const chromeos::onc::CertificateScope& scope) const override { + // The CertificateManagerModel only retrieves profile-wide certificates. + EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope); + net::CertificateList merged; merged.insert(merged.end(), web_trusted_certs_.begin(), web_trusted_certs_.end()); @@ -211,20 +216,36 @@ return merged; } - net::CertificateList GetAllAuthorityCertificates() const override { + net::CertificateList GetAllAuthorityCertificates( + const chromeos::onc::CertificateScope& scope) const override { // This function is not called by CertificateManagerModel. NOTREACHED(); return net::CertificateList(); } - net::CertificateList GetWebTrustedCertificates() const override { + net::CertificateList GetWebTrustedCertificates( + const chromeos::onc::CertificateScope& scope) const override { + // The CertificateManagerModel only retrieves profile-wide certificates. + EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope); + return web_trusted_certs_; } - net::CertificateList GetCertificatesWithoutWebTrust() const override { + net::CertificateList GetCertificatesWithoutWebTrust( + const chromeos::onc::CertificateScope& scope) const override { + // The CertificateManagerModel only retrieves profile-wide certificates. + EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope); + return not_web_trusted_certs_; } + const std::set<std::string>& GetExtensionIdsWithPolicyCertificates() + const override { + // This function is not called by CertificateManagerModel. + NOTREACHED(); + return kNoExtensions; + } + void SetPolicyProvidedCertificates( const net::CertificateList& web_trusted_certs, const net::CertificateList& not_web_trusted_certs) { @@ -242,6 +263,7 @@ true /* check_empty */>::Unchecked observer_list_; net::CertificateList web_trusted_certs_; net::CertificateList not_web_trusted_certs_; + const std::set<std::string> kNoExtensions = {}; }; class FakeExtensionCertificateProvider : public chromeos::CertificateProvider {
diff --git a/chrome/browser/chromeos/crostini/crostini_export_import.cc b/chrome/browser/chromeos/crostini/crostini_export_import.cc index 3f7363a..4f87d72 100644 --- a/chrome/browser/chromeos/crostini/crostini_export_import.cc +++ b/chrome/browser/chromeos/crostini/crostini_export_import.cc
@@ -8,7 +8,6 @@ #include "base/bind.h" #include "base/files/file_util.h" -#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/strings/stringprintf.h" #include "base/task/post_task.h" @@ -240,8 +239,7 @@ const base::Time& start, const ContainerId& container_id, CrostiniManager::CrostiniResultCallback callback, - CrostiniResult result, - uint64_t container_size) { + CrostiniResult result) { auto it = notifications_.find(container_id); DCHECK(it != notifications_.end()) << ContainerIdToString(container_id) << " has no notification to update"; @@ -264,43 +262,6 @@ case CrostiniExportImportNotification::Status::RUNNING: UMA_HISTOGRAM_LONG_TIMES("Crostini.BackupTimeSuccess", base::Time::Now() - start); - // Log backup size statistics. - // TODO(juwa): Send compressed bytes written in progress message from - // tremplin, to remove the need to read the file's size. - base::PostTask( - FROM_HERE, - {base::ThreadPool(), base::MayBlock(), - base::TaskPriority::BEST_EFFORT}, - base::BindOnce( - [](const base::FilePath& path, uint64_t size) { - if (size == 0) { - LOG(ERROR) << "Uncompressed container size from export " - "progress is zero."; - NOTREACHED(); - return; - } - uint64_t compressed_size{}; - { - int64_t file_size; - if (!base::GetFileSize(path, &file_size) || file_size < 0) { - LOG(ERROR) << "Couldn't get exported file size for " - "histogram"; - return; - } - compressed_size = static_cast<uint64_t>(file_size); - } - - base::UmaHistogramCustomCounts( - "Crostini.BackupUncompressedSizeLog2", - std::round(std::log2(size)), 0, 50, 50); - base::UmaHistogramCustomCounts( - "Crostini.BackupCompressedSizeLog2", - std::round(std::log2(compressed_size)), 0, 50, 50); - base::UmaHistogramPercentage( - "Crostini.BackupCompressionRatio", - std::round(compressed_size * 100.0 / size)); - }, - it->second->path(), container_size)); RemoveNotification(it).SetStatusDone(); break; default:
diff --git a/chrome/browser/chromeos/crostini/crostini_export_import.h b/chrome/browser/chromeos/crostini/crostini_export_import.h index 2d37bb2f..7284cdc7 100644 --- a/chrome/browser/chromeos/crostini/crostini_export_import.h +++ b/chrome/browser/chromeos/crostini/crostini_export_import.h
@@ -164,8 +164,7 @@ void OnExportComplete(const base::Time& start, const ContainerId& container_id, CrostiniManager::CrostiniResultCallback callback, - CrostiniResult result, - uint64_t container_size); + CrostiniResult result); void ImportAfterSharing(const ContainerId& container_id, CrostiniManager::CrostiniResultCallback callback,
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc index 7dd2892f..9ec4e99 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager.cc +++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -82,16 +82,16 @@ GetCiceroneClient()->WaitForServiceToBeAvailable(std::move(callback)); } -// Find any callbacks for the specified |vm_name|, invoke them with -// |arguments|... and erase them from the map. -template <typename... Arguments> +// Find any callbacks for the specified |vm_name|, invoke them with |result| +// and erase them from the map. void InvokeAndErasePendingCallbacks( - std::map<ContainerId, base::OnceCallback<void(Arguments...)>>* vm_keyed_map, + std::map<ContainerId, CrostiniManager::CrostiniResultCallback>* + vm_keyed_map, const std::string& vm_name, - Arguments&&... arguments) { + CrostiniResult result) { for (auto it = vm_keyed_map->begin(); it != vm_keyed_map->end();) { if (it->first.first == vm_name) { - std::move(it->second).Run(arguments...); + std::move(it->second).Run(result); vm_keyed_map->erase(it++); } else { ++it; @@ -1096,25 +1096,23 @@ request.container_name(), std::move(callback))); } -void CrostiniManager::ExportLxdContainer( - std::string vm_name, - std::string container_name, - base::FilePath export_path, - base::OnceCallback<void(CrostiniResult success, uint64_t container_size)> - callback) { +void CrostiniManager::ExportLxdContainer(std::string vm_name, + std::string container_name, + base::FilePath export_path, + CrostiniResultCallback callback) { if (vm_name.empty()) { LOG(ERROR) << "vm_name is required"; - std::move(callback).Run(CrostiniResult::CLIENT_ERROR, 0); + std::move(callback).Run(CrostiniResult::CLIENT_ERROR); return; } if (container_name.empty()) { LOG(ERROR) << "container_name is required"; - std::move(callback).Run(CrostiniResult::CLIENT_ERROR, 0); + std::move(callback).Run(CrostiniResult::CLIENT_ERROR); return; } if (export_path.empty()) { LOG(ERROR) << "export_path is required"; - std::move(callback).Run(CrostiniResult::CLIENT_ERROR, 0); + std::move(callback).Run(CrostiniResult::CLIENT_ERROR); return; } @@ -1123,7 +1121,7 @@ export_lxd_container_callbacks_.end()) { LOG(ERROR) << "Export currently in progress for " << vm_name << ", " << container_name; - std::move(callback).Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED, 0); + std::move(callback).Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED); return; } export_lxd_container_callbacks_.emplace(key, std::move(callback)); @@ -1779,7 +1777,7 @@ // be marked as failed. InvokeAndErasePendingCallbacks( &export_lxd_container_callbacks_, vm_name, - CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STARTED, uint64_t{0}); + CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STARTED); InvokeAndErasePendingCallbacks( &import_lxd_container_callbacks_, vm_name, CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STARTED); @@ -1859,7 +1857,7 @@ running_containers_.erase(vm_name); InvokeAndErasePendingCallbacks( &export_lxd_container_callbacks_, vm_name, - CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED, uint64_t{0}); + CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED); InvokeAndErasePendingCallbacks( &import_lxd_container_callbacks_, vm_name, CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED); @@ -2445,8 +2443,7 @@ if (!response) { LOG(ERROR) << "Failed to export lxd container. Empty response."; - std::move(it->second) - .Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED, 0); + std::move(it->second).Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED); export_lxd_container_callbacks_.erase(it); return; } @@ -2458,8 +2455,7 @@ vm_tools::cicerone::ExportLxdContainerResponse::EXPORTING) { LOG(ERROR) << "Failed to export container: status=" << response->status() << ", failure_reason=" << response->failure_reason(); - std::move(it->second) - .Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED, 0); + std::move(it->second).Run(CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED); export_lxd_container_callbacks_.erase(it); } } @@ -2520,7 +2516,7 @@ << signal.container_name(); return; } - std::move(it->second).Run(result, signal.input_bytes_streamed()); + std::move(it->second).Run(result); export_lxd_container_callbacks_.erase(it); }
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h index cd651e4..48ca586b 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager.h +++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -255,12 +255,10 @@ // Checks the arguments for exporting an Lxd container via // CiceroneClient::ExportLxdContainer. |callback| is called immediately if the // arguments are bad, or after the method call finishes. - void ExportLxdContainer( - std::string vm_name, - std::string container_name, - base::FilePath export_path, - base::OnceCallback<void(CrostiniResult result, uint64_t container_size)> - callback); + void ExportLxdContainer(std::string vm_name, + std::string container_name, + base::FilePath export_path, + CrostiniResultCallback callback); // Checks the arguments for importing an Lxd container via // CiceroneClient::ImportLxdContainer. |callback| is called immediately if the @@ -718,10 +716,7 @@ std::multimap<ContainerId, CrostiniResultCallback> create_lxd_container_callbacks_; std::multimap<ContainerId, BoolCallback> delete_lxd_container_callbacks_; - std::map< - ContainerId, - base::OnceCallback<void(CrostiniResult result, uint64_t container_size)>> - export_lxd_container_callbacks_; + std::map<ContainerId, CrostiniResultCallback> export_lxd_container_callbacks_; std::map<ContainerId, CrostiniResultCallback> import_lxd_container_callbacks_; // Callbacks to run after Tremplin is started, keyed by vm_name. These are
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc index 2e0746a3..fb8a6a86 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc +++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -55,16 +55,6 @@ std::move(closure).Run(); } -void ExpectCrostiniExportResult(base::OnceClosure closure, - CrostiniResult expected_result, - uint64_t expected_container_size, - CrostiniResult result, - uint64_t container_size) { - EXPECT_EQ(expected_result, result); - EXPECT_EQ(expected_container_size, container_size); - std::move(closure).Run(); -} - } // namespace class CrostiniManagerTest : public testing::Test { @@ -1113,8 +1103,8 @@ TEST_F(CrostiniManagerTest, ExportContainerSuccess) { crostini_manager()->ExportLxdContainer( kVmName, kContainerName, base::FilePath("export_path"), - base::BindOnce(&ExpectCrostiniExportResult, run_loop()->QuitClosure(), - CrostiniResult::SUCCESS, 123)); + base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(), + CrostiniResult::SUCCESS)); // Send signals, PACK, DOWNLOAD, DONE. vm_tools::cicerone::ExportLxdContainerProgressSignal signal; @@ -1132,7 +1122,6 @@ signal.set_status( vm_tools::cicerone::ExportLxdContainerProgressSignal_Status_DONE); - signal.set_input_bytes_streamed(123); fake_cicerone_client_->NotifyExportLxdContainerProgress(signal); run_loop()->Run(); @@ -1142,14 +1131,14 @@ // 1st call succeeds. crostini_manager()->ExportLxdContainer( kVmName, kContainerName, base::FilePath("export_path"), - base::BindOnce(&ExpectCrostiniExportResult, run_loop()->QuitClosure(), - CrostiniResult::SUCCESS, 123)); + base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(), + CrostiniResult::SUCCESS)); // 2nd call fails since 1st call is in progress. crostini_manager()->ExportLxdContainer( kVmName, kContainerName, base::FilePath("export_path"), - base::BindOnce(&ExpectCrostiniExportResult, base::DoNothing::Once(), - CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED, 0)); + base::BindOnce(&ExpectCrostiniResult, base::DoNothing::Once(), + CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED)); // Send signal to indicate 1st call is done. vm_tools::cicerone::ExportLxdContainerProgressSignal signal; @@ -1158,7 +1147,6 @@ signal.set_container_name(kContainerName); signal.set_status( vm_tools::cicerone::ExportLxdContainerProgressSignal_Status_DONE); - signal.set_input_bytes_streamed(123); fake_cicerone_client_->NotifyExportLxdContainerProgress(signal); run_loop()->Run(); @@ -1167,8 +1155,8 @@ TEST_F(CrostiniManagerTest, ExportContainerFailFromSignal) { crostini_manager()->ExportLxdContainer( kVmName, kContainerName, base::FilePath("export_path"), - base::BindOnce(&ExpectCrostiniExportResult, run_loop()->QuitClosure(), - CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED, 123)); + base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(), + CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED)); // Send signal with FAILED. vm_tools::cicerone::ExportLxdContainerProgressSignal signal; @@ -1177,7 +1165,6 @@ signal.set_container_name(kContainerName); signal.set_status( vm_tools::cicerone::ExportLxdContainerProgressSignal_Status_FAILED); - signal.set_input_bytes_streamed(123); fake_cicerone_client_->NotifyExportLxdContainerProgress(signal); run_loop()->Run(); @@ -1187,9 +1174,9 @@ crostini_manager()->AddRunningVmForTesting(kVmName); crostini_manager()->ExportLxdContainer( kVmName, kContainerName, base::FilePath("export_path"), - base::BindOnce(&ExpectCrostiniExportResult, run_loop()->QuitClosure(), - CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED, - 0)); + base::BindOnce( + &ExpectCrostiniResult, run_loop()->QuitClosure(), + CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED)); crostini_manager()->StopVm(kVmName, base::DoNothing()); run_loop()->Run(); }
diff --git a/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider_browsertest.cc b/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider_browsertest.cc index 9a7e2d9..5f6725e 100644 --- a/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider_browsertest.cc +++ b/chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider_browsertest.cc
@@ -31,11 +31,7 @@ #include "content/public/browser/browser_context.h" #include "dbus/message.h" #include "dbus/object_path.h" -#include "extensions/browser/deferred_start_render_host.h" -#include "extensions/browser/deferred_start_render_host_observer.h" -#include "extensions/browser/extension_host.h" -#include "extensions/browser/process_manager.h" -#include "extensions/browser/process_manager_observer.h" +#include "extensions/test/test_background_page_first_load_observer.h" #include "net/cert/asn1_util.h" #include "net/cert/x509_certificate.h" #include "net/cert/x509_util.h" @@ -56,68 +52,6 @@ return spki_bytes.as_string(); } -// Observer that allows to wait until the extension's background page was first -// loaded. -// TODO(crbug.com/826417): Extract into a generic helper to be usable in other -// tests. -class ExtensionBackgroundPageFirstLoadObserver final - : public extensions::ProcessManagerObserver, - public extensions::DeferredStartRenderHostObserver { - public: - ExtensionBackgroundPageFirstLoadObserver( - content::BrowserContext* browser_context, - const std::string& extension_id) - : extension_id_(extension_id), - process_manager_(extensions::ProcessManager::Get(browser_context)) { - process_manager_->AddObserver(this); - extension_host_ = - process_manager_->GetBackgroundHostForExtension(extension_id_); - if (extension_host_) - OnObtainedExtensionHost(); - } - - ~ExtensionBackgroundPageFirstLoadObserver() override { - if (extension_host_) { - static_cast<extensions::DeferredStartRenderHost*>(extension_host_) - ->RemoveDeferredStartRenderHostObserver(this); - } - process_manager_->RemoveObserver(this); - } - - void Wait() { - if (!extension_host_ || !extension_host_->has_loaded_once()) - run_loop_.Run(); - } - - private: - // extensions::ProcessManagerObserver: - void OnBackgroundHostCreated(extensions::ExtensionHost* host) override { - if (host->extension_id() == extension_id_) { - DCHECK(!extension_host_); - extension_host_ = host; - OnObtainedExtensionHost(); - } - } - - // extensions::DeferredStartRenderHostObserver: - void OnDeferredStartRenderHostDidStopFirstLoad( - const extensions::DeferredStartRenderHost* /* host */) override { - run_loop_.Quit(); - } - - void OnObtainedExtensionHost() { - static_cast<extensions::DeferredStartRenderHost*>(extension_host_) - ->AddDeferredStartRenderHostObserver(this); - } - - const std::string extension_id_; - extensions::ProcessManager* const process_manager_; - extensions::ExtensionHost* extension_host_ = nullptr; - base::RunLoop run_loop_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionBackgroundPageFirstLoadObserver); -}; - } // namespace // Tests for the CryptohomeKeyDelegateServiceProvider class. @@ -251,7 +185,7 @@ cert_provider_extension_ = std::make_unique<TestCertificateProviderExtension>(profile, kExtensionId); - ExtensionBackgroundPageFirstLoadObserver bg_page_first_load_observer( + extensions::TestBackgroundPageFirstLoadObserver bg_page_first_load_observer( profile, kExtensionId); AddExtensionForForceInstallation(kExtensionId, kExtensionUpdateManifestPath);
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc index 578a46df..31fd0ad 100644 --- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc +++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -80,6 +80,7 @@ #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/upstart/upstart_client.h" #include "chromeos/login/login_state/login_state.h" +#include "chromeos/network/onc/certificate_scope.h" #include "chromeos/network/proxy/proxy_config_service_impl.h" #include "chromeos/settings/cros_settings_names.h" #include "chromeos/timezone/timezone_resolver.h"
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater.cc b/chrome/browser/chromeos/policy/network_configuration_updater.cc index b3f5f15..888bcc0 100644 --- a/chrome/browser/chromeos/policy/network_configuration_updater.cc +++ b/chrome/browser/chromeos/policy/network_configuration_updater.cc
@@ -14,6 +14,7 @@ #include "net/cert/x509_certificate.h" #include "net/cert/x509_util_nss.h" +using chromeos::onc::CertificateScope; using chromeos::onc::OncParsedCertificates; namespace policy { @@ -24,19 +25,37 @@ using ServerOrAuthorityCertPredicate = base::RepeatingCallback<bool( const OncParsedCertificates::ServerOrAuthorityCertificate& cert)>; +// Returns a filtered copy of |sever_or_authority_certificates|. The filtered +// copy will contain a certificate from the input iff it matches |scope| and +// executing |predicate| on it returned true. net::CertificateList GetFilteredCertificateListFromOnc( const std::vector<OncParsedCertificates::ServerOrAuthorityCertificate>& server_or_authority_certificates, + const CertificateScope& scope, ServerOrAuthorityCertPredicate predicate) { net::CertificateList certificates; for (const auto& server_or_authority_cert : server_or_authority_certificates) { - if (predicate.Run(server_or_authority_cert)) + if (server_or_authority_cert.scope() == scope && + predicate.Run(server_or_authority_cert)) certificates.push_back(server_or_authority_cert.certificate()); } return certificates; } +// Returns all extension IDs that were used in a Scope of a one of the +// |server_or_authority_certificates|. +std::set<std::string> CollectExtensionIds( + const std::vector<OncParsedCertificates::ServerOrAuthorityCertificate>& + server_or_authority_certificates) { + std::set<std::string> extension_ids; + for (const auto& cert : server_or_authority_certificates) { + if (cert.scope().is_extension_scoped()) + extension_ids.insert(cert.scope().extension_id()); + } + return extension_ids; +} + } // namespace NetworkConfigurationUpdater::~NetworkConfigurationUpdater() { @@ -72,21 +91,22 @@ } net::CertificateList -NetworkConfigurationUpdater::GetAllServerAndAuthorityCertificates() const { +NetworkConfigurationUpdater::GetAllServerAndAuthorityCertificates( + const CertificateScope& scope) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return GetFilteredCertificateListFromOnc( - certs_->server_or_authority_certificates(), + certs_->server_or_authority_certificates(), scope, base::BindRepeating( [](const OncParsedCertificates::ServerOrAuthorityCertificate& cert) { return true; })); } -net::CertificateList NetworkConfigurationUpdater::GetAllAuthorityCertificates() - const { +net::CertificateList NetworkConfigurationUpdater::GetAllAuthorityCertificates( + const CertificateScope& scope) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return GetFilteredCertificateListFromOnc( - certs_->server_or_authority_certificates(), + certs_->server_or_authority_certificates(), scope, base::BindRepeating( [](const OncParsedCertificates::ServerOrAuthorityCertificate& cert) { return cert.type() == @@ -95,11 +115,11 @@ })); } -net::CertificateList NetworkConfigurationUpdater::GetWebTrustedCertificates() - const { +net::CertificateList NetworkConfigurationUpdater::GetWebTrustedCertificates( + const CertificateScope& scope) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return GetFilteredCertificateListFromOnc( - certs_->server_or_authority_certificates(), + certs_->server_or_authority_certificates(), scope, base::BindRepeating( [](const OncParsedCertificates::ServerOrAuthorityCertificate& cert) { return cert.web_trust_requested(); @@ -107,16 +127,22 @@ } net::CertificateList -NetworkConfigurationUpdater::GetCertificatesWithoutWebTrust() const { +NetworkConfigurationUpdater::GetCertificatesWithoutWebTrust( + const CertificateScope& scope) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return GetFilteredCertificateListFromOnc( - certs_->server_or_authority_certificates(), + certs_->server_or_authority_certificates(), scope, base::BindRepeating( [](const OncParsedCertificates::ServerOrAuthorityCertificate& cert) { return !cert.web_trust_requested(); })); } +const std::set<std::string>& +NetworkConfigurationUpdater::GetExtensionIdsWithPolicyCertificates() const { + return extension_ids_with_policy_certificates_; +} + NetworkConfigurationUpdater::NetworkConfigurationUpdater( onc::ONCSource onc_source, std::string policy_key, @@ -288,6 +314,8 @@ return; certs_ = std::move(incoming_certs); + extension_ids_with_policy_certificates_ = + CollectExtensionIds(certs_->server_or_authority_certificates()); if (client_certs_changed) ImportClientCertificates();
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater.h b/chrome/browser/chromeos/policy/network_configuration_updater.h index 2b5b1cb..27b25ac 100644 --- a/chrome/browser/chromeos/policy/network_configuration_updater.h +++ b/chrome/browser/chromeos/policy/network_configuration_updater.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_CHROMEOS_POLICY_NETWORK_CONFIGURATION_UPDATER_H_ #include <memory> +#include <set> #include <string> #include <vector> @@ -13,6 +14,7 @@ #include "base/macros.h" #include "base/observer_list.h" #include "base/sequence_checker.h" +#include "chromeos/network/onc/certificate_scope.h" #include "chromeos/network/onc/onc_parsed_certificates.h" #include "chromeos/network/policy_certificate_provider.h" #include "components/onc/onc_constants.h" @@ -54,10 +56,17 @@ chromeos::PolicyCertificateProvider::Observer* observer) override; void RemovePolicyProvidedCertsObserver( chromeos::PolicyCertificateProvider::Observer* observer) override; - net::CertificateList GetAllServerAndAuthorityCertificates() const override; - net::CertificateList GetAllAuthorityCertificates() const override; - net::CertificateList GetWebTrustedCertificates() const override; - net::CertificateList GetCertificatesWithoutWebTrust() const override; + net::CertificateList GetAllServerAndAuthorityCertificates( + const chromeos::onc::CertificateScope& scope) const override; + net::CertificateList GetAllAuthorityCertificates( + const chromeos::onc::CertificateScope& scope) const override; + net::CertificateList GetWebTrustedCertificates( + const chromeos::onc::CertificateScope& scope) const override; + net::CertificateList GetCertificatesWithoutWebTrust( + const chromeos::onc::CertificateScope& scope) const override; + + const std::set<std::string>& GetExtensionIdsWithPolicyCertificates() + const override; protected: NetworkConfigurationUpdater( @@ -149,6 +158,7 @@ // Holds certificates from the last parsed ONC policy. std::unique_ptr<chromeos::onc::OncParsedCertificates> certs_; + std::set<std::string> extension_ids_with_policy_certificates_; // Observer list for notifying about ONC-provided server and CA certificate // changes.
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc index efa54d9..a189f68d 100644 --- a/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc +++ b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc
@@ -23,6 +23,7 @@ #include "chrome/test/base/testing_profile.h" #include "chromeos/network/fake_network_device_handler.h" #include "chromeos/network/mock_managed_network_configuration_handler.h" +#include "chromeos/network/onc/certificate_scope.h" #include "chromeos/network/onc/onc_certificate_importer.h" #include "chromeos/network/onc/onc_parsed_certificates.h" #include "chromeos/network/onc/onc_test_utils.h" @@ -249,6 +250,41 @@ "Type": "UnencryptedConfiguration" })"; +const char kFakeONCWithExtensionScopedCert[] = R"( + { "Certificates": [ + { "GUID": "{extension-scoped-certificate}", + "TrustBits": [ + "Web" + ], + "Scope": { + "Type": "Extension", + "Id": "ngjobkbdodapjbbncmagbccommkggmnj" + }, + "Type": "Authority", + "X509": "-----BEGIN CERTIFICATE-----\n + MIIC8zCCAdugAwIBAgIJALF9qhLor0+aMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV\n + BAMMDFRlc3QgUm9vdCBDQTAeFw0xNDA4MTQwMzA1MjlaFw0yNDA4MTEwMzA1Mjla\n + MBcxFTATBgNVBAMMDFRlc3QgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP\n + ADCCAQoCggEBALZJQeNCAVGofzx6cdP7zZE1F4QajvY2x9FwHfqG8267dm/oMi43\n + /TiSPWjkin1CMxRGG9wE9pFuVEDECgn97C1i4l7huiycwbFgTNrH+CJcgiBlQh5W\n + d3VP65AsSupXDiKNbJWsEerM1+72cA0J3aY1YV3Jdm2w8h6/MIbYd1I2lZcO0UbF\n + 7YE9G7DyYZU8wUA4719dumGf7yucn4WJdHBj1XboNX7OAeHzERGQHA31/Y3OEGyt\n + fFUaIW/XLfR4FeovOL2RnjwdB0b1Q8GCi68SU2UZimlpZgay2gv6KgChKhWESfEB\n + v5swBtAVoB+dUZFH4VNf717swmF5whSfxOMCAwEAAaNCMEAwDwYDVR0TAQH/BAUw\n + AwEB/zAdBgNVHQ4EFgQUvPcw0TzA8nn675/JbFyT84poq4MwDgYDVR0PAQH/BAQD\n + AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBXByn7f+j/sObYWGrDkKE4HLTzaLHs6Ikj\n + JNeo8iHDYOSkSVwAv9/HgniAKxj3rd3QYl6nsMzwqrTOcBJZZWd2BQAYmv/EKhfj\n + 8VXYvlxe68rLU4cQ1QkyNqdeQfRT2n5WYNJ+TpqlCF9ddennMMsi6e8ZSYOlI6H4\n + YEzlNtU5eBjxXr/OqgtTgSx4qQpr2xMQIRR/G3A9iRpAigYsXVAZYvnHRYnyPWYF\n + PX11W1UegEJyoZp8bQp09u6mIWw6mPt3gl/ya1bm3ZuOUPDGrv3qpgUHqSYGVrOy\n + 2bI3oCE+eQYfuVG+9LFJTZC1M+UOx15bQMVqBNFDepRqpE9h/ILg\n + -----END CERTIFICATE-----" }, + ], + "Type": "UnencryptedConfiguration" + })"; + +const char kExtensionIdWithScopedCert[] = "ngjobkbdodapjbbncmagbccommkggmnj"; + std::string ValueToString(const base::Value& value) { std::stringstream str; str << value; @@ -391,7 +427,8 @@ return updater; } - void CreateNetworkConfigurationUpdaterForDevicePolicy() { + NetworkConfigurationUpdater* + CreateNetworkConfigurationUpdaterForDevicePolicy() { auto testing_device_asset_id_getter = base::BindRepeating([] { return std::string(kFakeAssetId); }); network_configuration_updater_ = @@ -399,6 +436,7 @@ policy_service_.get(), &network_config_handler_, &network_device_handler_, chromeos::CrosSettings::Get(), testing_device_asset_id_getter); + return network_configuration_updater_.get(); } content::TestBrowserThreadBundle thread_bundle_; @@ -534,9 +572,16 @@ base::RunLoop().RunUntilIdle(); // Certificates with the "Web" trust flag set will be returned. - EXPECT_EQ(1u, updater->GetWebTrustedCertificates().size()); - EXPECT_EQ(1u, updater->GetCertificatesWithoutWebTrust().size()); - EXPECT_EQ(2u, updater->GetAllServerAndAuthorityCertificates().size()); + const auto kDefaultScope = chromeos::onc::CertificateScope::Default(); + EXPECT_EQ(1u, updater->GetWebTrustedCertificates(kDefaultScope).size()); + EXPECT_EQ(1u, updater->GetCertificatesWithoutWebTrust(kDefaultScope).size()); + EXPECT_EQ( + 2u, updater->GetAllServerAndAuthorityCertificates(kDefaultScope).size()); + EXPECT_EQ(0u, updater + ->GetAllServerAndAuthorityCertificates( + chromeos::onc::CertificateScope::ForExtension( + kExtensionIdWithScopedCert)) + .size()); updater->RemovePolicyProvidedCertsObserver(&observer); } @@ -559,9 +604,11 @@ base::RunLoop().RunUntilIdle(); // Verify that the returned certificate list is empty. - EXPECT_TRUE(updater->GetWebTrustedCertificates().empty()); - EXPECT_TRUE(updater->GetCertificatesWithoutWebTrust().empty()); - EXPECT_TRUE(updater->GetAllServerAndAuthorityCertificates().empty()); + const auto kDefaultScope = chromeos::onc::CertificateScope::Default(); + EXPECT_TRUE(updater->GetWebTrustedCertificates(kDefaultScope).empty()); + EXPECT_TRUE(updater->GetCertificatesWithoutWebTrust(kDefaultScope).empty()); + EXPECT_TRUE( + updater->GetAllServerAndAuthorityCertificates(kDefaultScope).empty()); // No call has been made to the policy-provided certificates observer. Mock::VerifyAndClearExpectations(&observer); @@ -575,9 +622,58 @@ UpdateProviderPolicy(policy); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(1u, updater->GetWebTrustedCertificates().size()); - EXPECT_EQ(1u, updater->GetCertificatesWithoutWebTrust().size()); - EXPECT_EQ(2u, updater->GetAllServerAndAuthorityCertificates().size()); + // Certificates with the "Web" trust flag set will be returned and forwarded + // to observers. + EXPECT_EQ(1u, updater->GetWebTrustedCertificates(kDefaultScope).size()); + EXPECT_EQ(1u, updater->GetCertificatesWithoutWebTrust(kDefaultScope).size()); + EXPECT_EQ( + 2u, updater->GetAllServerAndAuthorityCertificates(kDefaultScope).size()); + EXPECT_EQ(0u, updater + ->GetAllServerAndAuthorityCertificates( + chromeos::onc::CertificateScope::ForExtension( + kExtensionIdWithScopedCert)) + .size()); + + updater->RemovePolicyProvidedCertsObserver(&observer); +} + +TEST_F(NetworkConfigurationUpdaterTest, ExtensionScopedWebTrustedCertificate) { + // Ignore network configuration changes. + EXPECT_CALL(network_config_handler_, SetPolicy(_, _, _, _)) + .Times(AnyNumber()); + + NetworkConfigurationUpdater* updater = + CreateNetworkConfigurationUpdaterForDevicePolicy(); + + MockPolicyProvidedCertsObserver observer; + EXPECT_CALL(observer, OnPolicyProvidedCertsChanged()); + updater->AddPolicyProvidedCertsObserver(&observer); + + PolicyMap policy; + policy.Set(key::kDeviceOpenNetworkConfiguration, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD, + std::make_unique<base::Value>(kFakeONCWithExtensionScopedCert), + nullptr); + UpdateProviderPolicy(policy); + MarkPolicyProviderInitialized(); + base::RunLoop().RunUntilIdle(); + + // Certificates with the "Web" trust flag set will be returned. + const auto kDefaultScope = chromeos::onc::CertificateScope::Default(); + EXPECT_EQ(0u, updater->GetWebTrustedCertificates(kDefaultScope).size()); + EXPECT_EQ(0u, updater->GetCertificatesWithoutWebTrust(kDefaultScope).size()); + EXPECT_EQ( + 0u, updater->GetAllServerAndAuthorityCertificates(kDefaultScope).size()); + EXPECT_EQ(1u, updater + ->GetAllServerAndAuthorityCertificates( + chromeos::onc::CertificateScope::ForExtension( + kExtensionIdWithScopedCert)) + .size()); + EXPECT_EQ(1u, updater + ->GetWebTrustedCertificates( + chromeos::onc::CertificateScope::ForExtension( + kExtensionIdWithScopedCert)) + .size()); updater->RemovePolicyProvidedCertsObserver(&observer); }
diff --git a/chrome/browser/chromeos/policy/policy_cert_service.cc b/chrome/browser/chromeos/policy/policy_cert_service.cc index e59df62..50fe87f 100644 --- a/chrome/browser/chromeos/policy/policy_cert_service.cc +++ b/chrome/browser/chromeos/policy/policy_cert_service.cc
@@ -17,6 +17,8 @@ #include "chrome/browser/net/profile_network_context_service.h" #include "chrome/browser/net/profile_network_context_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chromeos/network/onc/certificate_scope.h" +#include "chromeos/network/policy_certificate_provider.h" #include "components/user_manager/user_manager.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -51,7 +53,8 @@ policy_certificate_provider_->AddPolicyProvidedCertsObserver(this); profile_wide_all_server_and_authority_certs_ = - policy_certificate_provider_->GetAllServerAndAuthorityCertificates(); + policy_certificate_provider_->GetAllServerAndAuthorityCertificates( + chromeos::onc::CertificateScope::Default()); profile_wide_trust_anchors_ = GetAllowedProfileWideTrustAnchors(); } @@ -65,7 +68,8 @@ void PolicyCertService::OnPolicyProvidedCertsChanged() { profile_wide_all_server_and_authority_certs_ = - policy_certificate_provider_->GetAllServerAndAuthorityCertificates(); + policy_certificate_provider_->GetAllServerAndAuthorityCertificates( + chromeos::onc::CertificateScope::Default()); profile_wide_trust_anchors_ = GetAllowedProfileWideTrustAnchors(); // Make all policy-provided server and authority certificates available to NSS @@ -109,7 +113,8 @@ return {}; net::CertificateList trust_anchors = - policy_certificate_provider_->GetWebTrustedCertificates(); + policy_certificate_provider_->GetWebTrustedCertificates( + chromeos::onc::CertificateScope::Default()); // Do not use certificates installed via ONC policy if the current session has // multiple profiles. This is important to make sure that any possibly tainted
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc index 717be62..1ebb1303 100644 --- a/chrome/browser/download/download_target_determiner.cc +++ b/chrome/browser/download/download_target_determiner.cc
@@ -373,10 +373,14 @@ // Downloads/music/music/music/bar.mp3. base::FilePath new_path(download_prefs_->DownloadPath().Append( suggested_path).NormalizePathSeparators()); - // Do not pass a mime type to GenerateSafeFileName so that it does not force - // the filename to have an extension if the (Chrome) extension does not - // suggest it. - net::GenerateSafeFileName(std::string(), false, &new_path); + // If the (Chrome) extension does not suggest an file extension, do not pass + // a mime type to GenerateSafeFileName so that it does not force the + // filename to have an extension. Otherwise, correct the file extension in + // case it is wrongly given. + if (new_path.Extension().empty()) + net::GenerateSafeFileName(std::string(), false, &new_path); + else + net::GenerateSafeFileName(download_->GetMimeType(), true, &new_path); virtual_path_ = new_path; create_target_directory_ = true; }
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chrome/browser/extensions/api/chrome_extensions_api_client.cc index 9a0afb65f..82adf99 100644 --- a/chrome/browser/extensions/api/chrome_extensions_api_client.cc +++ b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
@@ -16,6 +16,7 @@ #include "chrome/browser/extensions/api/chrome_device_permissions_prompt.h" #include "chrome/browser/extensions/api/declarative_content/chrome_content_rules_registry.h" #include "chrome/browser/extensions/api/declarative_content/default_content_predicate_evaluators.h" +#include "chrome/browser/extensions/api/extension_action/extension_action_api.h" #include "chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.h" #include "chrome/browser/extensions/api/file_system/chrome_file_system_delegate.h" #include "chrome/browser/extensions/api/management/chrome_management_api_delegate.h" @@ -25,7 +26,10 @@ #include "chrome/browser/extensions/api/storage/managed_value_store_cache.h" #include "chrome/browser/extensions/api/storage/sync_value_store_cache.h" #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h" +#include "chrome/browser/extensions/extension_action.h" +#include "chrome/browser/extensions/extension_action_manager.h" #include "chrome/browser/extensions/extension_action_runner.h" +#include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/system_display/display_info_provider.h" #include "chrome/browser/favicon/favicon_utils.h" #include "chrome/browser/guest_view/app_view/chrome_app_view_guest_delegate.h" @@ -213,6 +217,30 @@ runner->OnWebRequestBlocked(extension); } +void ChromeExtensionsAPIClient::UpdateActionCount( + content::BrowserContext* context, + const ExtensionId& extension_id, + int tab_id, + int action_count) { + const Extension* extension = + ExtensionRegistry::Get(context)->enabled_extensions().GetByID( + extension_id); + DCHECK(extension); + + ExtensionAction* action = + ExtensionActionManager::Get(context)->GetExtensionAction(*extension); + DCHECK(action); + + action->SetDNRActionCount(tab_id, action_count); + content::WebContents* tab_contents = nullptr; + if (ExtensionTabUtil::GetTabById( + tab_id, context, true /* include_incognito */, &tab_contents) && + tab_contents) { + ExtensionActionAPI::Get(context)->NotifyChange(action, tab_contents, + context); + } +} + AppViewGuestDelegate* ChromeExtensionsAPIClient::CreateAppViewGuestDelegate() const { return new ChromeAppViewGuestDelegate(); @@ -241,9 +269,9 @@ return new ChromeWebViewGuestDelegate(web_view_guest); } -WebViewPermissionHelperDelegate* ChromeExtensionsAPIClient:: - CreateWebViewPermissionHelperDelegate( - WebViewPermissionHelper* web_view_permission_helper) const { +WebViewPermissionHelperDelegate* +ChromeExtensionsAPIClient::CreateWebViewPermissionHelperDelegate( + WebViewPermissionHelper* web_view_permission_helper) const { return new ChromeWebViewPermissionHelperDelegate(web_view_permission_helper); } @@ -251,12 +279,10 @@ ChromeExtensionsAPIClient::CreateContentRulesRegistry( content::BrowserContext* browser_context, RulesCacheDelegate* cache_delegate) const { - return scoped_refptr<ContentRulesRegistry>( - new ChromeContentRulesRegistry( - browser_context, - cache_delegate, - base::Bind(&CreateDefaultContentPredicateEvaluators, - base::Unretained(browser_context)))); + return scoped_refptr<ContentRulesRegistry>(new ChromeContentRulesRegistry( + browser_context, cache_delegate, + base::Bind(&CreateDefaultContentPredicateEvaluators, + base::Unretained(browser_context)))); } std::unique_ptr<DevicePermissionsPrompt>
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.h b/chrome/browser/extensions/api/chrome_extensions_api_client.h index a4837bf2..f1b8197f 100644 --- a/chrome/browser/extensions/api/chrome_extensions_api_client.h +++ b/chrome/browser/extensions/api/chrome_extensions_api_client.h
@@ -39,6 +39,10 @@ void NotifyWebRequestWithheld(int render_process_id, int render_frame_id, const ExtensionId& extension_id) override; + void UpdateActionCount(content::BrowserContext* context, + const ExtensionId& extension_id, + int tab_id, + int action_count) override; AppViewGuestDelegate* CreateAppViewGuestDelegate() const override; ExtensionOptionsGuestDelegate* CreateExtensionOptionsGuestDelegate( ExtensionOptionsGuest* guest) const override;
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc index 2c6777cd..bddbf354d 100644 --- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc +++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -29,16 +29,21 @@ #include "base/threading/thread_restrictions.h" #include "base/values.h" #include "build/build_config.h" +#include "chrome/browser/extensions/extension_action.h" +#include "chrome/browser/extensions/extension_action_manager.h" #include "chrome/browser/extensions/extension_action_runner.h" #include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/load_error_reporter.h" #include "chrome/browser/extensions/scripting_permissions_modifier.h" #include "chrome/browser/net/profile_network_context_service.h" #include "chrome/browser/net/profile_network_context_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/extensions/api/extension_action/action_info.h" #include "chrome/common/webui_url_constants.h" #include "chrome/test/base/ui_test_utils.h" #include "components/prefs/pref_service.h" @@ -2636,6 +2641,263 @@ } } +// Test that the badge text for an extension will update to reflect the number +// of actions taken on requests matching the extension's ruleset. +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, + ActionsMatchedCountAsBadgeText) { + auto get_url_for_host = [this](std::string hostname) { + return embedded_test_server()->GetURL(hostname, + "/pages_with_script/index.html"); + }; + + // This page simulates a user clicking on a link, so that the next page it + // navigates to has a Referrer header. + auto get_url_with_referrer = [this](std::string hostname) { + return embedded_test_server()->GetURL(hostname, "/simulate_click.html"); + }; + + // Navigates frame with name |frame_name| to |url|. + auto navigate_frame = [this](const std::string& frame_name, const GURL& url) { + content::TestNavigationObserver navigation_observer( + web_contents(), 1 /*number_of_navigations*/); + + // Before navigation, We set the referrer policy of the iframe to + // 'no-referrer' to prevent a referer header from being added for the iframe + // navigation. + ASSERT_TRUE(content::ExecuteScript( + GetMainFrame(), + base::StringPrintf(R"( + document.getElementsByName('%s')[0].referrerPolicy = 'no-referrer'; + document.getElementsByName('%s')[0].src = '%s';)", + frame_name.c_str(), frame_name.c_str(), + url.spec().c_str()))); + navigation_observer.Wait(); + }; + + const std::string kFrameName1 = "frame1"; + const GURL page_url = embedded_test_server()->GetURL( + "norulesmatched.com", "/page_with_two_frames.html"); + + struct { + std::string url_filter; + int id; + int priority; + std::string action_type; + base::Optional<std::string> redirect_url; + base::Optional<std::vector<std::string>> remove_headers_list; + } rules_data[] = { + {"abc.com", 1, 1, "block", base::nullopt, base::nullopt}, + {"def.com", 2, 1, "redirect", "http://zzz.com", base::nullopt}, + {"jkl.com", 3, 1, "removeHeaders", base::nullopt, + std::vector<std::string>({"referer"})}, + {"abcd.com", 4, 1, "block", base::nullopt, base::nullopt}, + {"abcd", 5, 1, "allow", base::nullopt, base::nullopt}, + }; + + // Load the extension. + std::vector<TestRule> rules; + for (const auto& rule_data : rules_data) { + TestRule rule = CreateGenericRule(); + rule.condition->url_filter = rule_data.url_filter; + rule.id = rule_data.id; + rule.priority = rule_data.priority; + rule.condition->resource_types = + std::vector<std::string>({"main_frame", "sub_frame"}); + rule.action->type = rule_data.action_type; + rule.action->redirect.emplace(); + rule.action->redirect->url = rule_data.redirect_url; + rule.action->remove_headers_list = rule_data.remove_headers_list; + rules.push_back(rule); + } + + ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules( + rules, "test_extension", {URLPattern::kAllUrlsPattern})); + + const Extension* dnr_extension = extension_registry()->GetExtensionById( + last_loaded_extension_id(), extensions::ExtensionRegistry::ENABLED); + + ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText( + last_loaded_extension_id(), true); + + ExtensionAction* action = + ExtensionActionManager::Get(web_contents()->GetBrowserContext()) + ->GetExtensionAction(*dnr_extension); + + struct { + std::string frame_hostname; + std::string expected_badge_text; + bool has_referrer_header; + } test_cases[] = { + // zzz.com does not match any rules, but we should still display 0 as the + // badge text as the preference is on. + {"zzz.com", "0", false}, + // abc.com is blocked by a matching rule and should increment the badge + // text. + {"abc.com", "1", false}, + // def.com is redirected by a matching rule and should increment the badge + // text. + {"def.com", "2", false}, + // jkl.com matches with a removeHeaders rule, but has no headers. + // Therefore no action is taken and the badge text stays the same. + {"jkl.com", "2", false}, + // jkl.com matches with a removeHeaders rule and has a referrer header. + // Therefore the badge text should be incremented. + {"jkl.com", "3", true}, + // abcd.com matches both a block rule and an allow rule. Since the allow + // rule overrides the block rule, no action is taken and the badge text + // stays the same, + {"abcd.com", "3", false}, + }; + + ui_test_utils::NavigateToURL(browser(), page_url); + ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame())); + + // Verify that the badge text is 0 when navigation finishes. + int first_tab_id = ExtensionTabUtil::GetTabId(web_contents()); + EXPECT_EQ("0", action->GetDisplayBadgeText(first_tab_id)); + + for (const auto& test_case : test_cases) { + GURL url = test_case.has_referrer_header + ? get_url_with_referrer(test_case.frame_hostname) + : get_url_for_host(test_case.frame_hostname); + SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str())); + + navigate_frame(kFrameName1, url); + EXPECT_EQ(test_case.expected_badge_text, + action->GetDisplayBadgeText(first_tab_id)); + } + + std::string first_tab_badge_text = action->GetDisplayBadgeText(first_tab_id); + + const GURL second_tab_url = get_url_for_host("nomatch.com"); + ui_test_utils::NavigateToURLWithDisposition( + browser(), second_tab_url, WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + ASSERT_EQ(2, browser()->tab_strip_model()->count()); + ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1)); + + int second_tab_id = ExtensionTabUtil::GetTabId(web_contents()); + EXPECT_EQ("0", action->GetDisplayBadgeText(second_tab_id)); + + // Verify that the badge text for the first tab is unaffected. + EXPECT_EQ(first_tab_badge_text, action->GetDisplayBadgeText(first_tab_id)); +} + +// Test that the action matched badge text for an extension is visible in an +// incognito context if the extension is incognito enabled. +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, + ActionsMatchedCountAsBadgeTextIncognito) { + TestRule rule = CreateGenericRule(); + rule.condition->url_filter = "abc.com"; + rule.id = kMinValidID; + rule.condition->resource_types = std::vector<std::string>({"main_frame"}); + rule.action->type = "block"; + + std::vector<TestRule> rules({rule}); + ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules( + {rules}, "test_extension", {URLPattern::kAllUrlsPattern})); + + const ExtensionId extension_id = last_loaded_extension_id(); + util::SetIsIncognitoEnabled(extension_id, profile(), true /*enabled*/); + + ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText(extension_id, + true); + + Browser* incognito_browser = CreateIncognitoBrowser(); + ui_test_utils::NavigateToURL(incognito_browser, GURL("http://abc.com")); + + content::WebContents* incognito_contents = + incognito_browser->tab_strip_model()->GetActiveWebContents(); + + const Extension* dnr_extension = extension_registry()->GetExtensionById( + extension_id, extensions::ExtensionRegistry::ENABLED); + ExtensionAction* incognito_action = + ExtensionActionManager::Get(incognito_contents->GetBrowserContext()) + ->GetExtensionAction(*dnr_extension); + + // TODO(crbug.com/992251): This should be a "1" after the main-frame + // navigation case is fixed. + EXPECT_EQ("0", incognito_action->GetDisplayBadgeText( + ExtensionTabUtil::GetTabId(incognito_contents))); +} + +// Test that the actions matched badge text for an extension will be reset +// when a main-frame navigation finishes. +// TODO(crbug.com/992251): Edit this test to add more main-frame cases. +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, + ActionsMatchedCountAsBadgeTextMainFrame) { + auto get_url_for_host = [this](std::string hostname) { + return embedded_test_server()->GetURL(hostname, + "/pages_with_script/index.html"); + }; + + struct { + std::string url_filter; + int id; + int priority; + std::string action_type; + std::vector<std::string> resource_types; + base::Optional<std::string> redirect_url; + } rules_data[] = { + {"abc.com", 1, 1, "block", std::vector<std::string>({"script"}), + base::nullopt}, + {"def.com", 2, 1, "redirect", std::vector<std::string>({"main_frame"}), + get_url_for_host("abc.com").spec()}, + }; + + // Load the extension. + std::vector<TestRule> rules; + for (const auto& rule_data : rules_data) { + TestRule rule = CreateGenericRule(); + rule.condition->url_filter = rule_data.url_filter; + rule.id = rule_data.id; + rule.priority = rule_data.priority; + rule.condition->resource_types = rule_data.resource_types; + rule.action->type = rule_data.action_type; + rule.action->redirect.emplace(); + rule.action->redirect->url = rule_data.redirect_url; + rules.push_back(rule); + } + + ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules( + rules, "test_extension", {URLPattern::kAllUrlsPattern})); + + const Extension* dnr_extension = extension_registry()->GetExtensionById( + last_loaded_extension_id(), extensions::ExtensionRegistry::ENABLED); + + ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText( + last_loaded_extension_id(), true); + + ExtensionAction* action = + ExtensionActionManager::Get(web_contents()->GetBrowserContext()) + ->GetExtensionAction(*dnr_extension); + + struct { + std::string frame_hostname; + std::string expected_badge_text; + } test_cases[] = { + // The script on get_url_for_host("abc.com") matches with a rule and + // should increment the badge text. + {"abc.com", "1"}, + // No rules match, so the badge text should be 0 once navigation finishes. + {"nomatch.com", "0"}, + // The request to def.com will redirect to get_url_for_host("abc.com") and + // the script on abc.com should match with a rule. + {"def.com", "1"}, + }; + + int first_tab_id = ExtensionTabUtil::GetTabId(web_contents()); + for (const auto& test_case : test_cases) { + GURL url = get_url_for_host(test_case.frame_hostname); + SCOPED_TRACE(base::StringPrintf("Testing %s", url.spec().c_str())); + + ui_test_utils::NavigateToURL(browser(), url); + EXPECT_EQ(test_case.expected_badge_text, + action->GetDisplayBadgeText(first_tab_id)); + } +} + // Test fixture to verify that host permissions for the request url and the // request initiator are properly checked when redirecting requests. Loads an // example.com url with four sub-frames named frame_[1..4] from hosts
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc index 2d635a8..6899282 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api.cc +++ b/chrome/browser/extensions/api/downloads/downloads_api.cc
@@ -45,7 +45,6 @@ #include "chrome/browser/download/download_query.h" #include "chrome/browser/download/download_shelf.h" #include "chrome/browser/download/download_stats.h" -#include "chrome/browser/download/drag_download_item.h" #include "chrome/browser/extensions/chrome_extension_function_details.h" #include "chrome/browser/icon_loader.h" #include "chrome/browser/icon_manager.h" @@ -1495,36 +1494,6 @@ Respond(NoArguments()); } -DownloadsDragFunction::DownloadsDragFunction() {} - -DownloadsDragFunction::~DownloadsDragFunction() {} - -ExtensionFunction::ResponseAction DownloadsDragFunction::Run() { - std::unique_ptr<downloads::Drag::Params> params( - downloads::Drag::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - DownloadItem* download_item = GetDownload( - browser_context(), include_incognito_information(), params->download_id); - content::WebContents* web_contents = - dispatcher()->GetVisibleWebContents(); - std::string error; - if (InvalidId(download_item, &error) || - Fault(!web_contents, download_extension_errors::kInvisibleContext, - &error)) { - return RespondNow(Error(error)); - } - RecordApiFunctions(DOWNLOADS_FUNCTION_DRAG); - gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath( - download_item->GetTargetFilePath(), IconLoader::NORMAL); - gfx::NativeView view = web_contents->GetNativeView(); - { - // Enable nested tasks during DnD, while |DragDownload()| blocks. - base::MessageLoopCurrent::ScopedNestableTaskAllower allow; - DragDownloadItem(download_item, icon, view); - } - return RespondNow(NoArguments()); -} - DownloadsSetShelfEnabledFunction::DownloadsSetShelfEnabledFunction() {} DownloadsSetShelfEnabledFunction::~DownloadsSetShelfEnabledFunction() {}
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.h b/chrome/browser/extensions/api/downloads/downloads_api.h index 81d3cc6..1f39ba0 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api.h +++ b/chrome/browser/extensions/api/downloads/downloads_api.h
@@ -275,19 +275,6 @@ DISALLOW_COPY_AND_ASSIGN(DownloadsSetShelfEnabledFunction); }; -class DownloadsDragFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("downloads.drag", DOWNLOADS_DRAG) - DownloadsDragFunction(); - ResponseAction Run() override; - - protected: - ~DownloadsDragFunction() override; - - private: - DISALLOW_COPY_AND_ASSIGN(DownloadsDragFunction); -}; - class DownloadsGetFileIconFunction : public ChromeAsyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("downloads.getFileIcon", DOWNLOADS_GETFILEICON)
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc index 3854457..0e2a4d3 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc +++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -1232,14 +1232,6 @@ RunFunction(new DownloadsShowDefaultFolderFunction(), DownloadItemIdAsArgList(item.get())); } - -IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, - DownloadsDragFunction) { - ScopedCancellingItem item(CreateFirstSlowTestDownload()); - ASSERT_TRUE(item.get()); - - RunFunction(new DownloadsDragFunction(), DownloadItemIdAsArgList(item.get())); -} #endif @@ -2329,14 +2321,14 @@ " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); - ASSERT_TRUE(WaitFor(downloads::OnChanged::kEventName, - base::StringPrintf( - "[{\"id\": %d," - " \"filename\": {" - " \"previous\": \"\"," - " \"current\": \"%s\"}}]", - result_id, - GetFilename("file.txt").c_str()))); + // File will be renamed to file.html due to its mime type. + ASSERT_TRUE( + WaitFor(downloads::OnChanged::kEventName, + base::StringPrintf("[{\"id\": %d," + " \"filename\": {" + " \"previous\": \"\"," + " \"current\": \"%s\"}}]", + result_id, GetFilename("file.html").c_str()))); ASSERT_TRUE(WaitFor(downloads::OnChanged::kEventName, base::StringPrintf( "[{\"id\": %d," @@ -3008,6 +3000,8 @@ EXPECT_FALSE(determine_result.get()); // No return value. } +// Tests that overriding a safe file extension to a dangerous extension will not +// trigger the dangerous prompt and will not change the extension. IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_DangerousOverride) { @@ -3047,7 +3041,7 @@ ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); - // Respond to the onDeterminingFilename. + // Respond to the onDeterminingFilename with a dangerous extension. std::string error; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( current_browser()->profile(), false, GetExtensionId(), result_id, @@ -3056,12 +3050,68 @@ EXPECT_EQ("", error); ASSERT_TRUE(WaitFor(downloads::OnChanged::kEventName, - base::StringPrintf( - "[{\"id\": %d," - " \"danger\": {" - " \"previous\":\"safe\"," - " \"current\":\"file\"}}]", - result_id))); + base::StringPrintf("[{\"id\": %d," + " \"state\": {" + " \"previous\": \"in_progress\"," + " \"current\": \"complete\"}}]", + result_id))); + EXPECT_EQ(downloads_directory().AppendASCII("overridden.txt"), + item->GetTargetFilePath()); +} + +// Tests that overriding a dangerous file extension to a safe extension will +// trigger the dangerous prompt and will not change the extension. +IN_PROC_BROWSER_TEST_F( + DownloadExtensionTest, + DownloadExtensionTest_OnDeterminingFilename_SafeOverride) { + GoOnTheRecord(); + LoadExtension("downloads_split"); + AddFilenameDeterminer(); + + std::string download_url = "data:application/x-shockwave-flash,"; + // Start downloading a file. + std::unique_ptr<base::Value> result(RunFunctionAndReturnResult( + new DownloadsDownloadFunction(), + base::StringPrintf("[{\"url\": \"%s\"}]", download_url.c_str()))); + ASSERT_TRUE(result.get()); + int result_id = -1; + ASSERT_TRUE(result->GetAsInteger(&result_id)); + DownloadItem* item = GetCurrentManager()->GetDownload(result_id); + ASSERT_TRUE(item); + ScopedCancellingItem canceller(item); + ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); + + ASSERT_TRUE(WaitFor( + downloads::OnCreated::kEventName, + base::StringPrintf("[{\"danger\": \"safe\"," + " \"incognito\": false," + " \"id\": %d," + " \"mime\": \"application/x-shockwave-flash\"," + " \"paused\": false," + " \"url\": \"%s\"}]", + result_id, download_url.c_str()))); + ASSERT_TRUE(WaitFor(downloads::OnDeterminingFilename::kEventName, + base::StringPrintf("[{\"id\": %d," + " \"filename\":\"download.swf\"}]", + result_id))); + ASSERT_TRUE(item->GetTargetFilePath().empty()); + ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); + + // Respond to the onDeterminingFilename with a safe extension. + std::string error; + ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( + current_browser()->profile(), false, GetExtensionId(), result_id, + base::FilePath(FILE_PATH_LITERAL("overridden.txt")), + downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); + EXPECT_EQ("", error); + + // Dangerous download prompt will be shown. + ASSERT_TRUE(WaitFor(downloads::OnChanged::kEventName, + base::StringPrintf("[{\"id\": %d, " + " \"danger\": {" + " \"previous\": \"safe\"," + " \"current\": \"file\"}}]", + result_id))); item->ValidateDangerousDownload(); ASSERT_TRUE(WaitFor(downloads::OnChanged::kEventName, @@ -3071,6 +3121,7 @@ " \"previous\":\"file\"," " \"current\":\"accepted\"}}]", result_id))); + ASSERT_TRUE(WaitFor(downloads::OnChanged::kEventName, base::StringPrintf( "[{\"id\": %d," @@ -4342,7 +4393,8 @@ LoadExtension("downloads_split"); std::unique_ptr<base::Value> result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), - "[{\"url\": \"data:,\", \"filename\": \"dangerous.swf\"}]")); + "[{\"url\": \"data:application/x-shockwave-flash,\", \"filename\": " + "\"dangerous.swf\"}]")); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id));
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc index 5af703b..9d60fa3d 100644 --- a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc +++ b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
@@ -209,7 +209,8 @@ // Test that we received the changes. ExtensionAction* action = GetBrowserAction(*extension); ASSERT_EQ("Modified", action->GetTitle(ExtensionAction::kDefaultTabId)); - ASSERT_EQ("badge", action->GetBadgeText(ExtensionAction::kDefaultTabId)); + ASSERT_EQ("badge", + action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId)); ASSERT_EQ(SkColorSetARGB(255, 255, 255, 255), action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); @@ -701,7 +702,8 @@ extensions::ProcessManager::Get(browser()->profile()); ASSERT_TRUE(manager->GetBackgroundHostForExtension(extension->id())); ExtensionAction* action = GetBrowserAction(*extension); - ASSERT_EQ("", action->GetBadgeText(ExtensionAction::kDefaultTabId)); + ASSERT_EQ("", + action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId)); content::WindowedNotificationObserver host_destroyed_observer( extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, @@ -715,7 +717,8 @@ // and the badge text has been set. host_destroyed_observer.Wait(); ASSERT_FALSE(manager->GetBackgroundHostForExtension(extension->id())); - ASSERT_EQ("X", action->GetBadgeText(ExtensionAction::kDefaultTabId)); + ASSERT_EQ("X", + action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId)); } IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BadgeBackgroundColor) {
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_browsertest.cc b/chrome/browser/extensions/api/extension_action/browser_action_browsertest.cc index 2c4893d0..5027f9a 100644 --- a/chrome/browser/extensions/api/extension_action/browser_action_browsertest.cc +++ b/chrome/browser/extensions/api/extension_action/browser_action_browsertest.cc
@@ -94,7 +94,7 @@ // If the extension hasn't already set the badge text, then we should wait for // it to do so. - if (extension_action->GetBadgeText(0) != "Hello") { + if (extension_action->GetExplicitlySetBadgeText(0) != "Hello") { ExtensionTestMessageListener listener("Badge Text Set", false /* won't send custom reply */); ASSERT_TRUE(listener.WaitUntilSatisfied());
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc index 74a6724..c553738 100644 --- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc +++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -467,8 +467,10 @@ ExtensionFunction::ResponseAction ExtensionActionGetBadgeTextFunction::RunExtensionAction() { - return RespondNow(OneArgument( - std::make_unique<base::Value>(extension_action_->GetBadgeText(tab_id_)))); + // TODO(crbug.com/990224): Return a placeholder value if the extension has + // called setActionCountAsBadgeText(true). + return RespondNow(OneArgument(std::make_unique<base::Value>( + extension_action_->GetExplicitlySetBadgeText(tab_id_)))); } ExtensionFunction::ResponseAction
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc index c90241d..495096e 100644 --- a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc +++ b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
@@ -889,7 +889,7 @@ ValuePair custom_badge_text2{"custom badge2", "'custom badge2'"}; auto get_badge_text = [](ExtensionAction* action, int tab_id) { - return action->GetBadgeText(tab_id); + return action->GetExplicitlySetBadgeText(tab_id); }; ActionTestHelper badge_text_helper(kApiName, "setBadgeText", "getBadgeText",
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc index 125a777..97da57de 100644 --- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc +++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
@@ -35,7 +35,9 @@ #include "components/language/core/browser/pref_names.h" #include "components/language/core/common/language_util.h" #include "components/language/core/common/locale_util.h" +#include "components/spellcheck/browser/spellcheck_platform.h" #include "components/spellcheck/common/spellcheck_common.h" +#include "components/spellcheck/common/spellcheck_features.h" #include "components/translate/core/browser/translate_download_manager.h" #include "components/translate/core/browser/translate_prefs.h" #include "third_party/icu/source/i18n/unicode/coll.h" @@ -495,6 +497,12 @@ SpellcheckServiceFactory::GetForContext(browser_context()); bool success = service->GetCustomDictionary()->AddWord(params->word); +#if BUILDFLAG(USE_BROWSER_SPELLCHECKER) + if (spellcheck::UseBrowserSpellChecker()) { + spellcheck_platform::AddWord(base::UTF8ToUTF16(params->word)); + } +#endif + return RespondNow(OneArgument(std::make_unique<base::Value>(success))); } @@ -514,6 +522,12 @@ SpellcheckServiceFactory::GetForContext(browser_context()); bool success = service->GetCustomDictionary()->RemoveWord(params->word); +#if BUILDFLAG(USE_BROWSER_SPELLCHECKER) + if (spellcheck::UseBrowserSpellChecker()) { + spellcheck_platform::RemoveWord(base::UTF8ToUTF16(params->word)); + } +#endif + return RespondNow(OneArgument(std::make_unique<base::Value>(success))); }
diff --git a/chrome/browser/extensions/extension_action.cc b/chrome/browser/extensions/extension_action.cc index 1d76297d..a0d748c7 100644 --- a/chrome/browser/extensions/extension_action.cc +++ b/chrome/browser/extensions/extension_action.cc
@@ -214,6 +214,7 @@ title_.erase(tab_id); icon_.erase(tab_id); badge_text_.erase(tab_id); + dnr_action_count_.erase(tab_id); badge_text_color_.erase(tab_id); badge_background_color_.erase(tab_id); is_visible_.erase(tab_id); @@ -251,6 +252,19 @@ return placeholder_icon_image_; } +std::string ExtensionAction::GetDisplayBadgeText(int tab_id) const { + // Tab specific badge text set by an extension overrides the automatically set + // action count. + if (HasBadgeText(tab_id)) + return GetExplicitlySetBadgeText(tab_id); + if (HasDNRActionCount(tab_id)) + return base::NumberToString(GetDNRActionCount(tab_id)); + + // Return the default badge text or an empty string if there is no badge text + // set for this tab. + return GetExplicitlySetBadgeText(kDefaultTabId); +} + bool ExtensionAction::HasPopupUrl(int tab_id) const { return HasValue(popup_url_, tab_id); } @@ -279,6 +293,10 @@ return HasValue(icon_, tab_id); } +bool ExtensionAction::HasDNRActionCount(int tab_id) const { + return HasValue(dnr_action_count_, tab_id); +} + void ExtensionAction::SetDefaultIconForTest( std::unique_ptr<ExtensionIconSet> default_icon) { default_icon_ = std::move(default_icon);
diff --git a/chrome/browser/extensions/extension_action.h b/chrome/browser/extensions/extension_action.h index b2d4893..27362cf 100644 --- a/chrome/browser/extensions/extension_action.h +++ b/chrome/browser/extensions/extension_action.h
@@ -118,8 +118,9 @@ void SetBadgeText(int tab_id, const std::string& text) { SetValue(&badge_text_, tab_id, text); } - // Get the badge text for a tab, or the default if no badge text was set. - std::string GetBadgeText(int tab_id) const { + // Get the badge text that has been set using SetBadgeText for a tab, or the + // default if no badge text was set. + std::string GetExplicitlySetBadgeText(int tab_id) const { return GetValue(&badge_text_, tab_id); } @@ -143,6 +144,23 @@ return GetValue(&badge_background_color_, tab_id); } + // Set this ExtensionAction's DNR matched action count on a specific tab. + void SetDNRActionCount(int tab_id, int action_count) { + SetValue(&dnr_action_count_, tab_id, action_count); + } + // Get this ExtensionAction's DNR matched action count on a specific tab. + // Returns -1 if no entry is found. + int GetDNRActionCount(int tab_id) const { + return GetValue(&dnr_action_count_, tab_id); + } + + // Get the badge text displayed for a tab, calculated based on both + // |badge_text_| and |dnr_action_count_|. Returns in order of priority: + // - GetExplicitlySetBadgeText(tab_id) if it exists for the |tab_id| + // - GetDNRActionCount(tab_id) + // - The default badge text, if set, otherwise: an empty string. + std::string GetDisplayBadgeText(int tab_id) const; + // Set this action's badge visibility on a specific tab. Returns true if // the visibility has changed. bool SetIsVisible(int tab_id, bool value); @@ -196,6 +214,7 @@ bool HasBadgeTextColor(int tab_id) const; bool HasIsVisible(int tab_id) const; bool HasIcon(int tab_id) const; + bool HasDNRActionCount(int tab_id) const; extensions::IconImage* default_icon_image() { return default_icon_image_.get(); @@ -283,6 +302,11 @@ // images that are currently in effect std::map<int, std::map<int, std::vector<gfx::Image> > > declarative_icon_; + // Maps tab_id to the number of actions taken based on declarative net request + // rule matches on incoming requests. Overrides the default |badge_text_| for + // this extension if it has called chrome.setActionCountAsBadgeText(true). + std::map<int, int> dnr_action_count_; + // ExtensionIconSet containing paths to bitmaps from which default icon's // image representations will be selected. std::unique_ptr<ExtensionIconSet> default_icon_;
diff --git a/chrome/browser/extensions/extension_action_runner.cc b/chrome/browser/extensions/extension_action_runner.cc index bcfe702..9608fa90 100644 --- a/chrome/browser/extensions/extension_action_runner.cc +++ b/chrome/browser/extensions/extension_action_runner.cc
@@ -19,6 +19,7 @@ #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" #include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/extensions/extension_action_manager.h" +#include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/extensions/scripting_permissions_modifier.h" #include "chrome/browser/extensions/tab_helper.h" @@ -35,6 +36,8 @@ #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" +#include "extensions/browser/api/declarative_net_request/action_tracker.h" +#include "extensions/browser/api/declarative_net_request/rules_monitor_service.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/extension.h" #include "extensions/common/extension_messages.h" @@ -521,11 +524,35 @@ // run". ExtensionActionAPI::Get(browser_context_) ->ClearAllValuesForTab(web_contents()); + + declarative_net_request::RulesMonitorService* rules_monitor_service = + declarative_net_request::RulesMonitorService::Get(browser_context_); + + // |rules_monitor_service| can be null for some unit tests. + if (rules_monitor_service) { + declarative_net_request::ActionTracker& action_tracker = + rules_monitor_service->ruleset_manager()->action_tracker(); + + int tab_id = ExtensionTabUtil::GetTabId(web_contents()); + action_tracker.ResetActionCountForTab(tab_id); + } } void ExtensionActionRunner::WebContentsDestroyed() { ExtensionActionAPI::Get(browser_context_) ->ClearAllValuesForTab(web_contents()); + + declarative_net_request::RulesMonitorService* rules_monitor_service = + declarative_net_request::RulesMonitorService::Get(browser_context_); + + // |rules_monitor_service| can be null for some unit tests. + if (rules_monitor_service) { + declarative_net_request::ActionTracker& action_tracker = + rules_monitor_service->ruleset_manager()->action_tracker(); + + int tab_id = ExtensionTabUtil::GetTabId(web_contents()); + action_tracker.ClearTabData(tab_id); + } } void ExtensionActionRunner::OnExtensionUnloaded(
diff --git a/chrome/browser/extensions/extension_action_storage_manager.cc b/chrome/browser/extensions/extension_action_storage_manager.cc index 33cdc582..d3ae2ae 100644 --- a/chrome/browser/extensions/extension_action_storage_manager.cc +++ b/chrome/browser/extensions/extension_action_storage_manager.cc
@@ -167,7 +167,8 @@ dict->SetString(kPopupUrlStorageKey, action->GetPopupUrl(kDefaultTabId).spec()); dict->SetString(kTitleStorageKey, action->GetTitle(kDefaultTabId)); - dict->SetString(kBadgeTextStorageKey, action->GetBadgeText(kDefaultTabId)); + dict->SetString(kBadgeTextStorageKey, + action->GetExplicitlySetBadgeText(kDefaultTabId)); dict->SetString( kBadgeBackgroundColorStorageKey, SkColorToRawString(action->GetBadgeBackgroundColor(kDefaultTabId)));
diff --git a/chrome/browser/extensions/extension_action_unittest.cc b/chrome/browser/extensions/extension_action_unittest.cc index da1f427..6db6bee 100644 --- a/chrome/browser/extensions/extension_action_unittest.cc +++ b/chrome/browser/extensions/extension_action_unittest.cc
@@ -79,17 +79,37 @@ TEST(ExtensionActionTest, Badge) { std::unique_ptr<ExtensionAction> action = CreateAction(ActionInfo(ActionInfo::TYPE_PAGE)); - ASSERT_EQ("", action->GetBadgeText(1)); + ASSERT_EQ("", action->GetExplicitlySetBadgeText(1)); action->SetBadgeText(ExtensionAction::kDefaultTabId, "foo"); - ASSERT_EQ("foo", action->GetBadgeText(1)); - ASSERT_EQ("foo", action->GetBadgeText(100)); + ASSERT_EQ("foo", action->GetExplicitlySetBadgeText(1)); + ASSERT_EQ("foo", action->GetExplicitlySetBadgeText(100)); action->SetBadgeText(100, "bar"); - ASSERT_EQ("foo", action->GetBadgeText(1)); - ASSERT_EQ("bar", action->GetBadgeText(100)); + ASSERT_EQ("foo", action->GetExplicitlySetBadgeText(1)); + ASSERT_EQ("bar", action->GetExplicitlySetBadgeText(100)); action->SetBadgeText(ExtensionAction::kDefaultTabId, "baz"); - ASSERT_EQ("baz", action->GetBadgeText(1)); + ASSERT_EQ("baz", action->GetExplicitlySetBadgeText(1)); action->ClearAllValuesForTab(100); - ASSERT_EQ("baz", action->GetBadgeText(100)); + ASSERT_EQ("baz", action->GetExplicitlySetBadgeText(100)); +} + +TEST(ExtensionActionTest, DisplayBadgeText) { + constexpr int kFirstTabId = 1; + constexpr int kSecondTabId = 2; + + std::unique_ptr<ExtensionAction> action = + CreateAction(ActionInfo(ActionInfo::TYPE_PAGE)); + ASSERT_EQ("", action->GetDisplayBadgeText(kFirstTabId)); + action->SetDNRActionCount(kFirstTabId, 10 /* action_count */); + ASSERT_EQ("10", action->GetDisplayBadgeText(kFirstTabId)); + action->SetBadgeText(ExtensionAction::kDefaultTabId, "foo"); + ASSERT_EQ("10", action->GetDisplayBadgeText(kFirstTabId)); + ASSERT_EQ("foo", action->GetDisplayBadgeText(kSecondTabId)); + action->SetBadgeText(kFirstTabId, "bar"); + ASSERT_EQ("bar", action->GetDisplayBadgeText(kFirstTabId)); + action->SetDNRActionCount(kFirstTabId, 100 /* action_count */); + ASSERT_EQ("bar", action->GetDisplayBadgeText(kFirstTabId)); + action->ClearAllValuesForTab(kFirstTabId); + ASSERT_EQ("foo", action->GetDisplayBadgeText(kFirstTabId)); } TEST(ExtensionActionTest, BadgeTextColor) {
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc index c1e9a9a..fbab482 100644 --- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc +++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -10,13 +10,16 @@ #include "base/bind.h" #include "base/logging.h" #include "base/metrics/histogram_macros_local.h" +#include "base/rand_util.h" #include "base/sequenced_task_runner.h" #include "base/task/post_task.h" #include "base/task_runner_util.h" +#include "base/time/default_clock.h" #include "components/optimization_guide/bloom_filter.h" #include "components/optimization_guide/hint_cache.h" #include "components/optimization_guide/hint_cache_store.h" #include "components/optimization_guide/hints_component_util.h" +#include "components/optimization_guide/hints_fetcher.h" #include "components/optimization_guide/hints_processing_util.h" #include "components/optimization_guide/optimization_filter.h" #include "components/optimization_guide/optimization_guide_features.h" @@ -27,6 +30,7 @@ #include "components/prefs/pref_service.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_handle.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" namespace { @@ -35,6 +39,24 @@ // will have a newer version than it. constexpr char kManualConfigComponentVersion[] = "0.0.0"; +// Delay between retries on failed fetch and store of hints from the remote +// Optimization Guide Service. +constexpr base::TimeDelta kFetchRetryDelay = base::TimeDelta::FromMinutes(15); + +// Delay until successfully fetched hints should be updated by requesting from +// the remote Optimization Guide Service. +constexpr base::TimeDelta kUpdateFetchedHintsDelay = + base::TimeDelta::FromHours(24); + +// Provides a random time delta in seconds between |kFetchRandomMinDelay| and +// |kFetchRandomMaxDelay|. +base::TimeDelta RandomFetchDelay() { + constexpr int kFetchRandomMinDelaySecs = 30; + constexpr int kFetchRandomMaxDelaySecs = 60; + return base::TimeDelta::FromSeconds( + base::RandInt(kFetchRandomMinDelaySecs, kFetchRandomMaxDelaySecs)); +} + void MaybeRunUpdateClosure(base::OnceClosure update_closure) { if (update_closure) std::move(update_closure).Run(); @@ -79,7 +101,8 @@ const base::FilePath& profile_path, PrefService* pref_service, leveldb_proto::ProtoDatabaseProvider* database_provider, - optimization_guide::TopHostProvider* top_host_provider) + optimization_guide::TopHostProvider* top_host_provider, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) : optimization_guide_service_(optimization_guide_service), background_task_runner_(base::CreateSequencedTaskRunnerWithTraits( {base::ThreadPool(), base::MayBlock(), @@ -91,7 +114,9 @@ profile_path, pref_service_, background_task_runner_))), - top_host_provider_(top_host_provider) { + top_host_provider_(top_host_provider), + url_loader_factory_(url_loader_factory), + clock_(base::DefaultClock::GetInstance()) { DCHECK(optimization_guide_service_); hint_cache_->Initialize( optimization_guide::switches::ShouldPurgeHintCacheStoreOnStartup(), @@ -312,17 +337,121 @@ next_update_closure_ = std::move(next_update_closure); } -void OptimizationGuideHintsManager::MaybeScheduleHintsFetch() { - bool hints_fetching_allowed = - optimization_guide::features::IsHintsFetchingEnabled() && - top_host_provider_; - // This local histogram is only used for testing and will be removed when the - // actual implementation to schedule hints fetches is in place. - LOCAL_HISTOGRAM_BOOLEAN("OptimizationGuide.HintsFetching.Allowed", - hints_fetching_allowed); +void OptimizationGuideHintsManager::SetClockForTesting( + const base::Clock* clock) { + clock_ = clock; +} - // TODO(crbug/969558): Implement this to actually schedule a hints fetch and - // remove above local histogram. +void OptimizationGuideHintsManager::SetHintsFetcherForTesting( + std::unique_ptr<optimization_guide::HintsFetcher> hints_fetcher) { + hints_fetcher_ = std::move(hints_fetcher); +} + +void OptimizationGuideHintsManager::MaybeScheduleHintsFetch() { + if (!optimization_guide::features::IsHintsFetchingEnabled() || + !top_host_provider_) { + return; + } + + if (optimization_guide::switches::ShouldOverrideFetchHintsTimer()) { + SetLastHintsFetchAttemptTime(clock_->Now()); + FetchHints(); + } else { + ScheduleHintsFetch(); + } +} + +void OptimizationGuideHintsManager::ScheduleHintsFetch() { + DCHECK(!hints_fetch_timer_.IsRunning()); + + const base::TimeDelta time_until_update_time = + hint_cache_->FetchedHintsUpdateTime() - clock_->Now(); + const base::TimeDelta time_until_retry = + GetLastHintsFetchAttemptTime() + kFetchRetryDelay - clock_->Now(); + base::TimeDelta fetcher_delay; + if (time_until_update_time <= base::TimeDelta() && + time_until_retry <= base::TimeDelta()) { + // Fetched hints in the store should be updated and an attempt has not + // been made in last |kFetchRetryDelay|. + SetLastHintsFetchAttemptTime(clock_->Now()); + hints_fetch_timer_.Start(FROM_HERE, RandomFetchDelay(), this, + &OptimizationGuideHintsManager::FetchHints); + } else { + if (time_until_update_time >= base::TimeDelta()) { + // If the fetched hints in the store are still up-to-date, set a timer + // for when the hints need to be updated. + fetcher_delay = time_until_update_time; + } else { + // Otherwise, hints need to be updated but an attempt was made in last + // |kFetchRetryDelay|. Schedule the timer for after the retry + // delay. + fetcher_delay = time_until_retry; + } + hints_fetch_timer_.Start( + FROM_HERE, fetcher_delay, this, + &OptimizationGuideHintsManager::ScheduleHintsFetch); + } +} + +void OptimizationGuideHintsManager::FetchHints() { + DCHECK(top_host_provider_); + + size_t max_hints_to_fetch = optimization_guide::features:: + MaxHostsForOptimizationGuideServiceHintsFetch(); + std::vector<std::string> top_hosts = + top_host_provider_->GetTopHosts(max_hints_to_fetch); + if (top_hosts.empty()) + return; + DCHECK_GE(max_hints_to_fetch, top_hosts.size()); + + if (!hints_fetcher_) { + hints_fetcher_ = std::make_unique<optimization_guide::HintsFetcher>( + url_loader_factory_, + optimization_guide::features::GetOptimizationGuideServiceURL()); + } + hints_fetcher_->FetchOptimizationGuideServiceHints( + top_hosts, base::BindOnce(&OptimizationGuideHintsManager::OnHintsFetched, + ui_weak_ptr_factory_.GetWeakPtr())); +} + +void OptimizationGuideHintsManager::OnHintsFetched( + base::Optional<std::unique_ptr<optimization_guide::proto::GetHintsResponse>> + get_hints_response) { + if (get_hints_response) { + hint_cache_->UpdateFetchedHints( + std::move(*get_hints_response), + clock_->Now() + kUpdateFetchedHintsDelay, + base::BindOnce(&OptimizationGuideHintsManager::OnFetchedHintsStored, + ui_weak_ptr_factory_.GetWeakPtr())); + } else { + // The fetch did not succeed so we will schedule to retry the fetch in + // after delaying for |kFetchRetryDelay| + // TODO(mcrouse): When the store is refactored from closures, the timer will + // be scheduled on failure of the store instead. + hints_fetch_timer_.Start( + FROM_HERE, kFetchRetryDelay, this, + &OptimizationGuideHintsManager::ScheduleHintsFetch); + } +} + +void OptimizationGuideHintsManager::OnFetchedHintsStored() { + hints_fetch_timer_.Stop(); + hints_fetch_timer_.Start( + FROM_HERE, hint_cache_->FetchedHintsUpdateTime() - clock_->Now(), this, + &OptimizationGuideHintsManager::ScheduleHintsFetch); +} + +base::Time OptimizationGuideHintsManager::GetLastHintsFetchAttemptTime() const { + return base::Time::FromDeltaSinceWindowsEpoch( + base::TimeDelta::FromMicroseconds(pref_service_->GetInt64( + optimization_guide::prefs::kHintsFetcherLastFetchAttempt))); +} + +void OptimizationGuideHintsManager::SetLastHintsFetchAttemptTime( + base::Time last_attempt_time) { + pref_service_->SetInt64( + optimization_guide::prefs::kHintsFetcherLastFetchAttempt, + last_attempt_time.ToDeltaSinceWindowsEpoch().InMicroseconds()); } void OptimizationGuideHintsManager::LoadHintForNavigation(
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h index 17e6b67..0f3e274 100644 --- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h +++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
@@ -11,10 +11,13 @@ #include <vector> #include "base/callback_forward.h" +#include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/sequenced_task_runner.h" #include "base/synchronization/lock.h" +#include "base/time/clock.h" +#include "base/timer/timer.h" #include "components/optimization_guide/hints_component_info.h" #include "components/optimization_guide/optimization_guide_service_observer.h" #include "components/optimization_guide/proto/hints.pb.h" @@ -31,9 +34,14 @@ class ProtoDatabaseProvider; } // namespace leveldb_proto +namespace network { +class SharedURLLoaderFactory; +} // namespace network + namespace optimization_guide { class HintCache; class HintUpdateData; +class HintsFetcher; class OptimizationFilter; class OptimizationGuideService; class TopHostProvider; @@ -49,7 +57,8 @@ const base::FilePath& profile_path, PrefService* pref_service, leveldb_proto::ProtoDatabaseProvider* database_provider, - optimization_guide::TopHostProvider* top_host_provider); + optimization_guide::TopHostProvider* top_host_provider, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); ~OptimizationGuideHintsManager() override; @@ -87,6 +96,18 @@ bool HasLoadedOptimizationFilter( optimization_guide::proto::OptimizationType optimization_type); + // Overrides |hints_fetcher_| for testing. + void SetHintsFetcherForTesting( + std::unique_ptr<optimization_guide::HintsFetcher> hints_fetcher); + + // Returns the current hints fetcher. + optimization_guide::HintsFetcher* hints_fetcher() const { + return hints_fetcher_.get(); + } + + // Overrides |clock_| for testing. + void SetClockForTesting(const base::Clock* clock); + private: // Processes the hints component. // @@ -123,9 +144,36 @@ void OnComponentHintsUpdated(base::OnceClosure update_closure, bool hints_updated) const; - // Method to request new hints for user's sites. + // Method to decide whether to fetch new hints for user's top sites and + // proceeds to schedule the fetch. void MaybeScheduleHintsFetch(); + // Schedules |hints_fetch_timer_| to fire based on: + // 1. The update time for the fetched hints in the store and + // 2. The last time a fetch attempt was made. + void ScheduleHintsFetch(); + + // Called to make a request to fetch hints from the remote Optimization Guide + // Service. + void FetchHints(); + + // Called when the hints have been fetched from the remote Optimization Guide + // Service and are ready for parsing. + void OnHintsFetched( + base::Optional< + std::unique_ptr<optimization_guide::proto::GetHintsResponse>> + get_hints_response); + + // Called when the fetched hints have been stored in |hint_cache| and are + // ready to be used. + void OnFetchedHintsStored(); + + // Returns the time when a hints fetch request was last attempted. + base::Time GetLastHintsFetchAttemptTime() const; + + // Sets the time when a hints fetch was last attempted to |last_attempt_time|. + void SetLastHintsFetchAttemptTime(base::Time last_attempt_time); + // Called when the request to load a hint has completed. void OnHintLoaded(base::OnceClosure callback, const optimization_guide::proto::Hint* loaded_hint) const; @@ -169,9 +217,25 @@ // fetched from the remote Optimization Guide Service. std::unique_ptr<optimization_guide::HintCache> hint_cache_; + // The fetcher that handles making requests to update hints from the remote + // Optimization Guide Service. + std::unique_ptr<optimization_guide::HintsFetcher> hints_fetcher_; + // The top host provider that can be queried. Not owned. optimization_guide::TopHostProvider* top_host_provider_ = nullptr; + // The URL loader factory used for fetching hints from the remote Optimization + // Guide Service. + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; + + // The timer used to schedule fetching hints from the remote Optimization + // Guide Service. + base::OneShotTimer hints_fetch_timer_; + + // The clock used to schedule fetching from the remote Optimization Guide + // Service. + const base::Clock* clock_; + // Used in testing to subscribe to an update event in this class. base::OnceClosure next_update_closure_;
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc index e129ec87..1602ffc9 100644 --- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc +++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/optimization_guide/optimization_guide_hints_manager.h" #include <string> +#include <utility> #include "base/base64.h" #include "base/command_line.h" @@ -12,19 +13,30 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "components/optimization_guide/bloom_filter.h" -#include "components/optimization_guide/command_line_top_host_provider.h" #include "components/optimization_guide/hints_component_util.h" +#include "components/optimization_guide/hints_fetcher.h" #include "components/optimization_guide/optimization_guide_features.h" #include "components/optimization_guide/optimization_guide_prefs.h" #include "components/optimization_guide/optimization_guide_service.h" #include "components/optimization_guide/optimization_guide_switches.h" #include "components/optimization_guide/proto_database_provider_test_base.h" +#include "components/optimization_guide/top_host_provider.h" #include "components/prefs/testing_pref_service.h" #include "content/public/test/mock_navigation_handle.h" #include "content/public/test/test_browser_thread_bundle.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" namespace { +// Retry delay is 16 minutes to allow for kFetchRetryDelaySecs + +// kFetchRandomMaxDelaySecs to pass. +constexpr int kTestFetchRetryDelaySecs = 60 * 16; +constexpr int kUpdateFetchHintsTimeSecs = 24 * 60 * 60; // 24 hours. + const int kBlackBlacklistBloomFilterNumHashFunctions = 7; const int kBlackBlacklistBloomFilterNumBits = 511; @@ -53,6 +65,22 @@ blacklist_proto->set_allocated_bloom_filter(bloom_filter_proto.release()); } +std::unique_ptr<optimization_guide::proto::GetHintsResponse> BuildHintsResponse( + std::vector<std::string> hosts) { + std::unique_ptr<optimization_guide::proto::GetHintsResponse> + get_hints_response = + std::make_unique<optimization_guide::proto::GetHintsResponse>(); + + for (const auto& host : hosts) { + optimization_guide::proto::Hint* hint = get_hints_response->add_hints(); + hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX); + hint->set_key(host); + optimization_guide::proto::PageHint* page_hint = hint->add_page_hints(); + page_hint->set_page_pattern("page pattern"); + } + return get_hints_response; +} + } // namespace class TestOptimizationGuideService @@ -82,6 +110,57 @@ bool remove_observer_called_ = false; }; +// A mock class implementation of TopHostProvider. +class MockTopHostProvider : public optimization_guide::TopHostProvider { + public: + MOCK_METHOD1(GetTopHosts, std::vector<std::string>(size_t max_sites)); +}; + +enum class HintsFetcherEndState { + kFetchFailed = 0, + kFetchSuccessWithHints = 1, + kFetchSuccessWithNoHints = 2, +}; + +// A mock class implementation of HintsFetcher. +class TestHintsFetcher : public optimization_guide::HintsFetcher { + using HintsFetcher::FetchOptimizationGuideServiceHints; + + public: + TestHintsFetcher( + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + GURL optimization_guide_service_url, + HintsFetcherEndState fetch_state) + : HintsFetcher(url_loader_factory, optimization_guide_service_url), + fetch_state_(fetch_state) {} + + bool FetchOptimizationGuideServiceHints( + const std::vector<std::string>& hosts, + optimization_guide::HintsFetchedCallback hints_fetched_callback) + override { + switch (fetch_state_) { + case HintsFetcherEndState::kFetchFailed: + std::move(hints_fetched_callback).Run(base::nullopt); + return false; + case HintsFetcherEndState::kFetchSuccessWithHints: + hints_fetched_ = true; + std::move(hints_fetched_callback).Run(BuildHintsResponse({"host.com"})); + return true; + case HintsFetcherEndState::kFetchSuccessWithNoHints: + hints_fetched_ = true; + std::move(hints_fetched_callback).Run(BuildHintsResponse({})); + return true; + } + return true; + } + + bool hints_fetched() { return hints_fetched_; } + + private: + bool hints_fetched_ = false; + HintsFetcherEndState fetch_state_; +}; + class OptimizationGuideHintsManagerTest : public optimization_guide::ProtoDatabaseProviderTestBase { public: @@ -109,9 +188,14 @@ pref_service_ = std::make_unique<TestingPrefServiceSimple>(); optimization_guide::prefs::RegisterProfilePrefs(pref_service_->registry()); + url_loader_factory_ = + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( + &test_url_loader_factory_); + hints_manager_ = std::make_unique<OptimizationGuideHintsManager>( optimization_guide_service_.get(), temp_dir(), pref_service_.get(), - db_provider_.get(), top_host_provider); + db_provider_.get(), top_host_provider, url_loader_factory_); + hints_manager_->SetClockForTesting(browser_thread_bundle_.GetMockClock()); // Add observer is called after the HintCache is fully initialized, // indicating that the OptimizationGuideHintsManager is ready to process @@ -171,10 +255,27 @@ ProcessHints(config, version); } + std::unique_ptr<TestHintsFetcher> BuildTestHintsFetcher( + HintsFetcherEndState end_state) { + std::unique_ptr<TestHintsFetcher> hints_fetcher = + std::make_unique<TestHintsFetcher>( + url_loader_factory_, GURL("https://hintsserver.com"), end_state); + return hints_fetcher; + } + + void MoveClockForwardBy(base::TimeDelta time_delta) { + browser_thread_bundle_.FastForwardBy(time_delta); + RunUntilIdle(); + } + OptimizationGuideHintsManager* hints_manager() const { return hints_manager_.get(); } + TestHintsFetcher* hints_fetcher() const { + return static_cast<TestHintsFetcher*>(hints_manager()->hints_fetcher()); + } + GURL url_with_hints() const { return GURL("https://somedomain.org/news/whatever"); } @@ -199,11 +300,15 @@ serialized_config.size())); } - content::TestBrowserThreadBundle browser_thread_bundle_; + content::TestBrowserThreadBundle browser_thread_bundle_ = { + base::test::ScopedTaskEnvironment::MainThreadType::UI, + base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME}; std::unique_ptr<OptimizationGuideHintsManager> hints_manager_; std::unique_ptr<TestOptimizationGuideService> optimization_guide_service_; std::unique_ptr<TestingPrefServiceSimple> pref_service_; + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; + network::TestURLLoaderFactory test_url_loader_factory_; DISALLOW_COPY_AND_ASSIGN(OptimizationGuideHintsManagerTest); }; @@ -711,46 +816,157 @@ feature_list.InitWithFeatures( {optimization_guide::features::kOptimizationHintsFetching}, {}); - base::HistogramTester histogram_tester; + std::unique_ptr<MockTopHostProvider> top_host_provider = + std::make_unique<MockTopHostProvider>(); + EXPECT_CALL(*top_host_provider, GetTopHosts(testing::_)).Times(0); CreateServiceAndHintsManager(/*top_host_provider=*/nullptr); + hints_manager()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); - histogram_tester.ExpectUniqueSample("OptimizationGuide.HintsFetching.Allowed", - false, 1); + // Force timer to expire and schedule a hints fetch. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_FALSE(hints_fetcher()->hints_fetched()); } TEST_F(OptimizationGuideHintsManagerTest, HintsFetchNotAllowedIfFeatureIsNotEnabledButTopHostProviderIsProvided) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - optimization_guide::switches::kFetchHintsOverride, "whatever.com"); base::test::ScopedFeatureList feature_list; feature_list.InitWithFeatures( {}, {optimization_guide::features::kOptimizationHintsFetching}); - base::HistogramTester histogram_tester; + std::unique_ptr<MockTopHostProvider> top_host_provider = + std::make_unique<MockTopHostProvider>(); + EXPECT_CALL(*top_host_provider, GetTopHosts(testing::_)).Times(0); - std::unique_ptr<optimization_guide::TopHostProvider> top_host_provider = - optimization_guide::CommandLineTopHostProvider::CreateIfEnabled(); CreateServiceAndHintsManager(top_host_provider.get()); - - histogram_tester.ExpectUniqueSample("OptimizationGuide.HintsFetching.Allowed", - false, 1); } TEST_F(OptimizationGuideHintsManagerTest, HintsFetchAllowedIfFeatureIsEnabledAndTopHostProviderIsProvided) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - optimization_guide::switches::kFetchHintsOverride, "whatever.com"); base::test::ScopedFeatureList feature_list; feature_list.InitWithFeatures( {optimization_guide::features::kOptimizationHintsFetching}, {}); - base::HistogramTester histogram_tester; + std::unique_ptr<MockTopHostProvider> top_host_provider = + std::make_unique<MockTopHostProvider>(); + std::vector<std::string> hosts = {"example1.com", "example2.com"}; + EXPECT_CALL(*top_host_provider, GetTopHosts(testing::_)) + .Times(1) + .WillRepeatedly(testing::Return(hosts)); - std::unique_ptr<optimization_guide::TopHostProvider> top_host_provider = - optimization_guide::CommandLineTopHostProvider::CreateIfEnabled(); CreateServiceAndHintsManager(top_host_provider.get()); + hints_manager()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); - histogram_tester.ExpectUniqueSample("OptimizationGuide.HintsFetching.Allowed", - true, 1); + // Force timer to expire and schedule a hints fetch. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_TRUE(hints_fetcher()->hints_fetched()); +} + +TEST_F(OptimizationGuideHintsManagerTest, HintsFetcherEnabledNoHostsToFetch) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + optimization_guide::features::kOptimizationHintsFetching); + + std::unique_ptr<MockTopHostProvider> top_host_provider = + std::make_unique<MockTopHostProvider>(); + EXPECT_CALL(*top_host_provider, GetTopHosts(testing::_)).Times(1); + CreateServiceAndHintsManager(top_host_provider.get()); + hints_manager()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); + + // Force timer to expire and schedule a hints fetch. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_FALSE(hints_fetcher()->hints_fetched()); +} + +TEST_F(OptimizationGuideHintsManagerTest, + HintsFetcherEnabledWithHostsNoHintsInResponse) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + optimization_guide::features::kOptimizationHintsFetching); + + std::unique_ptr<MockTopHostProvider> top_host_provider = + std::make_unique<MockTopHostProvider>(); + std::vector<std::string> hosts = {"example1.com", "example2.com"}; + // This should be called exactly once, confirming that hints are not fetched + // again after |kTestFetchRetryDelaySecs|. + EXPECT_CALL(*top_host_provider, GetTopHosts(testing::_)) + .Times(1) + .WillRepeatedly(testing::Return(hosts)); + CreateServiceAndHintsManager(top_host_provider.get()); + hints_manager()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithNoHints)); + + // Force timer to expire and schedule a hints fetch. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_TRUE(hints_fetcher()->hints_fetched()); + + // Check that hints should not be fetched again after the delay for a failed + // hints fetch attempt. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_CALL(*top_host_provider, GetTopHosts(testing::_)).Times(0); +} + +TEST_F(OptimizationGuideHintsManagerTest, HintsFetcherTimerRetryDelay) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + optimization_guide::features::kOptimizationHintsFetching); + + std::unique_ptr<MockTopHostProvider> top_host_provider = + std::make_unique<MockTopHostProvider>(); + std::vector<std::string> hosts = {"example1.com", "example2.com"}; + // Should be called twice: once for the failed fetch and then again for the + // successful fetch. + EXPECT_CALL(*top_host_provider, GetTopHosts(testing::_)) + .Times(2) + .WillRepeatedly(testing::Return(hosts)); + + CreateServiceAndHintsManager(top_host_provider.get()); + hints_manager()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchFailed)); + + // Force timer to expire and schedule a hints fetch - first time. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_FALSE(hints_fetcher()->hints_fetched()); + + // Force speculative timer to expire after fetch fails first time, update + // hints fetcher so it succeeds this time. + hints_manager()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_TRUE(hints_fetcher()->hints_fetched()); +} + +TEST_F(OptimizationGuideHintsManagerTest, HintsFetcherTimerFetchSucceeds) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + optimization_guide::features::kOptimizationHintsFetching); + + std::unique_ptr<MockTopHostProvider> top_host_provider = + std::make_unique<MockTopHostProvider>(); + std::vector<std::string> hosts = {"example1.com", "example2.com"}; + EXPECT_CALL(*top_host_provider, GetTopHosts(testing::_)) + .WillRepeatedly(testing::Return(hosts)); + + // Force hints fetch scheduling. + CreateServiceAndHintsManager(top_host_provider.get()); + hints_manager()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); + + // Force timer to expire and schedule a hints fetch that succeeds. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_TRUE(hints_fetcher()->hints_fetched()); + + // TODO(mcrouse): Make sure timer is triggered by metadata entry, + // |hint_cache| control needed. + hints_manager()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); + + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_FALSE(hints_fetcher()->hints_fetched()); + + MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs)); + EXPECT_TRUE(hints_fetcher()->hints_fetched()); }
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc index 22252bc..a4318882 100644 --- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc +++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -17,6 +17,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_handle.h" +#include "content/public/browser/storage_partition.h" namespace { @@ -57,10 +58,12 @@ DCHECK(optimization_guide_service); top_host_provider_ = GetTopHostProviderIfUserPermitted(browser_context_); + Profile* profile = Profile::FromBrowserContext(browser_context_); hints_manager_ = std::make_unique<OptimizationGuideHintsManager>( - optimization_guide_service, profile_path, - Profile::FromBrowserContext(browser_context_)->GetPrefs(), - database_provider, top_host_provider_.get()); + optimization_guide_service, profile_path, profile->GetPrefs(), + database_provider, top_host_provider_.get(), + content::BrowserContext::GetDefaultStoragePartition(profile) + ->GetURLLoaderFactoryForBrowserProcess()); } void OptimizationGuideKeyedService::RegisterOptimizationTypes(
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc index de5e56c..3dbe0c2 100644 --- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
@@ -6,6 +6,7 @@ #include <stddef.h> #include <stdint.h> + #include <memory> #include <utility> @@ -46,7 +47,8 @@ void RecordFirstMeaningfulPaintStatus( internal::FirstMeaningfulPaintStatus status) { UMA_HISTOGRAM_ENUMERATION(internal::kHistogramFirstMeaningfulPaintStatus, - status, internal::FIRST_MEANINGFUL_PAINT_LAST_ENTRY); + status, + internal::FIRST_MEANINGFUL_PAINT_LAST_ENTRY); } void RecordTimeToInteractiveStatus(internal::TimeToInteractiveStatus status) { @@ -797,41 +799,50 @@ info.first_foreground_time.value()); } - if (WasStartedInForegroundOptionalEventInForeground( - main_frame_timing.paint_timing->largest_image_paint, info)) { - PAGE_LOAD_HISTOGRAM( - internal::kHistogramLargestImagePaint, - main_frame_timing.paint_timing->largest_image_paint.value()); - } - if (WasStartedInForegroundOptionalEventInForeground( - main_frame_timing.paint_timing->largest_text_paint, info)) { - PAGE_LOAD_HISTOGRAM( - internal::kHistogramLargestTextPaint, - main_frame_timing.paint_timing->largest_text_paint.value()); - } - base::Optional<base::TimeDelta> largest_content_paint_time; - uint64_t largest_content_paint_size; - PageLoadMetricsObserver::LargestContentType largest_content_type; - if (AssignTimeAndSizeForLargestContentfulPaint( - main_frame_timing.paint_timing, &largest_content_paint_time, - &largest_content_paint_size, &largest_content_type) && + const page_load_metrics::ContentfulPaintTimingInfo& + main_frame_largest_image_paint = + largest_contentful_paint_handler_.MainFrameLargestImagePaint(); + if (!main_frame_largest_image_paint.IsEmpty() && WasStartedInForegroundOptionalEventInForeground( - largest_content_paint_time, info)) { - PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentfulPaintMainFrame, - largest_content_paint_time.value()); - UMA_HISTOGRAM_ENUMERATION( - internal::kHistogramLargestContentfulPaintMainFrameContentType, - largest_content_type); + main_frame_largest_image_paint.Time(), info)) { + PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestImagePaint, + main_frame_largest_image_paint.Time().value()); } - const page_load_metrics::ContentfulPaintTimingInfo& paint = - largest_contentful_paint_handler_.MergeMainFrameAndSubframes(); - if (!paint.IsEmpty() && - WasStartedInForegroundOptionalEventInForeground(paint.Time(), info)) { - PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentfulPaint, - paint.Time().value()); + const page_load_metrics::ContentfulPaintTimingInfo& + main_frame_largest_text_paint = + largest_contentful_paint_handler_.MainFrameLargestTextPaint(); + if (!main_frame_largest_text_paint.IsEmpty() && + WasStartedInForegroundOptionalEventInForeground( + main_frame_largest_text_paint.Time(), info)) { + PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestTextPaint, + main_frame_largest_text_paint.Time().value()); + } + + const page_load_metrics::ContentfulPaintTimingInfo& + main_frame_largest_contentful_paint = + largest_contentful_paint_handler_.MainFrameLargestContentfulPaint(); + if (!main_frame_largest_contentful_paint.IsEmpty() && + WasStartedInForegroundOptionalEventInForeground( + main_frame_largest_contentful_paint.Time(), info)) { + PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentfulPaintMainFrame, + main_frame_largest_contentful_paint.Time().value()); UMA_HISTOGRAM_ENUMERATION( - internal::kHistogramLargestContentfulPaintContentType, paint.Type()); + internal::kHistogramLargestContentfulPaintMainFrameContentType, + main_frame_largest_contentful_paint.Type()); + } + + const page_load_metrics::ContentfulPaintTimingInfo& + all_frames_largest_contentful_paint = + largest_contentful_paint_handler_.MergeMainFrameAndSubframes(); + if (!all_frames_largest_contentful_paint.IsEmpty() && + WasStartedInForegroundOptionalEventInForeground( + all_frames_largest_contentful_paint.Time(), info)) { + PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentfulPaint, + all_frames_largest_contentful_paint.Time().value()); + UMA_HISTOGRAM_ENUMERATION( + internal::kHistogramLargestContentfulPaintContentType, + all_frames_largest_contentful_paint.Type()); } if (main_frame_timing.paint_timing->first_paint &&
diff --git a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h b/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h index 59f205f6..0bc3200 100644 --- a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h +++ b/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h
@@ -78,6 +78,21 @@ return main_frame_tree_node_id_.value(); } + // We merge the candidates from text side and image side to get the largest + // candidate across both types of content. + const ContentfulPaintTimingInfo& MainFrameLargestContentfulPaint() { + return main_frame_contentful_paint_.MergeTextAndImageTiming(); + } + const ContentfulPaintTimingInfo& SubframesLargestContentfulPaint() { + return subframe_contentful_paint_.MergeTextAndImageTiming(); + } + const ContentfulPaintTimingInfo& MainFrameLargestImagePaint() { + return main_frame_contentful_paint_.Image(); + } + const ContentfulPaintTimingInfo& MainFrameLargestTextPaint() { + return main_frame_contentful_paint_.Text(); + } + // We merge the candidates from main frame and subframe to get the largest // candidate across all frames. const ContentfulPaintTimingInfo& MergeMainFrameAndSubframes();
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc index 8666d1f1..0fd7666 100644 --- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -298,35 +298,41 @@ builder.SetExperimental_PaintTiming_NavigationToFirstMeaningfulPaint( timing.paint_timing->first_meaningful_paint.value().InMilliseconds()); } - if (timing.paint_timing->largest_image_paint.has_value() && + const page_load_metrics::ContentfulPaintTimingInfo& + main_frame_largest_image_paint = + largest_contentful_paint_handler_.MainFrameLargestImagePaint(); + if (!main_frame_largest_image_paint.IsEmpty() && WasStartedInForegroundOptionalEventInForeground( - timing.paint_timing->largest_image_paint, info)) { + main_frame_largest_image_paint.Time(), info)) { builder.SetExperimental_PaintTiming_NavigationToLargestImagePaint( timing.paint_timing->largest_image_paint.value().InMilliseconds()); } - if (timing.paint_timing->largest_text_paint.has_value() && + const page_load_metrics::ContentfulPaintTimingInfo& + main_frame_largest_text_paint = + largest_contentful_paint_handler_.MainFrameLargestTextPaint(); + if (!main_frame_largest_text_paint.IsEmpty() && WasStartedInForegroundOptionalEventInForeground( - timing.paint_timing->largest_text_paint, info)) { + main_frame_largest_text_paint.Time(), info)) { builder.SetExperimental_PaintTiming_NavigationToLargestTextPaint( timing.paint_timing->largest_text_paint.value().InMilliseconds()); } - base::Optional<base::TimeDelta> largest_content_paint_time; - uint64_t largest_content_paint_size; - PageLoadMetricsObserver::LargestContentType largest_content_type; - if (AssignTimeAndSizeForLargestContentfulPaint( - timing.paint_timing, &largest_content_paint_time, - &largest_content_paint_size, &largest_content_type) && + const page_load_metrics::ContentfulPaintTimingInfo& + main_frame_largest_contentful_paint = + largest_contentful_paint_handler_.MainFrameLargestContentfulPaint(); + if (!main_frame_largest_contentful_paint.IsEmpty() && WasStartedInForegroundOptionalEventInForeground( - largest_content_paint_time, info)) { + main_frame_largest_contentful_paint.Time(), info)) { builder.SetPaintTiming_NavigationToLargestContentfulPaint_MainFrame( - largest_content_paint_time.value().InMilliseconds()); + main_frame_largest_contentful_paint.Time().value().InMilliseconds()); } - const page_load_metrics::ContentfulPaintTimingInfo& paint = - largest_contentful_paint_handler_.MergeMainFrameAndSubframes(); - if (!paint.IsEmpty() && - WasStartedInForegroundOptionalEventInForeground(paint.Time(), info)) { + const page_load_metrics::ContentfulPaintTimingInfo& + all_frames_largest_contentful_paint = + largest_contentful_paint_handler_.MergeMainFrameAndSubframes(); + if (!all_frames_largest_contentful_paint.IsEmpty() && + WasStartedInForegroundOptionalEventInForeground( + all_frames_largest_contentful_paint.Time(), info)) { builder.SetPaintTiming_NavigationToLargestContentfulPaint( - paint.Time().value().InMilliseconds()); + all_frames_largest_contentful_paint.Time().value().InMilliseconds()); } if (timing.interactive_timing->interactive) { base::TimeDelta time_to_interactive =
diff --git a/chrome/browser/payments/DEPS b/chrome/browser/payments/DEPS index be3cab72..d8ca073a 100644 --- a/chrome/browser/payments/DEPS +++ b/chrome/browser/payments/DEPS
@@ -1,3 +1,11 @@ include_rules = [ "+third_party/libaddressinput", ] + +specific_include_rules = { + "chrome_payment_request_delegate\.cc": [ + # This delegate is hardcoded to construct a Views dialog right now, since it + # is only used on Views platforms. + "+chrome/browser/ui/views", + ], +}
diff --git a/chrome/browser/payments/chrome_payment_request_delegate.cc b/chrome/browser/payments/chrome_payment_request_delegate.cc index 0ea908a..e3c4938d 100644 --- a/chrome/browser/payments/chrome_payment_request_delegate.cc +++ b/chrome/browser/payments/chrome_payment_request_delegate.cc
@@ -18,9 +18,9 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/views/payments/payment_request_dialog_view.h" #include "chrome/browser/web_data_service_factory.h" #include "components/autofill/core/browser/address_normalizer_impl.h" #include "components/autofill/core/browser/geo/region_data_loader_impl.h" @@ -64,7 +64,7 @@ void ChromePaymentRequestDelegate::ShowDialog(PaymentRequest* request) { DCHECK_EQ(nullptr, shown_dialog_); - shown_dialog_ = chrome::CreatePaymentRequestDialog(request); + shown_dialog_ = new payments::PaymentRequestDialogView(request, nullptr); shown_dialog_->ShowDialog(); }
diff --git a/chrome/browser/predictors/loading_predictor_browsertest.cc b/chrome/browser/predictors/loading_predictor_browsertest.cc index a1c33559..c99ec11 100644 --- a/chrome/browser/predictors/loading_predictor_browsertest.cc +++ b/chrome/browser/predictors/loading_predictor_browsertest.cc
@@ -940,7 +940,8 @@ request->url = url; content::SimpleURLLoaderTestHelper simple_loader_helper; url::Origin origin = url::Origin::Create(url); - request->trusted_network_isolation_key = + request->trusted_params = network::ResourceRequest::TrustedParams(); + request->trusted_params->network_isolation_key = net::NetworkIsolationKey(origin, origin); std::unique_ptr<network::SimpleURLLoader> simple_loader = network::SimpleURLLoader::Create(std::move(request),
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc index 032070c33..423cc90 100644 --- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc +++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc
@@ -164,11 +164,8 @@ } else { std::vector<url::Origin> origins_to_remove; - // CHECK_EQ instead of DCHECK_EQ to determine whether - // https://crbug.com/966059 still happens in production. - // TODO(fdoray): Remove once https://crbug.com/966059 is fixed. - CHECK_EQ(deletion_info.deleted_urls_origin_map().size(), - CountOriginsInURLRows(deletion_info.deleted_rows())); + DCHECK_EQ(deletion_info.deleted_urls_origin_map().size(), + CountOriginsInURLRows(deletion_info.deleted_rows())); for (const auto& it : deletion_info.deleted_urls_origin_map()) { const url::Origin origin = url::Origin::Create(it.first); const int remaining_visits_in_history = it.second.first;
diff --git a/chrome/browser/resources/chromeos/arc_support/background.js b/chrome/browser/resources/chromeos/arc_support/background.js index 44e3c44..df88dbf 100644 --- a/chrome/browser/resources/chromeos/arc_support/background.js +++ b/chrome/browser/resources/chromeos/arc_support/background.js
@@ -972,10 +972,6 @@ if (outerHeight > screen.availHeight) { outerHeight = screen.availHeight; } - if (appWindow.outerBounds.width == outerWidth && - appWindow.outerBounds.height == outerHeight) { - return; - } appWindow.outerBounds.width = outerWidth; appWindow.outerBounds.height = outerHeight;
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js index 33b1463..76e7f42 100644 --- a/chrome/browser/resources/local_ntp/customize.js +++ b/chrome/browser/resources/local_ntp/customize.js
@@ -304,6 +304,13 @@ */ customize.colorsMenuLoaded = false; + +/** + * Custom color picked in hex format. + * @type {string} + */ +customize.customColorPicked = '#000000'; + /** * Sets the visibility of the settings menu and individual options depending on * their respective features. @@ -2259,6 +2266,7 @@ // Configure custom color picker. if (configData.chromeColorsCustomColorPicker) { $(customize.IDS.COLOR_PICKER_TILE).onclick = function(event) { + $(customize.IDS.COLOR_PICKER).value = customize.customColorPicked; $(customize.IDS.COLOR_PICKER).click(); }; $(customize.IDS.COLOR_PICKER_TILE).onkeydown = @@ -2321,8 +2329,7 @@ tile = $(customize.IDS.COLOR_PICKER_TILE); // Update color picker tile colors. - $(customize.IDS.COLOR_PICKER).value = - colorArrayToHex(themeInfo.colorPicked); + customize.customColorPicked = colorArrayToHex(themeInfo.colorPicked); $(customize.IDS.COLORS_MENU) .style.setProperty( '--custom-color-border', colorArrayToHex(themeInfo.colorDark));
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html index 8be24f5..cea9ce9 100644 --- a/chrome/browser/resources/settings/people_page/people_page.html +++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -28,6 +28,7 @@ <if expr="chromeos"> <link rel="import" href="account_manager.html"> +<link rel="import" href="account_manager_browser_proxy.html"> <link rel="import" href="change_picture.html"> <link rel="import" href="chrome://resources/cr_elements/chromeos/cr_picture/cr_png_behavior.html"> <link rel="import" href="fingerprint_list.html">
diff --git a/chrome/browser/resources/settings/people_page/people_page.js b/chrome/browser/resources/settings/people_page/people_page.js index c7481d27..defd0c7 100644 --- a/chrome/browser/resources/settings/people_page/people_page.js +++ b/chrome/browser/resources/settings/people_page/people_page.js
@@ -233,10 +233,24 @@ /** @override */ attached: function() { - const profileInfoProxy = settings.ProfileInfoBrowserProxyImpl.getInstance(); - profileInfoProxy.getProfileInfo().then(this.handleProfileInfo_.bind(this)); - this.addWebUIListener( - 'profile-info-changed', this.handleProfileInfo_.bind(this)); + let useProfileNameAndIcon = true; + // <if expr="chromeos"> + if (!loadTimeData.getBoolean('showOSSettings') && + this.isAccountManagerEnabled_) { + // If this is SplitSettings and we have the Google Account manager, + // prefer the GAIA name and icon. + useProfileNameAndIcon = false; + this.addWebUIListener( + 'accounts-changed', this.updateAccounts_.bind(this)); + this.updateAccounts_(); + } + // </if> + if (useProfileNameAndIcon) { + settings.ProfileInfoBrowserProxyImpl.getInstance().getProfileInfo().then( + this.handleProfileInfo_.bind(this)); + this.addWebUIListener( + 'profile-info-changed', this.handleProfileInfo_.bind(this)); + } this.syncBrowserProxy_ = settings.SyncBrowserProxyImpl.getInstance(); this.syncBrowserProxy_.getSyncStatus().then( @@ -318,6 +332,27 @@ this.profileIconUrl_ = info.iconUrl; }, + // <if expr="chromeos"> + /** + * @private + * @suppress {checkTypes} The types only exists in Chrome OS builds, but + * Closure doesn't understand the <if> above. + */ + updateAccounts_: async function() { + const /** @type {!Array<{settings.Account}>} */ accounts = + await settings.AccountManagerBrowserProxyImpl.getInstance() + .getAccounts(); + // The user might not have any GAIA accounts (e.g. guest mode, Kerberos, + // Active Directory). In these cases the profile row is hidden, so there's + // nothing to do. + if (accounts.length == 0) { + return; + } + this.profileName_ = accounts[0].fullName; + this.profileIconUrl_ = accounts[0].pic; + }, + // </if> + /** * Handler for when the sync state is pushed from the browser. * @param {?settings.SyncStatus} syncStatus
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc index 3970f79..16b7daf 100644 --- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc +++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
@@ -24,8 +24,6 @@ #include "ui/views/controls/menu/menu_config.h" #include "url/url_constants.h" -using SharingMessage = chrome_browser_sharing::SharingMessage; - ClickToCallContextMenuObserver::SubMenuDelegate::SubMenuDelegate( ClickToCallContextMenuObserver* parent) : parent_(parent) {}
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h index 968e3235..e48c96e 100644 --- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h +++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h
@@ -11,7 +11,6 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" -#include "base/memory/weak_ptr.h" #include "base/optional.h" #include "chrome/browser/sharing/sharing_device_info.h" #include "components/renderer_context_menu/render_view_context_menu_observer.h" @@ -26,9 +25,7 @@ class SharingService; -class ClickToCallContextMenuObserver - : public RenderViewContextMenuObserver, - public base::SupportsWeakPtr<ClickToCallContextMenuObserver> { +class ClickToCallContextMenuObserver : public RenderViewContextMenuObserver { public: class SubMenuDelegate : public ui::SimpleMenuModel::Delegate { public:
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc new file mode 100644 index 0000000..d6b1cf6 --- /dev/null +++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc
@@ -0,0 +1,158 @@ +// 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/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h" + +#include "base/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/renderer_context_menu/render_view_context_menu.h" +#include "chrome/browser/sharing/shared_clipboard/feature_flags.h" +#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.h" +#include "chrome/browser/sharing/sharing_constants.h" +#include "chrome/browser/sharing/sharing_metrics.h" +#include "chrome/browser/sharing/sharing_service.h" +#include "chrome/browser/sharing/sharing_service_factory.h" +#include "chrome/grit/generated_resources.h" +#include "components/vector_icons/vector_icons.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/color_palette.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/native_theme/native_theme.h" +#include "ui/resources/grit/ui_resources.h" +#include "ui/views/controls/menu/menu_config.h" + +SharedClipboardContextMenuObserver::SubMenuDelegate::SubMenuDelegate( + SharedClipboardContextMenuObserver* parent) + : parent_(parent) {} + +SharedClipboardContextMenuObserver::SubMenuDelegate::~SubMenuDelegate() = + default; + +bool SharedClipboardContextMenuObserver::SubMenuDelegate::IsCommandIdEnabled( + int command_id) const { + // All supported commands are enabled in sub menu. + return true; +} + +void SharedClipboardContextMenuObserver::SubMenuDelegate::ExecuteCommand( + int command_id, + int event_flags) { + if (command_id < kSubMenuFirstDeviceCommandId || + command_id > kSubMenuLastDeviceCommandId) + return; + int device_index = command_id - kSubMenuFirstDeviceCommandId; + parent_->SendSharedClipboardMessage(device_index); +} + +SharedClipboardContextMenuObserver::SharedClipboardContextMenuObserver( + RenderViewContextMenuProxy* proxy) + : proxy_(proxy), + sharing_service_(SharingServiceFactory::GetForBrowserContext( + proxy_->GetBrowserContext())) {} + +SharedClipboardContextMenuObserver::~SharedClipboardContextMenuObserver() = + default; + +void SharedClipboardContextMenuObserver::InitMenu( + const content::ContextMenuParams& params) { + text_ = params.selection_text; + devices_ = sharing_service_->GetDeviceCandidates( + static_cast<int>(SharingDeviceCapability::kNone)); + // TODO(yasmo): add logging + + if (devices_.empty()) + return; + + proxy_->AddSeparator(); + if (devices_.size() == 1) { +#if defined(OS_MACOSX) + proxy_->AddMenuItem( + IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE, + l10n_util::GetStringFUTF16( + IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET, + devices_[0].human_readable_name())); +#else + proxy_->AddMenuItemWithIcon( + IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE, + l10n_util::GetStringFUTF16( + IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET, + devices_[0].human_readable_name()), + GetContextMenuIcon()); +#endif + } else { + BuildSubMenu(); +#if defined(OS_MACOSX) + proxy_->AddSubMenu( + IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES, + l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF), + sub_menu_model_.get()); +#else + proxy_->AddSubMenuWithStringIdAndIcon( + IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES, + IDS_CONTEXT_MENU_SEND_TAB_TO_SELF, sub_menu_model_.get(), + GetContextMenuIcon()); +#endif + } +} + +void SharedClipboardContextMenuObserver::BuildSubMenu() { + sub_menu_model_ = std::make_unique<ui::SimpleMenuModel>(&sub_menu_delegate_); + + int command_id = kSubMenuFirstDeviceCommandId; + for (const auto& device : devices_) { + if (command_id > kSubMenuLastDeviceCommandId) + break; + sub_menu_model_->AddItem(command_id++, device.human_readable_name()); + } +} + +bool SharedClipboardContextMenuObserver::IsCommandIdSupported(int command_id) { + if (devices_.empty()) + return false; + + if (devices_.size() == 1) { + return command_id == + IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE; + } else { + return command_id == + IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES; + } +} + +bool SharedClipboardContextMenuObserver::IsCommandIdEnabled(int command_id) { + // All supported commands are enabled. + return true; +} + +void SharedClipboardContextMenuObserver::ExecuteCommand(int command_id) { + if (command_id == + IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE) { + DCHECK(devices_.size() == 1); + SendSharedClipboardMessage(0); + } +} + +void SharedClipboardContextMenuObserver::SendSharedClipboardMessage( + int chosen_device_index) { + if (chosen_device_index >= static_cast<int>(devices_.size())) + return; + + // TODO(yasmo): Add logging + + SharedClipboardUiController::DeviceSelected(proxy_->GetWebContents(), text_, + devices_[chosen_device_index]); +} + +gfx::ImageSkia SharedClipboardContextMenuObserver::GetContextMenuIcon() const { + const ui::NativeTheme* native_theme = + ui::NativeTheme::GetInstanceForNativeUi(); + bool is_dark = native_theme && native_theme->ShouldUseDarkColors(); + int resource_id = is_dark ? IDR_SEND_TAB_TO_SELF_ICON_DARK + : IDR_SEND_TAB_TO_SELF_ICON_LIGHT; + return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + resource_id); +}
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h new file mode 100644 index 0000000..99a3cd0 --- /dev/null +++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h
@@ -0,0 +1,83 @@ +// 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_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_CONTEXT_MENU_OBSERVER_H_ +#define CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_CONTEXT_MENU_OBSERVER_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/optional.h" +#include "chrome/browser/sharing/sharing_device_info.h" +#include "components/renderer_context_menu/render_view_context_menu_observer.h" +#include "ui/base/models/simple_menu_model.h" + +namespace gfx { +class ImageSkia; +} + +class RenderViewContextMenuProxy; + +class SharingService; + +class SharedClipboardContextMenuObserver + : public RenderViewContextMenuObserver { + public: + class SubMenuDelegate : public ui::SimpleMenuModel::Delegate { + public: + explicit SubMenuDelegate(SharedClipboardContextMenuObserver* parent); + ~SubMenuDelegate() override; + + bool IsCommandIdEnabled(int command_id) const override; + void ExecuteCommand(int command_id, int event_flags) override; + + private: + SharedClipboardContextMenuObserver* const parent_; + + DISALLOW_COPY_AND_ASSIGN(SubMenuDelegate); + }; + + explicit SharedClipboardContextMenuObserver( + RenderViewContextMenuProxy* proxy); + ~SharedClipboardContextMenuObserver() override; + + // RenderViewContextMenuObserver implementation. + void InitMenu(const content::ContextMenuParams& params) override; + bool IsCommandIdSupported(int command_id) override; + bool IsCommandIdEnabled(int command_id) override; + void ExecuteCommand(int command_id) override; + + private: + FRIEND_TEST_ALL_PREFIXES(SharedClipboardContextMenuObserverTest, + SingleDevice_ShowMenu); + FRIEND_TEST_ALL_PREFIXES(SharedClipboardContextMenuObserverTest, + MultipleDevices_ShowMenu); + FRIEND_TEST_ALL_PREFIXES(SharedClipboardContextMenuObserverTest, + MultipleDevices_MoreThanMax_ShowMenu); + + void BuildSubMenu(); + + void SendSharedClipboardMessage(int chosen_device_index); + + gfx::ImageSkia GetContextMenuIcon() const; + + RenderViewContextMenuProxy* proxy_ = nullptr; + + SharingService* sharing_service_ = nullptr; + + SubMenuDelegate sub_menu_delegate_{this}; + + base::string16 text_; + + std::vector<SharingDeviceInfo> devices_; + + std::unique_ptr<ui::SimpleMenuModel> sub_menu_model_; + + DISALLOW_COPY_AND_ASSIGN(SharedClipboardContextMenuObserver); +}; + +#endif // CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_CONTEXT_MENU_OBSERVER_H_
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc new file mode 100644 index 0000000..b882ecb --- /dev/null +++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
@@ -0,0 +1,266 @@ +// 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/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h" + +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h" +#include "chrome/browser/sharing/shared_clipboard/feature_flags.h" +#include "chrome/browser/sharing/sharing_constants.h" +#include "chrome/browser/sharing/sharing_device_info.h" +#include "chrome/browser/sharing/sharing_fcm_handler.h" +#include "chrome/browser/sharing/sharing_fcm_sender.h" +#include "chrome/browser/sharing/sharing_service.h" +#include "chrome/browser/sharing/sharing_service_factory.h" +#include "chrome/browser/sharing/sharing_sync_preference.h" +#include "chrome/browser/sharing/vapid_key_manager.h" +#include "content/public/common/context_menu_params.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/web_contents_tester.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::ByMove; +using ::testing::Eq; +using ::testing::NiceMock; +using ::testing::Return; + +using SharingMessage = chrome_browser_sharing::SharingMessage; + +namespace { + +const char kText[] = "Some random text to be copied."; + +constexpr int kSeparatorCommandId = -1; + +class MockSharingService : public SharingService { + public: + explicit MockSharingService(std::unique_ptr<SharingFCMHandler> fcm_handler) + : SharingService(/* sync_prefs= */ nullptr, + /* vapid_key_manager= */ nullptr, + /* sharing_device_registration= */ nullptr, + /* fcm_sender= */ nullptr, + std::move(fcm_handler), + /* gcm_driver= */ nullptr, + /* device_info_tracker= */ nullptr, + /* local_device_info_provider= */ nullptr, + /* sync_service */ nullptr) {} + + ~MockSharingService() override = default; + + MOCK_CONST_METHOD1(GetDeviceCandidates, + std::vector<SharingDeviceInfo>(int required_capabilities)); + + MOCK_METHOD4(SendMessageToDevice, + void(const std::string& device_guid, + base::TimeDelta time_to_live, + SharingMessage message, + SharingService::SendMessageCallback callback)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockSharingService); +}; + +class SharedClipboardContextMenuObserverTest : public testing::Test { + public: + SharedClipboardContextMenuObserverTest() = default; + + ~SharedClipboardContextMenuObserverTest() override = default; + + void SetUp() override { + web_contents_ = content::WebContentsTester::CreateTestWebContents( + menu_.GetBrowserContext(), nullptr); + menu_.set_web_contents(web_contents_.get()); + SharingServiceFactory::GetInstance()->SetTestingFactory( + menu_.GetBrowserContext(), + base::BindRepeating([](content::BrowserContext* context) + -> std::unique_ptr<KeyedService> { + return std::make_unique<NiceMock<MockSharingService>>( + std::make_unique<SharingFCMHandler>(nullptr, nullptr)); + })); + observer_ = std::make_unique<SharedClipboardContextMenuObserver>(&menu_); + menu_.SetObserver(observer_.get()); + } + + void InitMenu(const base::string16 text) { + content::ContextMenuParams params; + params.selection_text = text; + observer_->InitMenu(params); + sharing_message.mutable_shared_clipboard_message()->set_text( + base::UTF16ToUTF8(text)); + } + + std::vector<SharingDeviceInfo> CreateMockDevices(int count) { + std::vector<SharingDeviceInfo> devices; + for (int i = 0; i < count; i++) { + devices.emplace_back( + base::StrCat({"guid", base::NumberToString(i)}), + base::UTF8ToUTF16(base::StrCat({"name", base::NumberToString(i)})), + sync_pb::SyncEnums::TYPE_PHONE, base::Time::Now(), + static_cast<int>(SharingDeviceCapability::kNone)); + } + return devices; + } + + protected: + NiceMock<MockSharingService>* service() { + return static_cast<NiceMock<MockSharingService>*>( + SharingServiceFactory::GetForBrowserContext(menu_.GetBrowserContext())); + } + + content::TestBrowserThreadBundle thread_bundle_; + MockRenderViewContextMenu menu_{/* incognito= */ false}; + std::unique_ptr<content::WebContents> web_contents_; + std::unique_ptr<SharedClipboardContextMenuObserver> observer_; + SharingMessage sharing_message; + + DISALLOW_COPY_AND_ASSIGN(SharedClipboardContextMenuObserverTest); +}; + +} // namespace + +MATCHER_P(ProtoEquals, message, "") { + std::string expected_serialized, actual_serialized; + message.SerializeToString(&expected_serialized); + arg.SerializeToString(&actual_serialized); + return expected_serialized == actual_serialized; +} + +TEST_F(SharedClipboardContextMenuObserverTest, NoDevices_DoNotShowMenu) { + auto devices = CreateMockDevices(0); + + EXPECT_CALL(*service(), GetDeviceCandidates(_)) + .WillOnce(Return(ByMove(std::move(devices)))); + + InitMenu(base::ASCIIToUTF16(kText)); + + EXPECT_EQ(0U, menu_.GetMenuSize()); +} + +TEST_F(SharedClipboardContextMenuObserverTest, SingleDevice_ShowMenu) { + auto devices = CreateMockDevices(1); + auto guid = devices[0].guid(); + + EXPECT_CALL(*service(), GetDeviceCandidates(_)) + .WillOnce(Return(ByMove(std::move(devices)))); + + InitMenu(base::ASCIIToUTF16(kText)); + + // The first item is a separator and the second item is the device. + EXPECT_EQ(2U, menu_.GetMenuSize()); + + // Assert item ordering. + MockRenderViewContextMenu::MockMenuItem item; + ASSERT_TRUE(menu_.GetMenuItem(0, &item)); + EXPECT_EQ(kSeparatorCommandId, item.command_id); + + ASSERT_TRUE(menu_.GetMenuItem(1, &item)); + EXPECT_EQ(IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE, + item.command_id); + + // Emulate click on the device. + EXPECT_CALL(*service(), SendMessageToDevice(Eq(guid), Eq(kSharingMessageTTL), + ProtoEquals(sharing_message), _)) + .Times(1); + menu_.ExecuteCommand( + IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE, 0); +} + +TEST_F(SharedClipboardContextMenuObserverTest, MultipleDevices_ShowMenu) { + constexpr int device_count = 3; + auto devices = CreateMockDevices(device_count); + std::vector<std::string> guids; + for (auto& device : devices) + guids.push_back(device.guid()); + + EXPECT_CALL(*service(), GetDeviceCandidates(_)) + .WillOnce(Return(ByMove(std::move(devices)))); + + InitMenu(base::ASCIIToUTF16(kText)); + + EXPECT_EQ(device_count + 2U, menu_.GetMenuSize()); + + // Assert item ordering. + MockRenderViewContextMenu::MockMenuItem item; + ASSERT_TRUE(menu_.GetMenuItem(0, &item)); + EXPECT_EQ(kSeparatorCommandId, item.command_id); + + ASSERT_TRUE(menu_.GetMenuItem(1, &item)); + EXPECT_EQ(IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES, + item.command_id); + + for (int i = 0; i < device_count; i++) { + ASSERT_TRUE(menu_.GetMenuItem(i + 2, &item)); + EXPECT_EQ(kSubMenuFirstDeviceCommandId + i, item.command_id); + } + + // Emulate clicks on all commands to check for commands with no device + // assigned. + for (int i = 0; i < kMaxDevicesShown; i++) { + if (i < device_count) { + EXPECT_CALL(*service(), + SendMessageToDevice(Eq(guids[i]), Eq(kSharingMessageTTL), + ProtoEquals(sharing_message), _)) + .Times(1); + } else { + EXPECT_CALL(*service(), SendMessageToDevice(_, _, _, _)).Times(0); + } + observer_->sub_menu_delegate_.ExecuteCommand( + kSubMenuFirstDeviceCommandId + i, 0); + } +} + +TEST_F(SharedClipboardContextMenuObserverTest, + MultipleDevices_MoreThanMax_ShowMenu) { + int device_count = kMaxDevicesShown + 1; + auto devices = CreateMockDevices(device_count); + std::vector<std::string> guids; + for (auto& device : devices) + guids.push_back(device.guid()); + + EXPECT_CALL(*service(), GetDeviceCandidates(_)) + .WillOnce(Return(ByMove(std::move(devices)))); + + InitMenu(base::ASCIIToUTF16(kText)); + + EXPECT_EQ(kMaxDevicesShown + 2U, menu_.GetMenuSize()); + + // Assert item ordering. + MockRenderViewContextMenu::MockMenuItem item; + ASSERT_TRUE(menu_.GetMenuItem(0, &item)); + EXPECT_EQ(kSeparatorCommandId, item.command_id); + + ASSERT_TRUE(menu_.GetMenuItem(1, &item)); + EXPECT_EQ(IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES, + item.command_id); + + for (int i = 0; i < kMaxDevicesShown; i++) { + ASSERT_TRUE(menu_.GetMenuItem(i + 2, &item)); + EXPECT_EQ(kSubMenuFirstDeviceCommandId + i, item.command_id); + } + + // Emulate clicks on all device commands to check for commands outside valid + // range too. + for (int i = 0; i < device_count; i++) { + if (i < kMaxDevicesShown) { + EXPECT_CALL(*service(), + SendMessageToDevice(Eq(guids[i]), Eq(kSharingMessageTTL), + ProtoEquals(sharing_message), _)) + .Times(1); + } else { + EXPECT_CALL(*service(), SendMessageToDevice(_, _, _, _)).Times(0); + } + observer_->sub_menu_delegate_.ExecuteCommand( + kSubMenuFirstDeviceCommandId + i, 0); + } +}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 6e2a3906..2c53465 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -2775,6 +2775,8 @@ "views/infobars/infobar_container_view.h", "views/infobars/infobar_view.cc", "views/infobars/infobar_view.h", + "views/layout/interpolating_layout_manager.cc", + "views/layout/interpolating_layout_manager.h", "views/load_complete_listener.cc", "views/load_complete_listener.h", "views/location_bar/content_setting_image_view.cc",
diff --git a/chrome/browser/ui/android/sms/sms_dialog_android.cc b/chrome/browser/ui/android/sms/sms_dialog_android.cc index 70c0ae07..6f70378 100644 --- a/chrome/browser/ui/android/sms/sms_dialog_android.cc +++ b/chrome/browser/ui/android/sms/sms_dialog_android.cc
@@ -32,10 +32,8 @@ } void SmsDialogAndroid::Open(content::RenderFrameHost* host, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_confirm_ = std::move(on_confirm); - on_cancel_ = std::move(on_cancel); + EventHandler handler) { + handler_ = std::move(handler); content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(host); @@ -55,10 +53,10 @@ Java_SmsReceiverDialog_smsReceived(AttachCurrentThread(), java_dialog_); } -void SmsDialogAndroid::OnConfirm(JNIEnv* env) { - std::move(on_confirm_).Run(); +void SmsDialogAndroid::SmsTimeout() { + Java_SmsReceiverDialog_smsTimeout(AttachCurrentThread(), java_dialog_); } -void SmsDialogAndroid::OnCancel(JNIEnv* env) { - std::move(on_cancel_).Run(); +void SmsDialogAndroid::OnEvent(JNIEnv* env, jint event_type) { + std::move(handler_).Run(static_cast<Event>(event_type)); }
diff --git a/chrome/browser/ui/android/sms/sms_dialog_android.h b/chrome/browser/ui/android/sms/sms_dialog_android.h index 6f4caa1..8d3ef597 100644 --- a/chrome/browser/ui/android/sms/sms_dialog_android.h +++ b/chrome/browser/ui/android/sms/sms_dialog_android.h
@@ -19,22 +19,17 @@ explicit SmsDialogAndroid(const url::Origin& origin); ~SmsDialogAndroid() override; - void Open(content::RenderFrameHost*, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) override; + void Open(content::RenderFrameHost*, EventHandler handler) override; void Close() override; void SmsReceived() override; + void SmsTimeout() override; - // Report the user manually clicks the 'Confirm' button. - void OnConfirm(JNIEnv* env); - - // Report the user manually dismisses the SMS dialog. - void OnCancel(JNIEnv* env); + // Report the user's action through |event_type|. + void OnEvent(JNIEnv* env, jint event_type); private: base::android::ScopedJavaGlobalRef<jobject> java_dialog_; - base::OnceClosure on_confirm_; - base::OnceClosure on_cancel_; + EventHandler handler_; DISALLOW_COPY_AND_ASSIGN(SmsDialogAndroid); };
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h index 2e272c9..e47a91ca 100644 --- a/chrome/browser/ui/browser_dialogs.h +++ b/chrome/browser/ui/browser_dialogs.h
@@ -43,11 +43,6 @@ class AuthChallengeInfo; } -namespace payments { -class PaymentRequest; -class PaymentRequestDialog; -} // namespace payments - namespace safe_browsing { class ChromeCleanerController; class ChromeCleanerDialogController; @@ -139,9 +134,6 @@ task_manager::TaskManagerTableModel* ShowTaskManagerViews(Browser* browser); void HideTaskManagerViews(); -// Show the Views "Chrome Update" dialog. -void ShowUpdateChromeDialogViews(gfx::NativeWindow parent); - #endif // OS_MACOSX #if defined(TOOLKIT_VIEWS) @@ -152,15 +144,6 @@ content::WebContents* web_contents, LoginAuthRequiredCallback auth_required_callback); -// Shows the toolkit-views based BookmarkEditor. -void ShowBookmarkEditorViews(gfx::NativeWindow parent_window, - Profile* profile, - const BookmarkEditor::EditDetails& details, - BookmarkEditor::Configuration configuration); - -payments::PaymentRequestDialog* CreatePaymentRequestDialog( - payments::PaymentRequest* request); - #endif // TOOLKIT_VIEWS // Values used in the Dialog.Creation UMA metric. Each value represents a
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.cc b/chrome/browser/ui/extensions/extension_action_view_controller.cc index 28d030c4..77d09ae5 100644 --- a/chrome/browser/ui/extensions/extension_action_view_controller.cc +++ b/chrome/browser/ui/extensions/extension_action_view_controller.cc
@@ -432,7 +432,7 @@ image_source->SetIcon(icon_factory_.GetIcon(tab_id)); std::unique_ptr<IconWithBadgeImageSource::Badge> badge; - std::string badge_text = extension_action_->GetBadgeText(tab_id); + std::string badge_text = extension_action_->GetDisplayBadgeText(tab_id); if (!badge_text.empty()) { badge.reset(new IconWithBadgeImageSource::Badge( badge_text,
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc index 84ebb31..429e6aa 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -639,17 +639,3 @@ if (!title.empty()) ui::TreeNodeModel<EditorNode>::SetTitle(node, title); } - -namespace chrome { - -void ShowBookmarkEditorViews(gfx::NativeWindow parent_window, - Profile* profile, - const BookmarkEditor::EditDetails& details, - BookmarkEditor::Configuration configuration) { - DCHECK(profile); - BookmarkEditorView* editor = new BookmarkEditorView( - profile, details.parent_node, details, configuration); - editor->Show(parent_window); -} - -} // namespace chrome
diff --git a/chrome/browser/ui/views/browser_dialogs_views.cc b/chrome/browser/ui/views/browser_dialogs_views.cc index 8f0c610..e2943e5 100644 --- a/chrome/browser/ui/views/browser_dialogs_views.cc +++ b/chrome/browser/ui/views/browser_dialogs_views.cc
@@ -12,6 +12,7 @@ #include "chrome/browser/extensions/chrome_extension_chooser_dialog.h" #include "chrome/browser/extensions/extension_install_prompt.h" #include "chrome/browser/ui/login/login_handler.h" +#include "chrome/browser/ui/views/bookmarks/bookmark_editor_view.h" #include "chrome/browser/ui/views/task_manager_view.h" // This file provides definitions of desktop browser dialog-creation methods for @@ -30,8 +31,10 @@ Profile* profile, const EditDetails& details, Configuration configuration) { - chrome::ShowBookmarkEditorViews(parent_window, profile, details, - configuration); + auto editor = std::make_unique<BookmarkEditorView>( + profile, details.parent_node, details, configuration); + editor->Show(parent_window); + editor.release(); // BookmarkEditorView is self-deleting } void ChromeDevicePermissionsPrompt::ShowDialog() {
diff --git a/chrome/browser/ui/views/chrome_typography_provider.cc b/chrome/browser/ui/views/chrome_typography_provider.cc index ff9664f..1b0e9426 100644 --- a/chrome/browser/ui/views/chrome_typography_provider.cc +++ b/chrome/browser/ui/views/chrome_typography_provider.cc
@@ -224,7 +224,6 @@ return gfx::kGoogleBlue700; case STYLE_SECONDARY: case STYLE_SECONDARY_MONOSPACED: - case STYLE_EMPHASIZED_SECONDARY: case STYLE_HINT: return native_theme->ShouldUseDarkColors() ? gfx::kGoogleGrey500 : gfx::kGoogleGrey700;
diff --git a/chrome/browser/ui/views/dropdown_bar_host.cc b/chrome/browser/ui/views/dropdown_bar_host.cc index 9cda042..138b246 100644 --- a/chrome/browser/ui/views/dropdown_bar_host.cc +++ b/chrome/browser/ui/views/dropdown_bar_host.cc
@@ -23,7 +23,8 @@ // DropdownBarHost, public: DropdownBarHost::DropdownBarHost(BrowserView* browser_view) - : browser_view_(browser_view), + : AnimationDelegateViews(browser_view), + browser_view_(browser_view), view_(nullptr), delegate_(nullptr), focus_manager_(nullptr), @@ -192,7 +193,7 @@ } //////////////////////////////////////////////////////////////////////////////// -// DropdownBarHost, gfx::AnimationDelegate implementation: +// DropdownBarHost, views::AnimationDelegateViews implementation: void DropdownBarHost::AnimationProgressed(const gfx::Animation* animation) { // First, we calculate how many pixels to slide the widget.
diff --git a/chrome/browser/ui/views/dropdown_bar_host.h b/chrome/browser/ui/views/dropdown_bar_host.h index 6180f6c1..85b6464 100644 --- a/chrome/browser/ui/views/dropdown_bar_host.h +++ b/chrome/browser/ui/views/dropdown_bar_host.h
@@ -10,9 +10,9 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "content/public/browser/native_web_keyboard_event.h" -#include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" +#include "ui/views/animation/animation_delegate_views.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/widget/widget_delegate.h" @@ -40,7 +40,7 @@ //////////////////////////////////////////////////////////////////////////////// class DropdownBarHost : public ui::AcceleratorTarget, public views::FocusChangeListener, - public gfx::AnimationDelegate, + public views::AnimationDelegateViews, public views::WidgetDelegate { public: explicit DropdownBarHost(BrowserView* browser_view); @@ -87,7 +87,7 @@ bool AcceleratorPressed(const ui::Accelerator& accelerator) override = 0; bool CanHandleAccelerators() const override = 0; - // gfx::AnimationDelegate implementation: + // views::AnimationDelegateViews implementation: void AnimationProgressed(const gfx::Animation* animation) override; void AnimationEnded(const gfx::Animation* animation) override;
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc index d42a425..6a0d4fb 100644 --- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc +++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -126,8 +126,7 @@ anchor_view->SetVisible(true); active_bubble_ = new ToolbarActionsBarBubbleViews( - anchor_view, gfx::Point(), anchor_view != extensions_button_, - std::move(controller)); + anchor_view, anchor_view != extensions_button_, std::move(controller)); views::BubbleDialogDelegateView::CreateBubble(active_bubble_) ->AddObserver(this); active_bubble_->Show();
diff --git a/ui/views/layout/interpolating_layout_manager.cc b/chrome/browser/ui/views/layout/interpolating_layout_manager.cc similarity index 66% rename from ui/views/layout/interpolating_layout_manager.cc rename to chrome/browser/ui/views/layout/interpolating_layout_manager.cc index 496b934..b62bb84 100644 --- a/ui/views/layout/interpolating_layout_manager.cc +++ b/chrome/browser/ui/views/layout/interpolating_layout_manager.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/views/layout/interpolating_layout_manager.h" +#include "chrome/browser/ui/views/layout/interpolating_layout_manager.h" #include <memory> #include <utility> @@ -10,50 +10,11 @@ #include "ui/gfx/animation/tween.h" #include "ui/views/view.h" -namespace views { - -namespace { - -using ChildLayout = LayoutManagerBase::ChildLayout; -using ProposedLayout = LayoutManagerBase::ProposedLayout; - -// Returns a layout that's linearly interpolated between |first_layout| and -// |second_layout| by |percent|. See gfx::Tween::LinearIntValueBetween() for -// the exact math involved. -ProposedLayout Interpolate(double value, - const ProposedLayout& start, - const ProposedLayout& target) { - ProposedLayout layout; - const size_t num_children = start.child_layouts.size(); - DCHECK_EQ(num_children, target.child_layouts.size()); - - // Interpolate the host size. - // TODO(dfried): Add direct gfx::Size interpolation to gfx::Tween. - const gfx::Size size = - gfx::Tween::SizeValueBetween(value, start.host_size, target.host_size); - layout.host_size = gfx::Size(size.width(), size.height()); - - // Interpolate the child bounds. - for (size_t i = 0; i < num_children; ++i) { - const ChildLayout& start_child = start.child_layouts[i]; - const ChildLayout& target_child = target.child_layouts[i]; - DCHECK_EQ(start_child.child_view, target_child.child_view); - layout.child_layouts.emplace_back( - ChildLayout{start_child.child_view, - gfx::Tween::RectValueBetween(value, start_child.bounds, - target_child.bounds), - start_child.visible && target_child.visible}); - } - return layout; -} - -} // namespace - InterpolatingLayoutManager::InterpolatingLayoutManager() {} InterpolatingLayoutManager::~InterpolatingLayoutManager() = default; InterpolatingLayoutManager& InterpolatingLayoutManager::SetOrientation( - LayoutOrientation orientation) { + views::LayoutOrientation orientation) { if (orientation_ != orientation) { orientation_ = orientation; InvalidateLayout(); @@ -63,7 +24,7 @@ void InterpolatingLayoutManager::AddLayoutInternal( std::unique_ptr<LayoutManagerBase> engine, - const Span& interpolation_range) { + const views::Span& interpolation_range) { DCHECK(engine); SyncStateTo(engine.get()); @@ -73,8 +34,8 @@ << interpolation_range.ToString(); #if DCHECK_IS_ON() - // Sanity checking to ensure interpolation ranges do not overlap (we can only - // interpolate between two layouts currently). + // Sanity checking to ensure interpolation ranges do not overlap (we can + // only interpolate between two layouts currently). auto next = result.first; ++next; if (next != embedded_layouts_.end()) @@ -89,14 +50,15 @@ InterpolatingLayoutManager::LayoutInterpolation InterpolatingLayoutManager::GetInterpolation( - const SizeBounds& size_bounds) const { + const views::SizeBounds& size_bounds) const { DCHECK(!embedded_layouts_.empty()); LayoutInterpolation result; const base::Optional<int> dimension = - orientation_ == LayoutOrientation::kHorizontal ? size_bounds.width() - : size_bounds.height(); + orientation_ == views::LayoutOrientation::kHorizontal + ? size_bounds.width() + : size_bounds.height(); // Find the larger layout that overlaps the target size. auto match = dimension ? embedded_layouts_.upper_bound({*dimension, 0}) @@ -108,7 +70,7 @@ result.first = (--match)->second.get(); // If the target size falls in an interpolation range, get the other layout. - const Span& first_span = match->first; + const views::Span& first_span = match->first; if (dimension && first_span.end() > *dimension) { DCHECK(match != embedded_layouts_.begin()) << "Primary dimension size " << (dimension ? *dimension : -1) @@ -122,9 +84,9 @@ return result; } -LayoutManagerBase::ProposedLayout +views::LayoutManagerBase::ProposedLayout InterpolatingLayoutManager::CalculateProposedLayout( - const SizeBounds& size_bounds) const { + const views::SizeBounds& size_bounds) const { // For interpolating layout we will never call this method except for fully- // specified sizes. DCHECK(size_bounds.width()); @@ -157,24 +119,27 @@ InvalidateLayout(); } -gfx::Size InterpolatingLayoutManager::GetPreferredSize(const View* host) const { +gfx::Size InterpolatingLayoutManager::GetPreferredSize( + const views::View* host) const { DCHECK_EQ(host_view(), host); DCHECK(host); return GetDefaultLayout()->GetPreferredSize(host); } -gfx::Size InterpolatingLayoutManager::GetMinimumSize(const View* host) const { +gfx::Size InterpolatingLayoutManager::GetMinimumSize( + const views::View* host) const { DCHECK_EQ(host_view(), host); DCHECK(host); return GetSmallestLayout()->GetMinimumSize(host); } -int InterpolatingLayoutManager::GetPreferredHeightForWidth(const View* host, - int width) const { - // It is in general not possible to determine what the correct - // height-for-width trade-off is while interpolating between two already- - // generated layouts because the values tend to rely on the behavior of - // individual child views at specific dimensions. +int InterpolatingLayoutManager::GetPreferredHeightForWidth( + const views::View* host, + int width) const { + // It is in general not possible to determine what the correct height-for- + // width trade-off is while interpolating between two already-generated + // layouts because the values tend to rely on the behavior of individual child + // views at specific dimensions. // // The two reasonable choices are to use the larger of the two values (with // the understanding that the height of the view may "pop" at the edge of the @@ -203,49 +168,88 @@ embedded.second->InvalidateLayout(); } -void InterpolatingLayoutManager::SetChildViewIgnoredByLayout(View* child_view, - bool ignored) { +void InterpolatingLayoutManager::SetChildViewIgnoredByLayout( + views::View* child_view, + bool ignored) { LayoutManagerBase::SetChildViewIgnoredByLayout(child_view, ignored); for (auto& embedded : embedded_layouts_) embedded.second->SetChildViewIgnoredByLayout(child_view, ignored); } -void InterpolatingLayoutManager::Installed(View* host_view) { +// static +views::LayoutManagerBase::ProposedLayout +InterpolatingLayoutManager::Interpolate(double value, + const ProposedLayout& start, + const ProposedLayout& target) { + if (value >= 1.0) + return target; + + ProposedLayout layout; + + // Interpolate the host size. + layout.host_size = + gfx::Tween::SizeValueBetween(value, start.host_size, target.host_size); + + // The views may not be listed in the same order and some views might be + // omitted from either the |start| or |target| layout. + std::map<const views::View*, size_t> start_view_to_index; + for (size_t i = 0; i < start.child_layouts.size(); ++i) + start_view_to_index.emplace(start.child_layouts[i].child_view, i); + for (const ChildLayout& target_child : target.child_layouts) { + // Try to match the view from the target with the view from the start. + const auto start_match = start_view_to_index.find(target_child.child_view); + if (start_match == start_view_to_index.end()) { + // If there is no match, make the view present but invisible. + layout.child_layouts.push_back({target_child.child_view, false}); + } else { + // Tween the two layouts. + const ChildLayout& start_child = start.child_layouts[start_match->second]; + layout.child_layouts.push_back( + {target_child.child_view, start_child.visible && target_child.visible, + gfx::Tween::RectValueBetween(value, start_child.bounds, + target_child.bounds)}); + } + } + return layout; +} + +void InterpolatingLayoutManager::Installed(views::View* host_view) { LayoutManagerBase::Installed(host_view); for (auto& embedded : embedded_layouts_) embedded.second->Installed(host_view); } -void InterpolatingLayoutManager::ViewAdded(View* host_view, View* child_view) { +void InterpolatingLayoutManager::ViewAdded(views::View* host_view, + views::View* child_view) { LayoutManagerBase::ViewAdded(host_view, child_view); for (auto& embedded : embedded_layouts_) embedded.second->ViewAdded(host_view, child_view); } -void InterpolatingLayoutManager::ViewRemoved(View* host_view, - View* child_view) { +void InterpolatingLayoutManager::ViewRemoved(views::View* host_view, + views::View* child_view) { LayoutManagerBase::ViewRemoved(host_view, child_view); for (auto& embedded : embedded_layouts_) embedded.second->ViewRemoved(host_view, child_view); } -void InterpolatingLayoutManager::ViewVisibilitySet(View* host, - View* view, +void InterpolatingLayoutManager::ViewVisibilitySet(views::View* host, + views::View* view, bool visible) { LayoutManagerBase::ViewVisibilitySet(host, view, visible); for (auto& embedded : embedded_layouts_) embedded.second->ViewVisibilitySet(host, view, visible); } -const LayoutManagerBase* InterpolatingLayoutManager::GetDefaultLayout() const { +const views::LayoutManagerBase* InterpolatingLayoutManager::GetDefaultLayout() + const { DCHECK(!embedded_layouts_.empty()); return default_layout_ ? default_layout_ : embedded_layouts_.rbegin()->second.get(); } -const LayoutManagerBase* InterpolatingLayoutManager::GetSmallestLayout() const { +const views::LayoutManagerBase* InterpolatingLayoutManager::GetSmallestLayout() + const { DCHECK(!embedded_layouts_.empty()); return embedded_layouts_.begin()->second.get(); } - -} // namespace views
diff --git a/ui/views/layout/interpolating_layout_manager.h b/chrome/browser/ui/views/layout/interpolating_layout_manager.h similarity index 67% rename from ui/views/layout/interpolating_layout_manager.h rename to chrome/browser/ui/views/layout/interpolating_layout_manager.h index 0cc084c..7923c719 100644 --- a/ui/views/layout/interpolating_layout_manager.h +++ b/chrome/browser/ui/views/layout/interpolating_layout_manager.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_ -#define UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_ +#ifndef CHROME_BROWSER_UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_ +#define CHROME_BROWSER_UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_ #include <map> #include <memory> @@ -12,8 +12,6 @@ #include "ui/views/layout/flex_layout_types.h" #include "ui/views/layout/layout_manager_base.h" -namespace views { - // Layout which interpolates between multiple embedded LayoutManagerBase // layouts. // @@ -47,13 +45,14 @@ // Note that behavior when interpolation ranges overlap is undefined, but will // be guaranteed to at least be the result of mixing two adjacent layouts that // fall over the range in a way that is not completely irrational. -class VIEWS_EXPORT InterpolatingLayoutManager : public LayoutManagerBase { +class InterpolatingLayoutManager : public views::LayoutManagerBase { public: InterpolatingLayoutManager(); ~InterpolatingLayoutManager() override; - InterpolatingLayoutManager& SetOrientation(LayoutOrientation orientation); - LayoutOrientation orientation() const { return orientation_; } + InterpolatingLayoutManager& SetOrientation( + views::LayoutOrientation orientation); + views::LayoutOrientation orientation() const { return orientation_; } // Adds a layout which starts and finished phasing in at |start_interpolation| // and |end_interpolation|, respectively. Currently, having more than one @@ -63,7 +62,7 @@ // a typed raw pointer to the added layout engine. template <class T> T* AddLayout(std::unique_ptr<T> layout_manager, - const Span& interpolation_range = Span()) { + const views::Span& interpolation_range = views::Span()) { T* const temp = layout_manager.get(); AddLayoutInternal(std::move(layout_manager), interpolation_range); return temp; @@ -75,20 +74,31 @@ void SetDefaultLayout(LayoutManagerBase* default_layout); // LayoutManagerBase: - gfx::Size GetPreferredSize(const View* host) const override; - gfx::Size GetMinimumSize(const View* host) const override; - int GetPreferredHeightForWidth(const View* host, int width) const override; + gfx::Size GetPreferredSize(const views::View* host) const override; + gfx::Size GetMinimumSize(const views::View* host) const override; + int GetPreferredHeightForWidth(const views::View* host, + int width) const override; void InvalidateLayout() override; - void SetChildViewIgnoredByLayout(View* child_view, bool ignored) override; + void SetChildViewIgnoredByLayout(views::View* child_view, + bool ignored) override; + + // Returns a layout that's linearly interpolated between |start| and |target| + // by |value|, which should be between 0 and 1. See + // gfx::Tween::LinearIntValueBetween() for the exact math involved. + static ProposedLayout Interpolate(double value, + const ProposedLayout& start, + const ProposedLayout& target); protected: // LayoutManagerBase: - void Installed(View* host_view) override; - void ViewAdded(View* host_view, View* child_view) override; - void ViewRemoved(View* host_view, View* child_view) override; - void ViewVisibilitySet(View* host, View* view, bool visible) override; + void Installed(views::View* host_view) override; + void ViewAdded(views::View* host_view, views::View* child_view) override; + void ViewRemoved(views::View* host_view, views::View* child_view) override; + void ViewVisibilitySet(views::View* host, + views::View* view, + bool visible) override; ProposedLayout CalculateProposedLayout( - const SizeBounds& size_bounds) const override; + const views::SizeBounds& size_bounds) const override; private: // Describes an interpolation between two layouts as a pointer to each and @@ -104,12 +114,12 @@ }; void AddLayoutInternal(std::unique_ptr<LayoutManagerBase> layout, - const Span& interpolation_range); + const views::Span& interpolation_range); // Given a set of size bounds and the current layout's orientation, returns // a LayoutInterpolation providing the two layouts to interpolate between. // If only one layout applies, only |right| is set and |percent| is set to 1. - LayoutInterpolation GetInterpolation(const SizeBounds& bounds) const; + LayoutInterpolation GetInterpolation(const views::SizeBounds& bounds) const; // Returns the default layout, or the largest layout if the default has not // been set. @@ -118,15 +128,13 @@ // Returns the smallest layout; useful for calculating minimum layout size. const LayoutManagerBase* GetSmallestLayout() const; - LayoutOrientation orientation_ = LayoutOrientation::kHorizontal; + views::LayoutOrientation orientation_ = views::LayoutOrientation::kHorizontal; // Maps from interpolation range to embedded layout. - std::map<Span, std::unique_ptr<LayoutManagerBase>> embedded_layouts_; + std::map<views::Span, std::unique_ptr<LayoutManagerBase>> embedded_layouts_; LayoutManagerBase* default_layout_ = nullptr; DISALLOW_COPY_AND_ASSIGN(InterpolatingLayoutManager); }; -} // namespace views - -#endif // UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_ +#endif // CHROME_BROWSER_UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_
diff --git a/ui/views/layout/interpolating_layout_manager_unittest.cc b/chrome/browser/ui/views/layout/interpolating_layout_manager_unittest.cc similarity index 98% rename from ui/views/layout/interpolating_layout_manager_unittest.cc rename to chrome/browser/ui/views/layout/interpolating_layout_manager_unittest.cc index cf44cda..4175611 100644 --- a/ui/views/layout/interpolating_layout_manager_unittest.cc +++ b/chrome/browser/ui/views/layout/interpolating_layout_manager_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/views/layout/interpolating_layout_manager.h" +#include "chrome/browser/ui/views/layout/interpolating_layout_manager.h" #include <memory> @@ -11,7 +11,7 @@ #include "ui/views/test/test_views.h" #include "ui/views/view.h" -namespace views { +using namespace views; namespace { @@ -319,5 +319,3 @@ expected_other.child_layouts[0].bounds), actual.child_layouts[0].bounds); } - -} // namespace views
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc index 969180d..09c60a7b 100644 --- a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc +++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
@@ -37,16 +37,6 @@ #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/grid_layout.h" -namespace chrome { - -payments::PaymentRequestDialog* CreatePaymentRequestDialog( - payments::PaymentRequest* request) { - return new payments::PaymentRequestDialogView(request, - /* no observer */ nullptr); -} - -} // namespace chrome - namespace payments { namespace {
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc index 6050c46..b59debc 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view.cc +++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -64,10 +64,6 @@ // Number of times the Dice sign-in promo illustration should be shown. constexpr int kDiceSigninPromoIllustrationShowCountMax = 10; -bool IsProfileChooser(profiles::BubbleViewMode mode) { - return mode == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER; -} - BadgedProfilePhoto::BadgeType GetProfileBadgeType(Profile* profile) { if (profile->IsSupervised()) { return profile->IsChild() ? BadgedProfilePhoto::BADGE_TYPE_CHILD @@ -135,11 +131,9 @@ ProfileMenuView::ProfileMenuView(views::Button* anchor_button, Browser* browser, - profiles::BubbleViewMode view_mode, signin::GAIAServiceType service_type, signin_metrics::AccessPoint access_point) : ProfileMenuViewBase(anchor_button, browser), - view_mode_(view_mode), gaia_service_type_(service_type), access_point_(access_point), dice_enabled_(AccountConsistencyModeManager::IsDiceEnabledForProfile( @@ -181,13 +175,6 @@ this, browser())); avatar_menu_->RebuildMenu(); - Profile* profile = browser()->profile(); - signin::IdentityManager* identity_manager = - IdentityManagerFactory::GetForProfile(profile); - - if (identity_manager) - identity_manager->AddObserver(this); - if (dice_enabled_) { // Fetch DICE accounts. Note: This always includes the primary account if it // is set. @@ -195,68 +182,22 @@ signin_ui_util::GetAccountsForDicePromos(browser()->profile()); } - ShowViewOrOpenTab(view_mode_); + ShowView(avatar_menu_.get()); } void ProfileMenuView::OnAvatarMenuChanged( AvatarMenu* avatar_menu) { - if (IsProfileChooser(view_mode_)) { - // Refresh the view with the new menu. We can't just update the local copy - // as this may have been triggered by a sign out action, in which case - // the view is being destroyed. - ShowView(view_mode_, avatar_menu); - } + // Refresh the view with the new menu. We can't just update the local copy + // as this may have been triggered by a sign out action, in which case + // the view is being destroyed. + ShowView(avatar_menu); } -void ProfileMenuView::OnRefreshTokenUpdatedForAccount( - const CoreAccountInfo& account_info) { - if (view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT || - view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH) { - ShowViewOrOpenTab(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER); - } -} - -void ProfileMenuView::ShowView(profiles::BubbleViewMode view_to_display, - AvatarMenu* avatar_menu) { - if (browser()->profile()->IsSupervised() && - view_to_display == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT) { - LOG(WARNING) << "Supervised user attempted to add account"; - return; - } - - view_mode_ = view_to_display; - switch (view_mode_) { - case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN: - case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT: - case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: - // The modal sign-in view is shown in for bubble view modes. - // See |SigninViewController::ShouldShowSigninForMode|. - NOTREACHED(); - break; - case profiles::BUBBLE_VIEW_MODE_INCOGNITO: - // Covered in IncognitoView. - NOTREACHED(); - break; - case profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER: - AddProfileMenuView(avatar_menu); - break; - } +void ProfileMenuView::ShowView(AvatarMenu* avatar_menu) { + AddProfileMenuView(avatar_menu); RepopulateViewFromMenuItems(); } -void ProfileMenuView::ShowViewOrOpenTab(profiles::BubbleViewMode mode) { - if (SigninViewController::ShouldShowSigninForMode(mode)) { - // Hides the user menu if it is currently shown. The user menu automatically - // closes when it loses focus; however, on Windows, the signin modals do not - // take away focus, thus we need to manually close the bubble. - Hide(); - browser()->signin_view_controller()->ShowSignin(mode, browser(), - access_point_); - } else { - ShowView(mode, avatar_menu_.get()); - } -} - void ProfileMenuView::FocusButtonOnKeyboardOpen() { if (first_profile_button_) first_profile_button_->RequestFocus(); @@ -266,10 +207,6 @@ // Unsubscribe from everything early so that the updates do not reach the // bubble and change its state. avatar_menu_.reset(); - signin::IdentityManager* identity_manager = - IdentityManagerFactory::GetForProfile(browser()->profile()); - if (identity_manager) - identity_manager->RemoveObserver(this); } views::View* ProfileMenuView::GetInitiallyFocusedView() { @@ -346,11 +283,15 @@ signin::PrimaryAccountMutator::ClearAccountsAction::kDefault, signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS, signin_metrics::SignoutDelete::IGNORE_METRIC); - ShowViewOrOpenTab(profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN); + Hide(); + browser()->signin_view_controller()->ShowSignin( + profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN, browser(), access_point_); } break; case sync_ui_util::AUTH_ERROR: - ShowViewOrOpenTab(profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH); + Hide(); + browser()->signin_view_controller()->ShowSignin( + profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH, browser(), access_point_); break; case sync_ui_util::UPGRADE_CLIENT_ERROR: chrome::OpenUpdateChromeDialog(browser()); @@ -378,7 +319,9 @@ PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_NAME); } } else if (sender == signin_current_profile_button_) { - ShowViewOrOpenTab(profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN); + Hide(); + browser()->signin_view_controller()->ShowSignin( + profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN, browser(), access_point_); } else if (sender == signin_with_gaia_account_button_) { DCHECK(dice_signin_button_view_->account()); Hide();
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.h b/chrome/browser/ui/views/profiles/profile_menu_view.h index 08ef753d..32f2986 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view.h +++ b/chrome/browser/ui/views/profiles/profile_menu_view.h
@@ -16,10 +16,8 @@ #include "chrome/browser/profiles/avatar_menu_observer.h" #include "chrome/browser/sync/sync_ui_util.h" #include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/profile_chooser_constants.h" #include "chrome/browser/ui/views/profiles/profile_menu_view_base.h" #include "components/signin/core/browser/signin_header_helper.h" -#include "components/signin/public/identity_manager/identity_manager.h" #include "ui/views/controls/styled_label.h" namespace views { @@ -33,13 +31,10 @@ // This bubble view is displayed when the user clicks on the avatar button. // It displays a list of profiles and allows users to switch between profiles. -class ProfileMenuView : public ProfileMenuViewBase, - public AvatarMenuObserver, - public signin::IdentityManager::Observer { +class ProfileMenuView : public ProfileMenuViewBase, public AvatarMenuObserver { public: ProfileMenuView(views::Button* anchor_button, Browser* browser, - profiles::BubbleViewMode view_mode, signin::GAIAServiceType service_type, signin_metrics::AccessPoint access_point); ~ProfileMenuView() override; @@ -70,10 +65,6 @@ // AvatarMenuObserver: void OnAvatarMenuChanged(AvatarMenu* avatar_menu) override; - // signin::IdentityManager::Observer overrides. - void OnRefreshTokenUpdatedForAccount( - const CoreAccountInfo& account_info) override; - // We normally close the bubble any time it becomes inactive but this can lead // to flaky tests where unexpected UI events are triggering this behavior. // Tests set this to "false" for more consistent operation. @@ -81,11 +72,8 @@ void Reset(); - // Shows the bubble with the |view_to_display|. - void ShowView(profiles::BubbleViewMode view_to_display, - AvatarMenu* avatar_menu); - // Shows the bubble view or opens a tab based on given |mode|. - void ShowViewOrOpenTab(profiles::BubbleViewMode mode); + // Shows the bubble view. + void ShowView(AvatarMenu* avatar_menu); // Adds the profile chooser view. void AddProfileMenuView(AvatarMenu* avatar_menu); @@ -178,9 +166,6 @@ // View for the signin/turn-on-sync button in the dice promo. DiceSigninButtonView* dice_signin_button_view_; - // Active view mode. - profiles::BubbleViewMode view_mode_; - // The GAIA service type provided in the response header. signin::GAIAServiceType gaia_service_type_;
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc index e2f10326..5672e95 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc +++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -72,17 +72,16 @@ if (IsShowing()) return; - DCHECK_EQ(browser->profile()->IsIncognitoProfile(), - view_mode == profiles::BUBBLE_VIEW_MODE_INCOGNITO); - ProfileMenuViewBase* bubble; if (view_mode == profiles::BUBBLE_VIEW_MODE_INCOGNITO) { + DCHECK(browser->profile()->IsIncognitoProfile()); bubble = new IncognitoMenuView(anchor_button, browser); } else { + DCHECK_EQ(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, view_mode); #if !defined(OS_CHROMEOS) - bubble = new ProfileMenuView(anchor_button, browser, view_mode, - manage_accounts_params.service_type, - access_point); + bubble = + new ProfileMenuView(anchor_button, browser, + manage_accounts_params.service_type, access_point); #else NOTREACHED(); return;
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc index 6c71348b..bdae240 100644 --- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc +++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
@@ -51,12 +51,15 @@ num_tabs_--; // RemoveTabAt() expects the controller state to have been updated already. const bool was_active = index == active_index_; - if (active_index_ > index) { + if (was_active) { + active_index_ = std::min(active_index_, num_tabs_ - 1); + selection_model_.SetSelectedIndex(active_index_); + } else if (active_index_ > index) { --active_index_; - } else if (active_index_ == index) { - SetActiveIndex(std::min(active_index_, num_tabs_ - 1)); } tab_strip_->RemoveTabAt(nullptr, index, was_active); + if (was_active && IsValidIndex(active_index_)) + tab_strip_->SetSelection(selection_model_); } void FakeBaseTabStripController::MoveTabIntoGroup(
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc index 11f9470..5ee2da4 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc +++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -1128,4 +1128,23 @@ EXPECT_EQ(0u, ListGroupHeaders().size()); } +TEST_P(TabStripTest, ChangingLayoutTypeResizesTabs) { + tab_strip_->SetBounds(0, 0, 1000, 100); + + tab_strip_->AddTabAt(0, TabRendererData(), false); + Tab* tab = tab_strip_->tab_at(0); + const int initial_height = tab->height(); + + ui::test::MaterialDesignControllerTestAPI other_layout(!GetParam()); + + CompleteAnimationAndLayout(); + if (GetParam()) { + // Touch -> normal. + EXPECT_LT(tab->height(), initial_height); + } else { + // Normal -> touch. + EXPECT_GT(tab->height(), initial_height); + } +} + INSTANTIATE_TEST_SUITE_P(, TabStripTest, ::testing::Values(false, true));
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.cc b/chrome/browser/ui/views/toolbar/browser_actions_container.cc index d473ab7..61bc70d5 100644 --- a/chrome/browser/ui/views/toolbar/browser_actions_container.cc +++ b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
@@ -319,8 +319,7 @@ } ToolbarActionsBarBubbleViews* bubble = new ToolbarActionsBarBubbleViews( - anchor_view, gfx::Point(), anchored_to_action_view, - std::move(controller)); + anchor_view, anchored_to_action_view, std::move(controller)); active_bubble_ = bubble; views::BubbleDialogDelegateView::CreateBubble(bubble); bubble->GetWidget()->AddObserver(this);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc index 99c15d26..1921528b 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
@@ -27,16 +27,14 @@ ToolbarActionsBarBubbleViews::ToolbarActionsBarBubbleViews( views::View* anchor_view, - const gfx::Point& anchor_point, bool anchored_to_action, std::unique_ptr<ToolbarActionsBarBubbleDelegate> delegate) : views::BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), delegate_(std::move(delegate)), anchored_to_action_(anchored_to_action) { + DCHECK(anchor_view); set_close_on_deactivate(delegate_->ShouldCloseOnDeactivate()); - if (!anchor_view) - SetAnchorRect(gfx::Rect(anchor_point, gfx::Size())); chrome::RecordDialogCreation(chrome::DialogIdentifier::TOOLBAR_ACTIONS_BAR); }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h index 1e105f7..72565050 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h +++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h
@@ -22,11 +22,9 @@ class ToolbarActionsBarBubbleViews : public views::BubbleDialogDelegateView, public views::ButtonListener { public: - // Creates the bubble anchored to |anchor_view| or, if that is null, to - // |anchor_point| in screen coordinates. + // Creates the bubble anchored to |anchor_view|, which may not be nullptr. ToolbarActionsBarBubbleViews( views::View* anchor_view, - const gfx::Point& anchor_point, bool anchored_to_action, std::unique_ptr<ToolbarActionsBarBubbleDelegate> delegate); ~ToolbarActionsBarBubbleViews() override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc index c021fe36..03216b7a 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc
@@ -64,7 +64,7 @@ anchor_widget_ = CreateAnchorWidget(); bool anchored_to_action = false; bubble_ = new ToolbarActionsBarBubbleViews( - anchor_widget_->GetContentsView(), gfx::Point(), anchored_to_action, + anchor_widget_->GetContentsView(), anchored_to_action, delegate->GetDelegate()); bubble_widget_ = views::BubbleDialogDelegateView::CreateBubble(bubble_); bubble_->Show(); @@ -248,8 +248,7 @@ ActionString()); delegate.set_dismiss_button_text(DismissString()); ToolbarActionsBarBubbleViews* bubble = new ToolbarActionsBarBubbleViews( - anchor_widget->GetContentsView(), gfx::Point(), false, - delegate.GetDelegate()); + anchor_widget->GetContentsView(), false, delegate.GetDelegate()); EXPECT_FALSE(delegate.shown()); EXPECT_FALSE(delegate.close_action());
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc index 7a2a3a4..87c7dab 100644 --- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -68,6 +68,7 @@ #include "chromeos/login/auth/cryptohome_key_constants.h" #include "chromeos/login/auth/saml_password_attributes.h" #include "chromeos/login/auth/user_context.h" +#include "chromeos/network/onc/certificate_scope.h" #include "chromeos/settings/cros_settings_names.h" #include "components/login/localized_values_builder.h" #include "components/policy/proto/chrome_device_policy.pb.h" @@ -1320,7 +1321,8 @@ g_browser_process->platform_part() ->browser_policy_connector_chromeos() ->GetDeviceNetworkConfigurationUpdater() - ->GetAllAuthorityCertificates()); + ->GetAllAuthorityCertificates( + chromeos::onc::CertificateScope::Default())); } LoadAuthExtension(!gaia_silent_load_ /* force */, false /* offline */);
diff --git a/chrome/chrome_cleaner/DEPS b/chrome/chrome_cleaner/DEPS index f7285e7..f69b764 100644 --- a/chrome/chrome_cleaner/DEPS +++ b/chrome/chrome_cleaner/DEPS
@@ -1,5 +1,14 @@ +# chrome_cleaner is built into a separate executable and should not inherit +# rules from chromium. +noparent = True + include_rules = [ + "+base", "+components/chrome_cleaner", + "+crypto", + "+mojo/public", "+sandbox/win/src", + "+testing", "+third_party/protobuf/src/google/protobuf", + "+url", ]
diff --git a/chrome/chrome_cleaner/chrome_utils/DEPS b/chrome/chrome_cleaner/chrome_utils/DEPS deleted file mode 100644 index 9648b97a..0000000 --- a/chrome/chrome_cleaner/chrome_utils/DEPS +++ /dev/null
@@ -1,3 +0,0 @@ -include_rules = [ - "+components/chrome_cleaner", -]
diff --git a/chrome/chrome_cleaner/cleaner/DEPS b/chrome/chrome_cleaner/cleaner/DEPS deleted file mode 100644 index 9648b97a..0000000 --- a/chrome/chrome_cleaner/cleaner/DEPS +++ /dev/null
@@ -1,3 +0,0 @@ -include_rules = [ - "+components/chrome_cleaner", -]
diff --git a/chrome/chrome_cleaner/engines/DEPS b/chrome/chrome_cleaner/engines/DEPS deleted file mode 100644 index ef8ad28..0000000 --- a/chrome/chrome_cleaner/engines/DEPS +++ /dev/null
@@ -1,3 +0,0 @@ -include_rules = [ - "+mojo/public", -]
diff --git a/chrome/chrome_cleaner/ipc/DEPS b/chrome/chrome_cleaner/ipc/DEPS index f4d373b..9243dcd6 100644 --- a/chrome/chrome_cleaner/ipc/DEPS +++ b/chrome/chrome_cleaner/ipc/DEPS
@@ -1,4 +1,3 @@ include_rules = [ "+mojo/core/embedder", - "+mojo/public/cpp", ]
diff --git a/chrome/chrome_cleaner/logging/DEPS b/chrome/chrome_cleaner/logging/DEPS index 6c0df623..7097cb4 100644 --- a/chrome/chrome_cleaner/logging/DEPS +++ b/chrome/chrome_cleaner/logging/DEPS
@@ -1,4 +1,3 @@ include_rules = [ "+net/traffic_annotation", - "+url", ]
diff --git a/chrome/chrome_cleaner/mojom/typemaps/DEPS b/chrome/chrome_cleaner/mojom/typemaps/DEPS index 0a4da77..b4c64d8 100644 --- a/chrome/chrome_cleaner/mojom/typemaps/DEPS +++ b/chrome/chrome_cleaner/mojom/typemaps/DEPS
@@ -1,9 +1,4 @@ include_rules = [ - # Allow the typemaps to access their dependencies. - '+mojo/public/cpp/bindings', - '+mojo/public/cpp/base', - '+mojo/public/cpp/system/platform_handle.h', - # Allow unit tests to set up a mojo embedder. '+mojo/core/embedder', ]
diff --git a/chrome/chrome_cleaner/os/DEPS b/chrome/chrome_cleaner/os/DEPS deleted file mode 100644 index a7d4f73..0000000 --- a/chrome/chrome_cleaner/os/DEPS +++ /dev/null
@@ -1,3 +0,0 @@ -include_rules = [ - "+components/chrome_cleaner/public/constants", -]
diff --git a/chrome/chrome_cleaner/parsers/DEPS b/chrome/chrome_cleaner/parsers/DEPS deleted file mode 100644 index 093b1d9..0000000 --- a/chrome/chrome_cleaner/parsers/DEPS +++ /dev/null
@@ -1,3 +0,0 @@ -include_rules = [ - "+mojo/public/cpp", -]
diff --git a/chrome/chrome_cleaner/settings/DEPS b/chrome/chrome_cleaner/settings/DEPS deleted file mode 100644 index ef8ad28..0000000 --- a/chrome/chrome_cleaner/settings/DEPS +++ /dev/null
@@ -1,3 +0,0 @@ -include_rules = [ - "+mojo/public", -]
diff --git a/chrome/common/extensions/api/downloads.idl b/chrome/common/extensions/api/downloads.idl index bd0c9b4..4a49058 100644 --- a/chrome/common/extensions/api/downloads.idl +++ b/chrome/common/extensions/api/downloads.idl
@@ -516,10 +516,6 @@ // |callback|: Called when the danger prompt dialog closes. static void acceptDanger(long downloadId, optional NullCallback callback); - // Initiate dragging the downloaded file to another application. Call in a - // javascript <code>ondragstart</code> handler. - static void drag(long downloadId); - // Enable or disable the gray shelf at the bottom of every window associated // with the current browser profile. The shelf will be disabled as long as // at least one extension has disabled it. Enabling the shelf while at least
diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js index 1f37123..412a3af 100644 --- a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js
@@ -199,10 +199,6 @@ item.open(); return false; }; - item.getElement('open-filename').ondragstart = function() { - item.drag(); - return false; - }; item.getElement('pause').onclick = function() { item.pause(); return false; @@ -450,10 +446,6 @@ document.getElementById('items').removeChild(this.div); }; -DownloadItem.prototype.drag = function() { - chrome.downloads.drag(this.id); -}; - DownloadItem.prototype.show = function() { chrome.downloads.show(this.id); };
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 591c345..4030cb5fe 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -3925,6 +3925,7 @@ "../browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc", "../browser/sharing/click_to_call/click_to_call_ui_controller_unittest.cc", "../browser/sharing/click_to_call/click_to_call_utils_unittest.cc", + "../browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc", "../browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc", "../browser/ui/autofill/payments/local_card_migration_bubble_controller_impl_unittest.cc", "../browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc", @@ -4883,6 +4884,7 @@ "../browser/ui/views/hover_button_unittest.cc", "../browser/ui/views/infobars/infobar_view_unittest.cc", "../browser/ui/views/intent_picker_bubble_view_unittest.cc", + "../browser/ui/views/layout/interpolating_layout_manager_unittest.cc", "../browser/ui/views/layout_provider_unittest.cc", "../browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc", "../browser/ui/views/location_bar/location_icon_view_unittest.cc",
diff --git a/chrome/test/chromedriver/log_replay/client_replay.py b/chrome/test/chromedriver/log_replay/client_replay.py index 315eb06..2405d35 100755 --- a/chrome/test/chromedriver/log_replay/client_replay.py +++ b/chrome/test/chromedriver/log_replay/client_replay.py
@@ -748,8 +748,13 @@ self._id_map[id_old] = id_new self._staged_logged_ids = None - if "sessionId" in response and self._staged_logged_session_id: - self._id_map[self._staged_logged_session_id] = response["sessionId"] + # In W3C format, the http response is a single key dict, + # where the value is another dictionary + # sessionId is contained in the nested dictionary + if ("value" in response and "sessionId" in response["value"] + and self._staged_logged_session_id): + self._id_map[self._staged_logged_session_id] = ( + response["value"]["sessionId"]) self._staged_logged_session_id = None def _IngestLoggedResponse(self, response):
diff --git a/chrome/test/chromedriver/log_replay/client_replay_unittest.py b/chrome/test/chromedriver/log_replay/client_replay_unittest.py index 912bfb1..58dc576 100755 --- a/chrome/test/chromedriver/log_replay/client_replay_unittest.py +++ b/chrome/test/chromedriver/log_replay/client_replay_unittest.py
@@ -116,6 +116,23 @@ self.assertEqual(response.GetPayloadPrimitive(), {"param2": 42}) self.assertEqual(response.session_id, _SESSION_ID) + def testIngestRealResponseInitSession(self): + real_resp = {u'value': { + u'sessionId': u'b15232d5497ec0d8300a5a1ea56f33ce', + u'capabilities': { + u'browserVersion': u'76.0.3809.100', + u'browserName': u'chrome', + } + }} + + command_sequence = client_replay.CommandSequence() + command_sequence._staged_logged_session_id = _SESSION_ID_ALT + command_sequence._IngestRealResponse(real_resp) + + self.assertEqual( + command_sequence._id_map[_SESSION_ID_ALT], _SESSION_ID) + self.assertEqual(command_sequence._staged_logged_session_id, None) + def testGetPayload_simple(self): string_buffer = StringIO.StringIO(_RESPONSE_ONLY) header = string_buffer.readline()
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_dark_dominant_color.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_dark_dominant_color.Nexus_5-19.png.sha1 index ee627614a..8a3127ee 100644 --- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_dark_dominant_color.Nexus_5-19.png.sha1 +++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_dark_dominant_color.Nexus_5-19.png.sha1
@@ -1 +1 @@ -b4ff7f7b88cbb4d7b73b370bb475a0c546b586fa \ No newline at end of file +95b1f25236225a4865abd94d1724bad724d7c3ef \ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_dark_thumbnail.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_dark_thumbnail.Nexus_5-19.png.sha1 index 02ec298..61f80700 100644 --- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_dark_thumbnail.Nexus_5-19.png.sha1 +++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_dark_thumbnail.Nexus_5-19.png.sha1
@@ -1 +1 @@ -d187a8ab5917cf503f85feeb78ba23cbd1f80ec0 \ No newline at end of file +317dc3bff0867710f7bbe01e7875bee73bef2599 \ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_light_dominant_color.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_light_dominant_color.Nexus_5-19.png.sha1 index b82dacf..996efde 100644 --- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_light_dominant_color.Nexus_5-19.png.sha1 +++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_light_dominant_color.Nexus_5-19.png.sha1
@@ -1 +1 @@ -e6a100f55ea3621417e8dc0afcd52c323aa5c79a \ No newline at end of file +14ac5323250ad7dd55f3e84bd4e852aa5a0544c3 \ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_light_thumbnail.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_light_thumbnail.Nexus_5-19.png.sha1 index 9048dc3c..9ecac9ba 100644 --- a/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_light_thumbnail.Nexus_5-19.png.sha1 +++ b/chrome/test/data/android/render_tests/ArticleSnippetsTest.NightModeEnabled-video_suggestion_with_light_thumbnail.Nexus_5-19.png.sha1
@@ -1 +1 @@ -47112bbdb0b531bfb7fbf81d0ed6bcbe83574279 \ No newline at end of file +028a4f7a66d38531661aaa613ba43807508dd16f \ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/BookmarkTest.NightModeEnabled-bookmark_manager_folder_selected.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/BookmarkTest.NightModeEnabled-bookmark_manager_folder_selected.Nexus_5-19.png.sha1 index a71e4f3..01c9e20 100644 --- a/chrome/test/data/android/render_tests/BookmarkTest.NightModeEnabled-bookmark_manager_folder_selected.Nexus_5-19.png.sha1 +++ b/chrome/test/data/android/render_tests/BookmarkTest.NightModeEnabled-bookmark_manager_folder_selected.Nexus_5-19.png.sha1
@@ -1 +1 @@ -7c70275234e91d8d976305e3f191fc2014fa156f \ No newline at end of file +754e30663c932bb8439ba53a5674a5caf05bbda2 \ No newline at end of file
diff --git a/chrome/test/data/extensions/api_test/downloads/test.js b/chrome/test/data/extensions/api_test/downloads/test.js index e10baea7..a29d9bf5 100644 --- a/chrome/test/data/extensions/api_test/downloads/test.js +++ b/chrome/test/data/extensions/api_test/downloads/test.js
@@ -12,8 +12,8 @@ var contents = [ 'download', 'search', 'pause', 'resume', 'cancel', 'getFileIcon', 'open', - 'show', 'erase', 'acceptDanger', 'drag', - 'onCreated', 'onChanged', 'onErased', 'onDeterminingFilename']; + 'show', 'erase', 'acceptDanger', 'onCreated', 'onChanged', 'onErased', + 'onDeterminingFilename']; if (!chrome.downloads || !contains_all(chrome.downloads, contents)) {
diff --git a/chrome/test/data/webui/settings/a11y/tts_subpage_a11y_test.js b/chrome/test/data/webui/settings/a11y/tts_subpage_a11y_test.js index cd0e10a..0d7e730 100644 --- a/chrome/test/data/webui/settings/a11y/tts_subpage_a11y_test.js +++ b/chrome/test/data/webui/settings/a11y/tts_subpage_a11y_test.js
@@ -9,15 +9,31 @@ // SettingsAccessibilityTest fixture. GEN_INCLUDE([ + '//chrome/test/data/webui/polymer_browser_test_base.js', 'settings_accessibility_test.js', ]); +GEN('#include "chromeos/constants/chromeos_features.h"'); + +// TODO(crbug/950007): refactor this into an OSSettingsAccessibilityTest class // eslint-disable-next-line no-var -var TtsAccessibilityTest = class extends SettingsAccessibilityTest { +var TtsAccessibilityTest = class extends PolymerTest { /** @override */ get commandLineSwitches() { return ['enable-experimental-a11y-features']; } + + /** @override */ + get featureList() { + // Always test with SplitSettings on because the pages are the same in the + // legacy combined settings and we don't want to test everything twice. + return {enabled: ['chromeos::features::kSplitSettings']}; + } + + /** @override */ + get browsePreload() { + return 'chrome://os-settings/'; + } }; AccessibilityTest.define('TtsAccessibilityTest', {
diff --git a/chrome/test/data/webui/settings/people_page_test.js b/chrome/test/data/webui/settings/people_page_test.js index a3a82c0..0021ffb 100644 --- a/chrome/test/data/webui/settings/people_page_test.js +++ b/chrome/test/data/webui/settings/people_page_test.js
@@ -33,9 +33,15 @@ // UnifiedConsentUITest suite. unifiedConsentEnabled: false, }); + if (cr.isChromeOS) { + loadTimeData.overrideValues({ + // Account Manager is tested in the Chrome OS-specific section below. + isAccountManagerEnabled: false, + }); + } }); - setup(function() { + setup(async function() { browserProxy = new TestProfileInfoBrowserProxy(); settings.ProfileInfoBrowserProxyImpl.instance_ = browserProxy; @@ -47,14 +53,9 @@ peoplePage.pageVisibility = settings.pageVisibility; document.body.appendChild(peoplePage); - return Promise - .all([ - browserProxy.whenCalled('getProfileInfo'), - syncBrowserProxy.whenCalled('getSyncStatus') - ]) - .then(function() { - Polymer.dom.flush(); - }); + await syncBrowserProxy.whenCalled('getSyncStatus'); + await browserProxy.whenCalled('getProfileInfo'); + Polymer.dom.flush(); }); teardown(function() { @@ -587,18 +588,70 @@ }); if (cr.isChromeOS) { - suite('Chrome OS with SplitSettings', function() { + /** @implements {settings.AccountManagerBrowserProxy} */ + class TestAccountManagerBrowserProxy extends TestBrowserProxy { + constructor() { + super([ + 'getAccounts', + 'addAccount', + 'reauthenticateAccount', + 'removeAccount', + 'showWelcomeDialogIfRequired', + ]); + } + + /** @override */ + getAccounts() { + this.methodCalled('getAccounts'); + return Promise.resolve([{ + id: '123', + accountType: 1, + isDeviceAccount: false, + isSignedIn: true, + unmigrated: false, + fullName: 'Primary Account', + email: 'user@gmail.com', + pic: 'data:image/png;base64,primaryAccountPicData', + }]); + } + + /** @override */ + addAccount() { + this.methodCalled('addAccount'); + } + + /** @override */ + reauthenticateAccount(account_email) { + this.methodCalled('reauthenticateAccount', account_email); + } + + /** @override */ + removeAccount(account) { + this.methodCalled('removeAccount', account); + } + + /** @override */ + showWelcomeDialogIfRequired() { + this.methodCalled('showWelcomeDialogIfRequired'); + } + } + + suite('Chrome OS', function() { /** @type {SettingsPeoplePageElement} */ let peoplePage = null; /** @type {settings.SyncBrowserProxy} */ let browserProxy = null; /** @type {settings.ProfileInfoBrowserProxy} */ let profileInfoBrowserProxy = null; + /** @type {settings.AccountManagerBrowserProxy} */ + let accountManagerBrowserProxy = null; suiteSetup(function() { loadTimeData.overrideValues({ // Simulate SplitSettings (OS settings in their own surface). showOSSettings: false, + // Simulate ChromeOSAccountManager (Google Accounts support). + isAccountManagerEnabled: true, }); }); @@ -610,19 +663,33 @@ settings.ProfileInfoBrowserProxyImpl.instance_ = profileInfoBrowserProxy; + accountManagerBrowserProxy = new TestAccountManagerBrowserProxy(); + settings.AccountManagerBrowserProxyImpl.instance_ = + accountManagerBrowserProxy; + PolymerTest.clearBody(); peoplePage = document.createElement('settings-people-page'); peoplePage.pageVisibility = settings.pageVisibility; document.body.appendChild(peoplePage); - Polymer.dom.flush(); + await accountManagerBrowserProxy.whenCalled('getAccounts'); await browserProxy.whenCalled('getSyncStatus'); + Polymer.dom.flush(); }); teardown(function() { peoplePage.remove(); }); + test('GAIA name and picture', async () => { + chai.assert.include( + peoplePage.$$('#profile-icon').style.backgroundImage, + 'data:image/png;base64,primaryAccountPicData'); + assertEquals( + 'Primary Account', + peoplePage.$$('#profile-name').textContent.trim()); + }); + test('clicking profile row does not open change picture page', () => { // Simulate a signed-in user. sync_test_util.simulateSyncStatus({
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn index edc93537..e161ec5 100644 --- a/chrome/updater/BUILD.gn +++ b/chrome/updater/BUILD.gn
@@ -85,7 +85,7 @@ ] if (is_win) { - deps += [ "//chrome/updater/win:unittest" ] + deps += [ "//chrome/updater/win:updater_tests" ] data_deps = [ "//chrome/updater/win:updater",
diff --git a/chrome/updater/updater_constants.cc b/chrome/updater/updater_constants.cc index ed9df7edd..281447b 100644 --- a/chrome/updater/updater_constants.cc +++ b/chrome/updater/updater_constants.cc
@@ -11,6 +11,7 @@ const char kInstall[] = "install"; const char kUninstall[] = "uninstall"; const char kTestSwitch[] = "test"; +const char kInitDoneNotifierSwitch[] = "init-done-notifier"; const char kNoRateLimit[] = "--no-rate-limit";
diff --git a/chrome/updater/updater_constants.h b/chrome/updater/updater_constants.h index 5fc5e69..37fadddf6 100644 --- a/chrome/updater/updater_constants.h +++ b/chrome/updater/updater_constants.h
@@ -28,6 +28,10 @@ // https://bugs.chromium.org/p/crashpad/issues/detail?id=23 extern const char kNoRateLimit[]; +// The handle of an event to signal when the initialization of the main process +// is complete. +extern const char kInitDoneNotifierSwitch[]; + // URLs. // // Omaha server end point.
diff --git a/chrome/updater/win/BUILD.gn b/chrome/updater/win/BUILD.gn index 8aaf244..d27a45fc 100644 --- a/chrome/updater/win/BUILD.gn +++ b/chrome/updater/win/BUILD.gn
@@ -5,7 +5,7 @@ import("//chrome/process_version_rc_template.gni") import("//testing/test.gni") -# This target build the updater executable and its installer. +# This target builds the updater executable, its installer, and unittests. group("win") { deps = [ ":updater", @@ -64,18 +64,29 @@ "setup/setup.h", "setup/uninstall.cc", "setup/uninstall.h", + "task_scheduler.cc", + "task_scheduler.h", "util.cc", "util.h", ] + defines = [ "SECURITY_WIN32" ] + + libs = [ + "secur32.lib", + "taskschd.lib", + ] + deps = [ "//base", "//chrome/installer/util:with_no_strings", + "//chrome/updater:common", "//components/update_client", ] } -source_set("unittest") { +# Tests built into Chrome's unit_tests.exe. +source_set("updater_tests") { testonly = true sources = [ @@ -90,6 +101,27 @@ ] data_deps = [ + ":updater_unittests", "//chrome/updater/win/installer:installer_unittest", ] } + +# Specific tests which must run in their own process due to COM, security, or +# test isolation requirements. +test("updater_unittests") { + testonly = true + + sources = [ + "//chrome/updater/win/test/test_main.cc", + "task_scheduler_unittest.cc", + ] + + deps = [ + ":code", + "//base", + "//base/test:test_support", + "//chrome/updater/win/test:test_executables", + "//chrome/updater/win/test:test_strings", + "//testing/gtest", + ] +}
diff --git a/chrome/updater/win/task_scheduler.cc b/chrome/updater/win/task_scheduler.cc new file mode 100644 index 0000000..bdbc206 --- /dev/null +++ b/chrome/updater/win/task_scheduler.cc
@@ -0,0 +1,961 @@ +// 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/updater/win/task_scheduler.h" + +#include <mstask.h> +#include <oleauto.h> +#include <security.h> +#include <taskschd.h> +#include <wrl/client.h> + +#include <utility> + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/native_library.h" +#include "base/path_service.h" +#include "base/strings/strcat.h" +#include "base/strings/string16.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "base/win/scoped_bstr.h" +#include "base/win/scoped_co_mem.h" +#include "base/win/scoped_handle.h" +#include "base/win/scoped_variant.h" +#include "base/win/windows_version.h" +#include "chrome/updater/win/util.h" + +namespace updater { + +namespace { + +// Names of the TaskSchedulerV2 libraries so we can pin them below. +const wchar_t kV2Library[] = L"taskschd.dll"; + +// Text for times used in the V2 API of the Task Scheduler. +const wchar_t kOneHourText[] = L"PT1H"; +const wchar_t kFiveHoursText[] = L"PT5H"; +const wchar_t kZeroMinuteText[] = L"PT0M"; +const wchar_t kFifteenMinutesText[] = L"PT15M"; +const wchar_t kTwentyFourHoursText[] = L"PT24H"; + +// Most of the users with pending logs succeeds within 7 days, so no need to +// try for longer than that, especially for those who keep crashing. +const int kNumDaysBeforeExpiry = 7; +const size_t kNumDeleteTaskRetry = 3; +const size_t kDeleteRetryDelayInMs = 100; + +// Return |timestamp| in the following string format YYYY-MM-DDTHH:MM:SS. +base::string16 GetTimestampString(const base::Time& timestamp) { + base::Time::Exploded exploded_time; + // The Z timezone info at the end of the string means UTC. + timestamp.UTCExplode(&exploded_time); + return base::StringPrintf(L"%04d-%02d-%02dT%02d:%02d:%02dZ", + exploded_time.year, exploded_time.month, + exploded_time.day_of_month, exploded_time.hour, + exploded_time.minute, exploded_time.second); +} + +bool LocalSystemTimeToUTCFileTime(const SYSTEMTIME& system_time_local, + FILETIME* file_time_utc) { + DCHECK(file_time_utc); + SYSTEMTIME system_time_utc = {}; + if (!::TzSpecificLocalTimeToSystemTime(nullptr, &system_time_local, + &system_time_utc) || + !::SystemTimeToFileTime(&system_time_utc, file_time_utc)) { + PLOG(ERROR) << "Failed to convert local system time to UTC file time."; + return false; + } + return true; +} + +bool UTCFileTimeToLocalSystemTime(const FILETIME& file_time_utc, + SYSTEMTIME* system_time_local) { + DCHECK(system_time_local); + SYSTEMTIME system_time_utc = {}; + if (!::FileTimeToSystemTime(&file_time_utc, &system_time_utc) || + !::SystemTimeToTzSpecificLocalTime(nullptr, &system_time_utc, + system_time_local)) { + PLOG(ERROR) << "Failed to convert file time to UTC local system."; + return false; + } + return true; +} + +bool GetCurrentUser(base::win::ScopedBstr* user_name) { + DCHECK(user_name); + ULONG user_name_size = 256; + // Paranoia... ;-) + DCHECK_EQ(sizeof(OLECHAR), sizeof(WCHAR)); + if (!::GetUserNameExW( + NameSamCompatible, + user_name->AllocateBytes(user_name_size * sizeof(OLECHAR)), + &user_name_size)) { + if (::GetLastError() != ERROR_MORE_DATA) { + PLOG(ERROR) << "GetUserNameEx failed."; + return false; + } + if (!::GetUserNameExW( + NameSamCompatible, + user_name->AllocateBytes(user_name_size * sizeof(OLECHAR)), + &user_name_size)) { + DCHECK_NE(static_cast<DWORD>(ERROR_MORE_DATA), ::GetLastError()); + PLOG(ERROR) << "GetUserNameEx failed."; + return false; + } + } + return true; +} + +void PinModule(const wchar_t* module_name) { + // Force the DLL to stay loaded until program termination. We have seen + // cases where it gets unloaded even though we still have references to + // the objects we just CoCreated. + base::NativeLibrary module_handle = nullptr; + if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, module_name, + &module_handle)) { + PLOG(ERROR) << "Failed to pin '" << module_name << "'."; + } +} + +// A task scheduler class uses the V2 API of the task scheduler. +class TaskSchedulerV2 final : public TaskScheduler { + public: + static bool Initialize() { + DCHECK(!task_service_); + DCHECK(!root_task_folder_); + + HRESULT hr = + ::CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&task_service_)); + if (FAILED(hr)) { + PLOG(ERROR) << "CreateInstance failed for CLSID_TaskScheduler. " + << std::hex << hr; + return false; + } + hr = task_service_->Connect(base::win::ScopedVariant::kEmptyVariant, + base::win::ScopedVariant::kEmptyVariant, + base::win::ScopedVariant::kEmptyVariant, + base::win::ScopedVariant::kEmptyVariant); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to connect to task service. " << std::hex << hr; + return false; + } + hr = task_service_->GetFolder(base::win::ScopedBstr(L"\\"), + &root_task_folder_); + if (FAILED(hr)) { + LOG(ERROR) << "Can't get task service folder. " << std::hex << hr; + return false; + } + PinModule(kV2Library); + return true; + } + + static void Terminate() { + root_task_folder_.Reset(); + task_service_.Reset(); + } + + TaskSchedulerV2() { + DCHECK(task_service_); + DCHECK(root_task_folder_); + } + + // TaskScheduler overrides. + bool IsTaskRegistered(const wchar_t* task_name) override { + DCHECK(task_name); + if (!root_task_folder_) + return false; + + return GetTask(task_name, nullptr); + } + + bool GetNextTaskRunTime(const wchar_t* task_name, + base::Time* next_run_time) override { + DCHECK(task_name); + DCHECK(next_run_time); + if (!root_task_folder_) + return false; + + Microsoft::WRL::ComPtr<IRegisteredTask> registered_task; + if (!GetTask(task_name, ®istered_task)) + return false; + + // We unfortunately can't use get_NextRunTime because of a known bug which + // requires hotfix: http://support.microsoft.com/kb/2495489/en-us. So fetch + // one of the run times in the next day. + // Also, although it's not obvious from MSDN, IRegisteredTask::GetRunTimes + // expects local time. + SYSTEMTIME start_system_time = {}; + GetLocalTime(&start_system_time); + + base::Time tomorrow(base::Time::NowFromSystemTime() + + base::TimeDelta::FromDays(1)); + SYSTEMTIME end_system_time = {}; + if (!UTCFileTimeToLocalSystemTime(tomorrow.ToFileTime(), &end_system_time)) + return false; + + DWORD num_run_times = 1; + SYSTEMTIME* raw_run_times = nullptr; + HRESULT hr = registered_task->GetRunTimes( + &start_system_time, &end_system_time, &num_run_times, &raw_run_times); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to GetRunTimes, " << std::hex << hr; + return false; + } + + if (num_run_times == 0) + return false; + + base::win::ScopedCoMem<SYSTEMTIME> run_times; + run_times.Reset(raw_run_times); + // Again, although unclear from MSDN, IRegisteredTask::GetRunTimes returns + // local times. + FILETIME file_time = {}; + if (!LocalSystemTimeToUTCFileTime(run_times[0], &file_time)) + return false; + *next_run_time = base::Time::FromFileTime(file_time); + return true; + } + + bool SetTaskEnabled(const wchar_t* task_name, bool enabled) override { + DCHECK(task_name); + if (!root_task_folder_) + return false; + + Microsoft::WRL::ComPtr<IRegisteredTask> registered_task; + if (!GetTask(task_name, ®istered_task)) { + LOG(ERROR) << "Failed to find the task " << task_name + << " to enable/disable"; + return false; + } + + HRESULT hr; + hr = registered_task->put_Enabled(enabled ? VARIANT_TRUE : VARIANT_FALSE); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to set enabled status of task named " << task_name + << ". " << std::hex << hr; + return false; + } + return true; + } + + bool IsTaskEnabled(const wchar_t* task_name) override { + DCHECK(task_name); + if (!root_task_folder_) + return false; + + Microsoft::WRL::ComPtr<IRegisteredTask> registered_task; + if (!GetTask(task_name, ®istered_task)) + return false; + + HRESULT hr; + VARIANT_BOOL is_enabled; + hr = registered_task->get_Enabled(&is_enabled); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get enabled status for task named " << task_name + << ". " << std::hex << hr << ": " + << logging::SystemErrorCodeToString(hr); + return false; + } + + return is_enabled == VARIANT_TRUE; + } + + bool GetTaskNameList(std::vector<base::string16>* task_names) override { + DCHECK(task_names); + if (!root_task_folder_) + return false; + + for (TaskIterator it(root_task_folder_.Get()); !it.done(); it.Next()) + task_names->push_back(it.name()); + return true; + } + + bool GetTaskInfo(const wchar_t* task_name, TaskInfo* info) override { + DCHECK(task_name); + DCHECK(info); + if (!root_task_folder_) + return false; + + Microsoft::WRL::ComPtr<IRegisteredTask> registered_task; + if (!GetTask(task_name, ®istered_task)) + return false; + + // Collect information into internal storage to ensure that we start with + // a clean slate and don't return partial results on error. + TaskInfo info_storage; + HRESULT hr = + GetTaskDescription(registered_task.Get(), &info_storage.description); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get description for task '" << task_name << "'. " + << std::hex << hr << ": " + << logging::SystemErrorCodeToString(hr); + return false; + } + + if (!GetTaskExecActions(registered_task.Get(), + &info_storage.exec_actions)) { + LOG(ERROR) << "Failed to get actions for task '" << task_name << "'"; + return false; + } + + hr = GetTaskLogonType(registered_task.Get(), &info_storage.logon_type); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get logon type for task '" << task_name << "'. " + << std::hex << hr << ": " + << logging::SystemErrorCodeToString(hr); + return false; + } + info_storage.name = task_name; + std::swap(*info, info_storage); + return true; + } + + bool DeleteTask(const wchar_t* task_name) override { + DCHECK(task_name); + if (!root_task_folder_) + return false; + + VLOG(1) << "Delete Task '" << task_name << "'."; + + HRESULT hr = + root_task_folder_->DeleteTask(base::win::ScopedBstr(task_name), 0); + // This can happen, e.g., while running tests, when the file system stresses + // quite a lot. Give it a few more chances to succeed. + size_t num_retries_left = kNumDeleteTaskRetry; + + if (FAILED(hr)) { + while ((hr == HRESULT_FROM_WIN32(ERROR_TRANSACTION_NOT_ACTIVE) || + hr == HRESULT_FROM_WIN32(ERROR_TRANSACTION_ALREADY_ABORTED)) && + --num_retries_left && IsTaskRegistered(task_name)) { + LOG(WARNING) << "Retrying delete task because transaction not active, " + << std::hex << hr << "."; + hr = root_task_folder_->DeleteTask(base::win::ScopedBstr(task_name), 0); + ::Sleep(kDeleteRetryDelayInMs); + } + if (!IsTaskRegistered(task_name)) + hr = S_OK; + } + + if (FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { + PLOG(ERROR) << "Can't delete task. " << std::hex << hr; + return false; + } + + DCHECK(!IsTaskRegistered(task_name)); + return true; + } + + bool RegisterTask(const wchar_t* task_name, + const wchar_t* task_description, + const base::CommandLine& run_command, + TriggerType trigger_type, + bool hidden) override { + DCHECK(task_name); + DCHECK(task_description); + if (!DeleteTask(task_name)) + return false; + + // Create the task definition object to create the task. + Microsoft::WRL::ComPtr<ITaskDefinition> task; + DCHECK(task_service_); + HRESULT hr = task_service_->NewTask(0, &task); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't create new task. " << std::hex << hr; + return false; + } + + base::win::ScopedBstr user_name; + if (!GetCurrentUser(&user_name)) + return false; + + if (trigger_type != TRIGGER_TYPE_NOW) { + // Allow the task to run elevated on startup. + Microsoft::WRL::ComPtr<IPrincipal> principal; + hr = task->get_Principal(&principal); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't get principal. " << std::hex << hr; + return false; + } + + hr = principal->put_UserId(user_name); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put user id. " << std::hex << hr; + return false; + } + + hr = principal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put logon type. " << std::hex << hr; + return false; + } + } + + Microsoft::WRL::ComPtr<IRegistrationInfo> registration_info; + hr = task->get_RegistrationInfo(®istration_info); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't get registration info. " << std::hex << hr; + return false; + } + + hr = registration_info->put_Author(user_name); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't set registration info author. " << std::hex << hr; + return false; + } + + base::win::ScopedBstr description(task_description); + hr = registration_info->put_Description(description); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't set description. " << std::hex << hr; + return false; + } + + Microsoft::WRL::ComPtr<ITaskSettings> task_settings; + hr = task->get_Settings(&task_settings); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't get task settings. " << std::hex << hr; + return false; + } + + hr = task_settings->put_StartWhenAvailable(VARIANT_TRUE); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'StartWhenAvailable' to true. " << std::hex + << hr; + return false; + } + + // TODO(csharp): Find a way to only set this for log upload retry. + hr = task_settings->put_DeleteExpiredTaskAfter( + base::win::ScopedBstr(kZeroMinuteText)); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'DeleteExpiredTaskAfter'. " << std::hex << hr; + return false; + } + + hr = task_settings->put_DisallowStartIfOnBatteries(VARIANT_FALSE); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'DisallowStartIfOnBatteries' to false. " + << std::hex << hr; + return false; + } + + hr = task_settings->put_StopIfGoingOnBatteries(VARIANT_FALSE); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'StopIfGoingOnBatteries' to false. " << std::hex + << hr; + return false; + } + + if (hidden) { + hr = task_settings->put_Hidden(VARIANT_TRUE); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'Hidden' to true. " << std::hex << hr; + return false; + } + } + + Microsoft::WRL::ComPtr<ITriggerCollection> trigger_collection; + hr = task->get_Triggers(&trigger_collection); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't get trigger collection. " << std::hex << hr; + return false; + } + + TASK_TRIGGER_TYPE2 task_trigger_type = TASK_TRIGGER_EVENT; + base::win::ScopedBstr repetition_interval; + switch (trigger_type) { + case TRIGGER_TYPE_POST_REBOOT: + task_trigger_type = TASK_TRIGGER_LOGON; + break; + case TRIGGER_TYPE_NOW: + task_trigger_type = TASK_TRIGGER_REGISTRATION; + break; + case TRIGGER_TYPE_HOURLY: + case TRIGGER_TYPE_EVERY_FIVE_HOURS: + task_trigger_type = TASK_TRIGGER_DAILY; + if (trigger_type == TRIGGER_TYPE_EVERY_FIVE_HOURS) { + repetition_interval.Reset(::SysAllocString(kFiveHoursText)); + } else if (trigger_type == TRIGGER_TYPE_HOURLY) { + repetition_interval.Reset(::SysAllocString(kOneHourText)); + } else { + NOTREACHED() << "Unknown TriggerType?"; + } + break; + default: + NOTREACHED() << "Unknown TriggerType?"; + } + + Microsoft::WRL::ComPtr<ITrigger> trigger; + hr = trigger_collection->Create(task_trigger_type, &trigger); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't create trigger of type " << task_trigger_type + << ". " << std::hex << hr; + return false; + } + + if (trigger_type == TRIGGER_TYPE_HOURLY || + trigger_type == TRIGGER_TYPE_EVERY_FIVE_HOURS) { + Microsoft::WRL::ComPtr<IDailyTrigger> daily_trigger; + hr = trigger.As(&daily_trigger); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't Query for registration trigger. " << std::hex + << hr; + return false; + } + + hr = daily_trigger->put_DaysInterval(1); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'DaysInterval' to 1, " << std::hex << hr; + return false; + } + + Microsoft::WRL::ComPtr<IRepetitionPattern> repetition_pattern; + hr = trigger->get_Repetition(&repetition_pattern); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't get 'Repetition'. " << std::hex << hr; + return false; + } + + // The duration is the time to keep repeating until the next daily + // trigger. + hr = repetition_pattern->put_Duration( + base::win::ScopedBstr(kTwentyFourHoursText)); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'Duration' to " << kTwentyFourHoursText + << ". " << std::hex << hr; + return false; + } + + hr = repetition_pattern->put_Interval(repetition_interval); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'Interval' to " << repetition_interval << ". " + << std::hex << hr; + return false; + } + + // Start now. + base::Time now(base::Time::NowFromSystemTime()); + base::win::ScopedBstr start_boundary(GetTimestampString(now)); + hr = trigger->put_StartBoundary(start_boundary); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'StartBoundary' to " << start_boundary << ". " + << std::hex << hr; + return false; + } + } + + if (trigger_type == TRIGGER_TYPE_POST_REBOOT) { + Microsoft::WRL::ComPtr<ILogonTrigger> logon_trigger; + hr = trigger.As(&logon_trigger); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't query trigger for 'ILogonTrigger'. " << std::hex + << hr; + return false; + } + + hr = logon_trigger->put_Delay(base::win::ScopedBstr(kFifteenMinutesText)); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'Delay'. " << std::hex << hr; + return false; + } + } + + // None of the triggers should go beyond kNumDaysBeforeExpiry. + base::Time expiry_date(base::Time::NowFromSystemTime() + + base::TimeDelta::FromDays(kNumDaysBeforeExpiry)); + base::win::ScopedBstr end_boundary(GetTimestampString(expiry_date)); + hr = trigger->put_EndBoundary(end_boundary); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't put 'EndBoundary' to " << end_boundary << ". " + << std::hex << hr; + return false; + } + + Microsoft::WRL::ComPtr<IActionCollection> actions; + hr = task->get_Actions(&actions); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't get actions collection. " << std::hex << hr; + return false; + } + + Microsoft::WRL::ComPtr<IAction> action; + hr = actions->Create(TASK_ACTION_EXEC, &action); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't create exec action. " << std::hex << hr; + return false; + } + + Microsoft::WRL::ComPtr<IExecAction> exec_action; + hr = action.As(&exec_action); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't query for exec action. " << std::hex << hr; + return false; + } + + base::win::ScopedBstr path(run_command.GetProgram().value()); + hr = exec_action->put_Path(path); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't set path of exec action. " << std::hex << hr; + return false; + } + + base::win::ScopedBstr args(run_command.GetArgumentsString()); + hr = exec_action->put_Arguments(args); + if (FAILED(hr)) { + PLOG(ERROR) << "Can't set arguments of exec action. " << std::hex << hr; + return false; + } + + Microsoft::WRL::ComPtr<IRegisteredTask> registered_task; + base::win::ScopedVariant user(user_name); + + DCHECK(root_task_folder_); + hr = root_task_folder_->RegisterTaskDefinition( + base::win::ScopedBstr(task_name), task.Get(), TASK_CREATE, + *user.AsInput(), // Not really input, but API expect non-const. + base::win::ScopedVariant::kEmptyVariant, TASK_LOGON_NONE, + base::win::ScopedVariant::kEmptyVariant, ®istered_task); + if (FAILED(hr)) { + LOG(ERROR) << "RegisterTaskDefinition failed. " << std::hex << hr << ": " + << logging::SystemErrorCodeToString(hr); + return false; + } + + DCHECK(IsTaskRegistered(task_name)); + + VLOG(1) << "Successfully registered: " + << run_command.GetCommandLineString(); + return true; + } + + private: + // Helper class that lets us iterate over all registered tasks. + class TaskIterator { + public: + explicit TaskIterator(ITaskFolder* task_folder) { + DCHECK(task_folder); + HRESULT hr = task_folder->GetTasks(TASK_ENUM_HIDDEN, &tasks_); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get registered tasks from folder. " + << std::hex << hr; + done_ = true; + return; + } + hr = tasks_->get_Count(&num_tasks_); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get registered tasks count. " << std::hex + << hr; + done_ = true; + return; + } + Next(); + } + + // Increment to the next valid item in the task list. Skip entries for + // which we cannot retrieve a name. + void Next() { + DCHECK(!done_); + task_.Reset(); + name_.clear(); + if (++task_index_ >= num_tasks_) { + done_ = true; + return; + } + + // Note: get_Item uses 1 based indices. + HRESULT hr = + tasks_->get_Item(base::win::ScopedVariant(task_index_ + 1), &task_); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get task at index: " << task_index_ << ". " + << std::hex << hr; + Next(); + return; + } + + base::win::ScopedBstr task_name_bstr; + hr = task_->get_Name(task_name_bstr.Receive()); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get name at index: " << task_index_ << ". " + << std::hex << hr; + Next(); + return; + } + name_ = base::string16(task_name_bstr ? task_name_bstr : L""); + } + + // Detach the currently active task and pass ownership to the caller. + // After this method has been called, the -> operator must no longer be + // used. + IRegisteredTask* Detach() { return task_.Detach(); } + + // Provide access to the current task. + IRegisteredTask* operator->() const { + IRegisteredTask* result = task_.Get(); + DCHECK(result); + return result; + } + + const base::string16& name() const { return name_; } + bool done() const { return done_; } + + private: + Microsoft::WRL::ComPtr<IRegisteredTaskCollection> tasks_; + Microsoft::WRL::ComPtr<IRegisteredTask> task_; + base::string16 name_; + long task_index_ = -1; // NOLINT, API requires a long. + long num_tasks_ = 0; // NOLINT, API requires a long. + bool done_ = false; + }; + + // Return the task with |task_name| and false if not found. |task| can be null + // when only interested in task's existence. + bool GetTask(const wchar_t* task_name, IRegisteredTask** task) { + for (TaskIterator it(root_task_folder_.Get()); !it.done(); it.Next()) { + if (::_wcsicmp(it.name().c_str(), task_name) == 0) { + if (task) + *task = it.Detach(); + return true; + } + } + return false; + } + + // Return the description of the task. + HRESULT GetTaskDescription(IRegisteredTask* task, + base::string16* description) { + DCHECK(task); + DCHECK(description); + + base::win::ScopedBstr task_name_bstr; + HRESULT hr = task->get_Name(task_name_bstr.Receive()); + base::string16 task_name = + base::string16(task_name_bstr ? task_name_bstr : L""); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get task name"; + } + + Microsoft::WRL::ComPtr<ITaskDefinition> task_info; + hr = task->get_Definition(&task_info); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get definition for task, " << task_name << ": " + << logging::SystemErrorCodeToString(hr); + return hr; + } + + Microsoft::WRL::ComPtr<IRegistrationInfo> reg_info; + hr = task_info->get_RegistrationInfo(®_info); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get registration info, " << task_name << ": " + << logging::SystemErrorCodeToString(hr); + return hr; + } + + base::win::ScopedBstr raw_description; + hr = reg_info->get_Description(raw_description.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get description, " << task_name << ": " + << logging::SystemErrorCodeToString(hr); + return hr; + } + *description = base::string16(raw_description ? raw_description : L""); + return ERROR_SUCCESS; + } + + // Return all executable actions associated with the given task. Non-exec + // actions are silently ignored. + bool GetTaskExecActions(IRegisteredTask* task, + std::vector<TaskExecAction>* actions) { + DCHECK(task); + DCHECK(actions); + Microsoft::WRL::ComPtr<ITaskDefinition> task_definition; + HRESULT hr = task->get_Definition(&task_definition); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get definition of task, " << std::hex << hr; + return false; + } + + Microsoft::WRL::ComPtr<IActionCollection> action_collection; + hr = task_definition->get_Actions(&action_collection); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get action collection, " << std::hex << hr; + return false; + } + + long actions_count = 0; // NOLINT, API requires a long. + hr = action_collection->get_Count(&actions_count); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get number of actions, " << std::hex << hr; + return false; + } + + // Find and return as many exec actions as possible in |actions| and return + // false if there were any errors on the way. Note that the indexing of + // actions is 1-based. + bool success = true; + for (long action_index = 1; // NOLINT + action_index <= actions_count; ++action_index) { + Microsoft::WRL::ComPtr<IAction> action; + hr = action_collection->get_Item(action_index, &action); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get action at index " << action_index << ", " + << std::hex << hr; + success = false; + continue; + } + + ::TASK_ACTION_TYPE action_type; + hr = action->get_Type(&action_type); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get the type of action at index " + << action_index << ", " << std::hex << hr; + success = false; + continue; + } + + // We only care about exec actions for now. The other types are + // TASK_ACTION_COM_HANDLER, TASK_ACTION_SEND_EMAIL, + // TASK_ACTION_SHOW_MESSAGE. The latter two are marked as deprecated in + // the Task Scheduler's GUI. + if (action_type != ::TASK_ACTION_EXEC) + continue; + + Microsoft::WRL::ComPtr<IExecAction> exec_action; + hr = action.As(&exec_action); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to query from action, " << std::hex << hr; + success = false; + continue; + } + + base::win::ScopedBstr application_path; + hr = exec_action->get_Path(application_path.Receive()); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get path from action, " << std::hex << hr; + success = false; + continue; + } + + base::win::ScopedBstr working_dir; + hr = exec_action->get_WorkingDirectory(working_dir.Receive()); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get working directory for action, " + << std::hex << hr; + success = false; + continue; + } + + base::win::ScopedBstr parameters; + hr = exec_action->get_Arguments(parameters.Receive()); + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to get arguments from action of task, " + << std::hex << hr; + success = false; + continue; + } + + actions->push_back( + {base::FilePath(application_path ? application_path : L""), + base::FilePath(working_dir ? working_dir : L""), + base::string16(parameters ? parameters : L"")}); + } + return success; + } + + // Return the log-on type required for the task's actions to be run. + HRESULT GetTaskLogonType(IRegisteredTask* task, uint32_t* logon_type) { + DCHECK(task); + DCHECK(logon_type); + Microsoft::WRL::ComPtr<ITaskDefinition> task_info; + HRESULT hr = task->get_Definition(&task_info); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get definition, " << std::hex << hr << ": " + << logging::SystemErrorCodeToString(hr); + return hr; + } + + Microsoft::WRL::ComPtr<IPrincipal> principal; + hr = task_info->get_Principal(&principal); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get principal info, " << std::hex << hr << ": " + << logging::SystemErrorCodeToString(hr); + return hr; + } + + TASK_LOGON_TYPE raw_logon_type; + hr = principal->get_LogonType(&raw_logon_type); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get logon type info, " << std::hex << hr << ": " + << logging::SystemErrorCodeToString(hr); + return hr; + } + + switch (raw_logon_type) { + case TASK_LOGON_INTERACTIVE_TOKEN: + *logon_type = LOGON_INTERACTIVE; + break; + case TASK_LOGON_GROUP: // fall-thru + case TASK_LOGON_PASSWORD: // fall-thru + case TASK_LOGON_SERVICE_ACCOUNT: + *logon_type = LOGON_SERVICE; + break; + case TASK_LOGON_S4U: + *logon_type = LOGON_SERVICE | LOGON_S4U; + break; + case TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD: + *logon_type = LOGON_INTERACTIVE | LOGON_SERVICE; + break; + default: + *logon_type = LOGON_UNKNOWN; + break; + } + return ERROR_SUCCESS; + } + + static Microsoft::WRL::ComPtr<ITaskService> task_service_; + static Microsoft::WRL::ComPtr<ITaskFolder> root_task_folder_; + + DISALLOW_COPY_AND_ASSIGN(TaskSchedulerV2); +}; + +Microsoft::WRL::ComPtr<ITaskService> TaskSchedulerV2::task_service_; +Microsoft::WRL::ComPtr<ITaskFolder> TaskSchedulerV2::root_task_folder_; + +} // namespace + +TaskScheduler::TaskInfo::TaskInfo() = default; + +TaskScheduler::TaskInfo::TaskInfo(const TaskScheduler::TaskInfo&) = default; + +TaskScheduler::TaskInfo::TaskInfo(TaskScheduler::TaskInfo&&) = default; + +TaskScheduler::TaskInfo& TaskScheduler::TaskInfo::operator=( + const TaskScheduler::TaskInfo&) = default; + +TaskScheduler::TaskInfo& TaskScheduler::TaskInfo::operator=( + TaskScheduler::TaskInfo&&) = default; + +TaskScheduler::TaskInfo::~TaskInfo() = default; + +// static. +bool TaskScheduler::Initialize() { + return TaskSchedulerV2::Initialize(); +} + +// static. +void TaskScheduler::Terminate() { + TaskSchedulerV2::Terminate(); +} + +// static. +std::unique_ptr<TaskScheduler> TaskScheduler::CreateInstance() { + return std::make_unique<TaskSchedulerV2>(); +} + +TaskScheduler::TaskScheduler() = default; + +} // namespace updater
diff --git a/chrome/updater/win/task_scheduler.h b/chrome/updater/win/task_scheduler.h new file mode 100644 index 0000000..2708621 --- /dev/null +++ b/chrome/updater/win/task_scheduler.h
@@ -0,0 +1,141 @@ +// 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_UPDATER_WIN_TASK_SCHEDULER_H_ +#define CHROME_UPDATER_WIN_TASK_SCHEDULER_H_ + +#include <stdint.h> + +#include <memory> +#include <vector> + +#include "base/files/file_path.h" +#include "base/macros.h" + +namespace base { +class CommandLine; +class Time; +} // namespace base + +namespace updater { + +// This class wraps a scheduled task and expose an API to parametrize a task +// before calling |Register|, or to verify its existence, or delete it. +class TaskScheduler { + public: + // The type of trigger to register for this task. + enum TriggerType { + TRIGGER_TYPE_POST_REBOOT = 0, // Only run once post-reboot. + TRIGGER_TYPE_NOW = 1, // Run right now (mainly for tests). + TRIGGER_TYPE_HOURLY = 2, // Run every hour. + TRIGGER_TYPE_EVERY_FIVE_HOURS = 3, + TRIGGER_TYPE_MAX, + }; + + // The log-on requirements for a task to be scheduled. Note that a task can + // have both the interactive and service bit set. In that case the + // interactive token will be used when available, and a stored password + // otherwise. + enum LogonType { + LOGON_UNKNOWN = 0, + + // Run the task with the user's interactive token when logged in. + LOGON_INTERACTIVE = 1 << 0, + + // The task will run whether the user is logged in or not using either a + // user/password specified at registration time, a service account or a + // service for user (S4U). + LOGON_SERVICE = 1 << 1, + + // The task is run as a service for user and as such will be on an + // invisible desktop. + LOGON_S4U = 1 << 2, + }; + + // Struct representing a single scheduled task action. + struct TaskExecAction { + base::FilePath application_path; + base::FilePath working_dir; + base::string16 arguments; + }; + + // Detailed description of a scheduled task. This type is returned by the + // GetTaskInfo() method. + struct TaskInfo { + TaskInfo(); + TaskInfo(const TaskInfo&); + TaskInfo(TaskInfo&&); + ~TaskInfo(); + TaskInfo& operator=(const TaskInfo&); + TaskInfo& operator=(TaskInfo&&); + + base::string16 name; + + // Description of the task. + base::string16 description; + + // A scheduled task can have more than one action associated with it and + // actions can be of types other than executables (for example, sending + // emails). This list however contains only the execution actions. + std::vector<TaskExecAction> exec_actions; + + // The log-on requirements for the task's actions to be run. A bit mask with + // the mapping defined by LogonType. + uint32_t logon_type = 0; + }; + + // Control the lifespan of static data for the TaskScheduler. |Initialize| + // must be called before the first call to |CreateInstance|, and not other + // methods can be called after |Terminate| was called (unless |Initialize| is + // called again). |Initialize| can't be called out of balance with + // |Terminate|. |Terminate| can be called any number of times. + static bool Initialize(); + static void Terminate(); + + static std::unique_ptr<TaskScheduler> CreateInstance(); + virtual ~TaskScheduler() {} + + // Identify whether the task is registered or not. + virtual bool IsTaskRegistered(const wchar_t* task_name) = 0; + + // Return the time of the next schedule run for the given task name. Return + // false on failure. + virtual bool GetNextTaskRunTime(const wchar_t* task_name, + base::Time* next_run_time) = 0; + + // Delete the task if it exists. No-op if the task doesn't exist. Return false + // on failure to delete an existing task. + virtual bool DeleteTask(const wchar_t* task_name) = 0; + + // Enable or disable task based on the value of |enabled|. Return true if the + // task exists and the operation succeeded. + virtual bool SetTaskEnabled(const wchar_t* task_name, bool enabled) = 0; + + // Return true if task exists and is enabled. + virtual bool IsTaskEnabled(const wchar_t* task_name) = 0; + + // List all currently registered scheduled tasks. + virtual bool GetTaskNameList(std::vector<base::string16>* task_names) = 0; + + // Return detailed information about a task. Return true if no errors were + // encountered. On error, the struct is left unmodified. + virtual bool GetTaskInfo(const wchar_t* task_name, TaskInfo* info) = 0; + + // Register the task to run the specified application and using the given + // |trigger_type|. + virtual bool RegisterTask(const wchar_t* task_name, + const wchar_t* task_description, + const base::CommandLine& run_command, + TriggerType trigger_type, + bool hidden) = 0; + + protected: + TaskScheduler(); + + DISALLOW_COPY_AND_ASSIGN(TaskScheduler); +}; + +} // namespace updater + +#endif // CHROME_UPDATER_WIN_TASK_SCHEDULER_H_
diff --git a/chrome/updater/win/task_scheduler_unittest.cc b/chrome/updater/win/task_scheduler_unittest.cc new file mode 100644 index 0000000..3b20a78 --- /dev/null +++ b/chrome/updater/win/task_scheduler_unittest.cc
@@ -0,0 +1,319 @@ +// 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/updater/win/task_scheduler.h" + +#include <taskschd.h> + +#include <memory> +#include <vector> + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "base/stl_util.h" +#include "base/strings/strcat.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "base/test/test_timeouts.h" +#include "base/time/time.h" +#include "base/win/scoped_bstr.h" +#include "base/win/scoped_variant.h" +#include "base/win/windows_version.h" +#include "chrome/updater/win/test/test_executables.h" +#include "chrome/updater/win/test/test_strings.h" +#include "chrome/updater/win/util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace updater { + +namespace { + +// The name of the tasks as will be visible in the scheduler so we know we can +// safely delete them if they get stuck for whatever reason. +const wchar_t kTaskName1[] = L"Chrome Updater Test task 1 (delete me)"; +const wchar_t kTaskName2[] = L"Chrome Updater Test task 2 (delete me)"; +// Optional descriptions for the above tasks. +const wchar_t kTaskDescription1[] = + L"Task 1 used only for Chrome Updater unit testing."; +const wchar_t kTaskDescription2[] = + L"Task 2 used only for Chrome Updater unit testing."; +// A command-line switch used in testing. +const char kTestSwitch[] = "a_switch"; + +class TaskSchedulerTests : public testing::Test { + public: + void SetUp() override { + task_scheduler_ = TaskScheduler::CreateInstance(); + // In case previous tests failed and left these tasks in the scheduler. + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2)); + ASSERT_FALSE(IsProcessRunning(kTestProcessExecutableName)); + } + + void TearDown() override { + // Make sure to not leave tasks behind. + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2)); + // Make sure every processes launched with scheduled task are completed. + ASSERT_TRUE(WaitForProcessesStopped(kTestProcessExecutableName)); + } + + protected: + std::unique_ptr<TaskScheduler> task_scheduler_; +}; + +} // namespace + +TEST_F(TaskSchedulerTests, DeleteAndIsRegistered) { + EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName1)); + + // Construct the full-path of the test executable. + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line( + executable_path.Append(kTestProcessExecutableName)); + + // Validate that the task is properly seen as registered when it is. + EXPECT_TRUE( + task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line, + TaskScheduler::TRIGGER_TYPE_NOW, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + + // Validate that a task with a similar name is not seen as registered. + EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName2)); + + // While the first one is still seen as registered, until it gets deleted. + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); + EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName1)); + // The other task should still not be registered. + EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName2)); +} + +TEST_F(TaskSchedulerTests, RunAProgramNow) { + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line( + executable_path.Append(kTestProcessExecutableName)); + + // Create a unique name for a shared event to be waited for in this process + // and signaled in the test process to confirm it was scheduled and ran. + const base::string16 event_name = + base::StrCat({kTestProcessExecutableName, L"-", + base::NumberToString16(::GetCurrentProcessId())}); + base::WaitableEvent event(base::win::ScopedHandle( + ::CreateEvent(nullptr, FALSE, FALSE, event_name.c_str()))); + ASSERT_NE(event.handle(), nullptr); + + command_line.AppendSwitchNative(kTestEventToSignal, event_name); + EXPECT_TRUE( + task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line, + TaskScheduler::TRIGGER_TYPE_NOW, false)); + EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); + base::Time next_run_time; + EXPECT_FALSE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time)); + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); +} + +TEST_F(TaskSchedulerTests, Hourly) { + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line( + executable_path.Append(kTestProcessExecutableName)); + + base::Time now(base::Time::NowFromSystemTime()); + EXPECT_TRUE( + task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line, + TaskScheduler::TRIGGER_TYPE_HOURLY, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + + base::TimeDelta one_hour(base::TimeDelta::FromHours(1)); + base::TimeDelta one_minute(base::TimeDelta::FromMinutes(1)); + + base::Time next_run_time; + EXPECT_TRUE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time)); + EXPECT_LT(next_run_time, now + one_hour + one_minute); + EXPECT_GT(next_run_time, now + one_hour - one_minute); + + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); + EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName1)); + EXPECT_FALSE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time)); +} + +TEST_F(TaskSchedulerTests, EveryFiveHours) { + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line( + executable_path.Append(kTestProcessExecutableName)); + + base::Time now(base::Time::NowFromSystemTime()); + EXPECT_TRUE(task_scheduler_->RegisterTask( + kTaskName1, kTaskDescription1, command_line, + TaskScheduler::TRIGGER_TYPE_EVERY_FIVE_HOURS, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + + base::TimeDelta six_hours(base::TimeDelta::FromHours(5)); + base::TimeDelta one_minute(base::TimeDelta::FromMinutes(1)); + + base::Time next_run_time; + EXPECT_TRUE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time)); + EXPECT_LT(next_run_time, now + six_hours + one_minute); + EXPECT_GT(next_run_time, now + six_hours - one_minute); + + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); + EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName1)); + EXPECT_FALSE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time)); +} + +TEST_F(TaskSchedulerTests, SetTaskEnabled) { + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line( + executable_path.Append(kTestProcessExecutableName)); + + EXPECT_TRUE( + task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line, + TaskScheduler::TRIGGER_TYPE_HOURLY, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + EXPECT_TRUE(task_scheduler_->IsTaskEnabled(kTaskName1)); + + EXPECT_TRUE(task_scheduler_->SetTaskEnabled(kTaskName1, true)); + EXPECT_TRUE(task_scheduler_->IsTaskEnabled(kTaskName1)); + EXPECT_TRUE(task_scheduler_->SetTaskEnabled(kTaskName1, false)); + EXPECT_FALSE(task_scheduler_->IsTaskEnabled(kTaskName1)); + EXPECT_TRUE(task_scheduler_->SetTaskEnabled(kTaskName1, true)); + EXPECT_TRUE(task_scheduler_->IsTaskEnabled(kTaskName1)); + + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); +} + +TEST_F(TaskSchedulerTests, GetTaskNameList) { + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line( + executable_path.Append(kTestProcessExecutableName)); + + EXPECT_TRUE( + task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line, + TaskScheduler::TRIGGER_TYPE_HOURLY, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + EXPECT_TRUE( + task_scheduler_->RegisterTask(kTaskName2, kTaskDescription2, command_line, + TaskScheduler::TRIGGER_TYPE_HOURLY, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName2)); + + std::vector<base::string16> task_names; + EXPECT_TRUE(task_scheduler_->GetTaskNameList(&task_names)); + EXPECT_TRUE(base::Contains(task_names, kTaskName1)); + EXPECT_TRUE(base::Contains(task_names, kTaskName2)); + + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2)); +} + +TEST_F(TaskSchedulerTests, GetTasksIncludesHidden) { + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line( + executable_path.Append(kTestProcessExecutableName)); + + EXPECT_TRUE( + task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line, + TaskScheduler::TRIGGER_TYPE_HOURLY, true)); + + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + + std::vector<base::string16> task_names; + EXPECT_TRUE(task_scheduler_->GetTaskNameList(&task_names)); + EXPECT_TRUE(base::Contains(task_names, kTaskName1)); + + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); +} + +TEST_F(TaskSchedulerTests, GetTaskInfoExecActions) { + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line1( + executable_path.Append(kTestProcessExecutableName)); + + EXPECT_TRUE(task_scheduler_->RegisterTask( + kTaskName1, kTaskDescription1, command_line1, + TaskScheduler::TRIGGER_TYPE_HOURLY, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + + TaskScheduler::TaskInfo info; + EXPECT_FALSE(task_scheduler_->GetTaskInfo(kTaskName2, &info)); + EXPECT_EQ(0UL, info.exec_actions.size()); + EXPECT_TRUE(task_scheduler_->GetTaskInfo(kTaskName1, &info)); + ASSERT_EQ(1UL, info.exec_actions.size()); + EXPECT_EQ(command_line1.GetProgram(), info.exec_actions[0].application_path); + EXPECT_EQ(command_line1.GetArgumentsString(), info.exec_actions[0].arguments); + + base::CommandLine command_line2( + executable_path.Append(kTestProcessExecutableName)); + command_line2.AppendSwitch(kTestSwitch); + EXPECT_TRUE(task_scheduler_->RegisterTask( + kTaskName2, kTaskDescription2, command_line2, + TaskScheduler::TRIGGER_TYPE_HOURLY, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName2)); + + // The |info| struct is re-used to ensure that new task information overwrites + // the previous contents of the struct. + EXPECT_TRUE(task_scheduler_->GetTaskInfo(kTaskName2, &info)); + ASSERT_EQ(1UL, info.exec_actions.size()); + EXPECT_EQ(command_line2.GetProgram(), info.exec_actions[0].application_path); + EXPECT_EQ(command_line2.GetArgumentsString(), info.exec_actions[0].arguments); + + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2)); +} + +TEST_F(TaskSchedulerTests, GetTaskInfoNameAndDescription) { + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line1( + executable_path.Append(kTestProcessExecutableName)); + + EXPECT_TRUE(task_scheduler_->RegisterTask( + kTaskName1, kTaskDescription1, command_line1, + TaskScheduler::TRIGGER_TYPE_HOURLY, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + + TaskScheduler::TaskInfo info; + EXPECT_FALSE(task_scheduler_->GetTaskInfo(kTaskName2, &info)); + EXPECT_EQ(L"", info.description); + EXPECT_EQ(L"", info.name); + + EXPECT_TRUE(task_scheduler_->GetTaskInfo(kTaskName1, &info)); + EXPECT_EQ(kTaskDescription1, info.description); + EXPECT_EQ(kTaskName1, info.name); + + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); +} + +TEST_F(TaskSchedulerTests, GetTaskInfoLogonType) { + base::FilePath executable_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path)); + base::CommandLine command_line1( + executable_path.Append(kTestProcessExecutableName)); + + EXPECT_TRUE(task_scheduler_->RegisterTask( + kTaskName1, kTaskDescription1, command_line1, + TaskScheduler::TRIGGER_TYPE_HOURLY, false)); + EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1)); + + TaskScheduler::TaskInfo info; + EXPECT_FALSE(task_scheduler_->GetTaskInfo(kTaskName2, &info)); + EXPECT_EQ(0U, info.logon_type); + EXPECT_TRUE(task_scheduler_->GetTaskInfo(kTaskName1, &info)); + EXPECT_TRUE(info.logon_type & TaskScheduler::LOGON_INTERACTIVE); + EXPECT_FALSE(info.logon_type & TaskScheduler::LOGON_SERVICE); + EXPECT_FALSE(info.logon_type & TaskScheduler::LOGON_S4U); + + EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1)); +} + +} // namespace updater
diff --git a/chrome/updater/win/test/BUILD.gn b/chrome/updater/win/test/BUILD.gn new file mode 100644 index 0000000..d1d2c72d --- /dev/null +++ b/chrome/updater/win/test/BUILD.gn
@@ -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. + +import("//testing/test.gni") + +source_set("test_strings") { + testonly = true + + sources = [ + "test_strings.cc", + "test_strings.h", + ] +} + +source_set("test_common") { + testonly = true + + sources = [ + "test_inheritable_event.cc", + "test_inheritable_event.h", + "test_initializer.cc", + "test_initializer.h", + ] +} + +source_set("test_executables") { + testonly = true + + sources = [ + "test_executables.cc", + "test_executables.h", + ] + + data_deps = [ + ":updater_test_process", + ] + + deps = [ + ":test_common", + ":test_strings", + "//base", + ] +} + +executable("updater_test_process") { + testonly = true + + sources = [ + "test_process_main.cc", + ] + + deps = [ + ":test_common", + ":test_strings", + "//base", + "//base/test:test_support", + "//build/win:default_exe_manifest", + "//chrome/updater/win:code", + ] +}
diff --git a/chrome/updater/win/test/test_executables.cc b/chrome/updater/win/test/test_executables.cc new file mode 100644 index 0000000..8f4e38e --- /dev/null +++ b/chrome/updater/win/test/test_executables.cc
@@ -0,0 +1,65 @@ +// 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/updater/win/test/test_executables.h" + +#include <memory> + +#include "base/base_paths.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/process/launch.h" +#include "base/strings/string_number_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "base/win/win_util.h" +#include "chrome/updater/updater_constants.h" +#include "chrome/updater/win/test/test_inheritable_event.h" +#include "chrome/updater/win/test/test_strings.h" + +namespace updater { + +// If you add another test executable here, also add it to the data_deps in +// the "test_executables" target of updater/win/test/BUILD.gn. +const base::char16 kTestProcessExecutableName[] = L"updater_test_process.exe"; + +base::Process LongRunningProcess(base::CommandLine* cmd) { + base::FilePath exe_dir; + if (!base::PathService::Get(base::DIR_EXE, &exe_dir)) { + LOG(ERROR) << "Failed to get the executable path, unable to create always " + "running process"; + return base::Process(); + } + + base::FilePath exe_path = exe_dir.Append(updater::kTestProcessExecutableName); + base::CommandLine command_line(exe_path); + + // This will ensure this new process will run for one minute before dying. + command_line.AppendSwitchASCII(updater::kTestSleepMinutesSwitch, "1"); + + auto init_done_event = updater::CreateInheritableEvent( + base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); + command_line.AppendSwitchNative( + updater::kInitDoneNotifierSwitch, + base::NumberToString16( + base::win::HandleToUint32(init_done_event->handle()))); + + if (cmd) + *cmd = command_line; + + base::LaunchOptions launch_options; + launch_options.handles_to_inherit.push_back(init_done_event->handle()); + base::Process result = base::LaunchProcess(command_line, launch_options); + + if (!init_done_event->TimedWait(base::TimeDelta::FromSeconds(10))) { + LOG(ERROR) << "Process did not signal"; + result.Terminate(/*exit_code=*/1, /*wait=*/false); + return base::Process(); + } + + return result; +} + +} // namespace updater
diff --git a/chrome/updater/win/test/test_executables.h b/chrome/updater/win/test/test_executables.h new file mode 100644 index 0000000..1ebe8b3 --- /dev/null +++ b/chrome/updater/win/test/test_executables.h
@@ -0,0 +1,30 @@ +// 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_UPDATER_WIN_TEST_TEST_EXECUTABLES_H_ +#define CHROME_UPDATER_WIN_TEST_TEST_EXECUTABLES_H_ + +#include "base/process/process.h" +#include "base/strings/string16.h" + +namespace base { +class CommandLine; +} // namespace base + +namespace updater { + +// The name of the service executable used for tests. +extern const base::char16 kTestServiceExecutableName[]; + +// The name of the executable used for tests. +extern const base::char16 kTestProcessExecutableName[]; + +// Creates a process that will run for a minute, which is long enough to be +// killed by a reasonably fast unit or integration test. +// Populates |command_line| with the used command line if it is not nullptr. +base::Process LongRunningProcess(base::CommandLine* command_line); + +} // namespace updater + +#endif // CHROME_UPDATER_WIN_TEST_TEST_EXECUTABLES_H_
diff --git a/chrome/updater/win/test/test_inheritable_event.cc b/chrome/updater/win/test/test_inheritable_event.cc new file mode 100644 index 0000000..1866165 --- /dev/null +++ b/chrome/updater/win/test/test_inheritable_event.cc
@@ -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. + +#include "chrome/updater/win/test/test_inheritable_event.h" + +#include <windows.h> + +#include <utility> + +#include "base/logging.h" + +namespace updater { + +std::unique_ptr<base::WaitableEvent> CreateInheritableEvent( + base::WaitableEvent::ResetPolicy reset_policy, + base::WaitableEvent::InitialState initial_state) { + SECURITY_ATTRIBUTES attributes = {sizeof(SECURITY_ATTRIBUTES)}; + attributes.bInheritHandle = true; + + HANDLE handle = ::CreateEvent( + &attributes, reset_policy == base::WaitableEvent::ResetPolicy::MANUAL, + initial_state == base::WaitableEvent::InitialState::SIGNALED, nullptr); + if (handle == nullptr || handle == INVALID_HANDLE_VALUE) { + PLOG(ERROR) << "Could not create inheritable event"; + return nullptr; + } + base::win::ScopedHandle event_handle(handle); + return std::make_unique<base::WaitableEvent>(std::move(event_handle)); +} + +} // namespace updater
diff --git a/chrome/updater/win/test/test_inheritable_event.h b/chrome/updater/win/test/test_inheritable_event.h new file mode 100644 index 0000000..3912f9d4 --- /dev/null +++ b/chrome/updater/win/test/test_inheritable_event.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_UPDATER_WIN_TEST_TEST_INHERITABLE_EVENT_H_ +#define CHROME_UPDATER_WIN_TEST_TEST_INHERITABLE_EVENT_H_ + +#include <memory> + +#include "base/synchronization/waitable_event.h" + +namespace updater { + +std::unique_ptr<base::WaitableEvent> CreateInheritableEvent( + base::WaitableEvent::ResetPolicy reset_policy, + base::WaitableEvent::InitialState initial_state); + +} // namespace updater + +#endif // CHROME_UPDATER_WIN_TEST_TEST_INHERITABLE_EVENT_H_
diff --git a/chrome/updater/win/test/test_initializer.cc b/chrome/updater/win/test/test_initializer.cc new file mode 100644 index 0000000..e84914b --- /dev/null +++ b/chrome/updater/win/test/test_initializer.cc
@@ -0,0 +1,57 @@ +// 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/updater/win/test/test_initializer.h" + +#include <memory> +#include <utility> + +#include "base/command_line.h" +#include "base/strings/string_number_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "base/time/time.h" +#include "base/win/scoped_handle.h" +#include "base/win/win_util.h" +#include "chrome/updater/updater_constants.h" + +namespace updater { + +namespace { + +std::unique_ptr<base::WaitableEvent> SignalInitializationDone() { + base::win::ScopedHandle init_done_notifier; + + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + uint32_t handle = 0; + if (command_line->HasSwitch(kInitDoneNotifierSwitch) && + base::StringToUint( + command_line->GetSwitchValueNative(kInitDoneNotifierSwitch), + &handle)) { + init_done_notifier.Set(base::win::Uint32ToHandle(handle)); + } + + std::unique_ptr<base::WaitableEvent> notifier_event; + if (init_done_notifier.IsValid()) { + notifier_event = + std::make_unique<base::WaitableEvent>(std::move(init_done_notifier)); + notifier_event->Signal(); + } + + return notifier_event; +} + +} // namespace + +void NotifyInitializationDoneForTesting() { + auto notifier_event = SignalInitializationDone(); + + // The event has ResetPolicy AUTOMATIC, so after the test is woken up it is + // immediately reset. Wait at most 5 seconds for the test to signal that + // it's ready using the same event before continuing. If the test takes + // longer than that stop waiting to prevent hangs. + if (notifier_event) + notifier_event->TimedWait(base::TimeDelta::FromSeconds(5)); +} + +} // namespace updater
diff --git a/chrome/updater/win/test/test_initializer.h b/chrome/updater/win/test/test_initializer.h new file mode 100644 index 0000000..8c38625 --- /dev/null +++ b/chrome/updater/win/test/test_initializer.h
@@ -0,0 +1,19 @@ +// 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_UPDATER_WIN_TEST_TEST_INITIALIZER_H_ +#define CHROME_UPDATER_WIN_TEST_TEST_INITIALIZER_H_ + +namespace updater { + +// Signals the event handle that was passed on the command line with +// --init-done-notifier, if it exists. Then waits for the event to be signalled +// again before continuing. This allows a test harness to pause the binary's +// execution, do some extra setup, and resume it. +// Note, this means the event must be AUTOMATIC. +void NotifyInitializationDoneForTesting(); + +} // namespace updater + +#endif // CHROME_UPDATER_WIN_TEST_TEST_INITIALIZER_H_
diff --git a/chrome/updater/win/test/test_main.cc b/chrome/updater/win/test/test_main.cc new file mode 100644 index 0000000..edeb235 --- /dev/null +++ b/chrome/updater/win/test/test_main.cc
@@ -0,0 +1,45 @@ +// 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 "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" +#include "base/win/scoped_com_initializer.h" +#include "chrome/updater/win/task_scheduler.h" +#include "chrome/updater/win/util.h" + +int main(int argc, char** argv) { + // ScopedCOMInitializer keeps COM initialized in a specific scope. We don't + // want to initialize it for sandboxed processes, so manage its lifetime with + // a unique_ptr, which will call ScopedCOMInitializer's destructor when it + // goes out of scope below. + auto scoped_com_initializer = + std::make_unique<base::win::ScopedCOMInitializer>( + base::win::ScopedCOMInitializer::kMTA); + bool success = updater::InitializeCOMSecurity(); + DCHECK(success) << "InitializeCOMSecurity() failed."; + + success = updater::TaskScheduler::Initialize(); + DCHECK(success) << "TaskScheduler::Initialize() failed."; + + // Some tests will fail if two tests try to launch test_process.exe + // simultaneously, so run the tests serially. This will still shard them and + // distribute the shards to different swarming bots, but tests will run + // serially on each bot. + base::TestSuite test_suite(argc, argv); + const int result = base::LaunchUnitTestsWithOptions( + argc, argv, + /*parallel_jobs=*/1U, // Like LaunchUnitTestsSerially + /*default_batch_limit=*/10, // Like LaunchUnitTestsSerially + false, + base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); + + updater::TaskScheduler::Terminate(); + + return result; +}
diff --git a/chrome/updater/win/test/test_process_main.cc b/chrome/updater/win/test/test_process_main.cc new file mode 100644 index 0000000..32f34b4 --- /dev/null +++ b/chrome/updater/win/test/test_process_main.cc
@@ -0,0 +1,54 @@ +// 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 <windows.h> + +#include <string> + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "base/time/time.h" +#include "chrome/updater/win/test/test_initializer.h" +#include "chrome/updater/win/test/test_strings.h" + +int main(int, char**) { + bool success = base::CommandLine::Init(0, nullptr); + DCHECK(success); + + updater::NotifyInitializationDoneForTesting(); + + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(updater::kTestSleepMinutesSwitch)) { + std::string value = + command_line->GetSwitchValueASCII(updater::kTestSleepMinutesSwitch); + int sleep_minutes = 0; + if (base::StringToInt(value, &sleep_minutes) && sleep_minutes > 0) { + VLOG(1) << "Process is sleeping for " << sleep_minutes << " minutes"; + ::Sleep(base::TimeDelta::FromMinutes(sleep_minutes).InMilliseconds()); + } else { + LOG(ERROR) << "Invalid sleep delay value " << value; + } + NOTREACHED(); + return 1; + } + + if (command_line->HasSwitch(updater::kTestEventToSignal)) { + VLOG(1) << "Process is signaling event '" << updater::kTestEventToSignal + << "'"; + base::string16 event_name = + command_line->GetSwitchValueNative(updater::kTestEventToSignal); + base::win::ScopedHandle handle( + ::OpenEvent(EVENT_ALL_ACCESS, TRUE, event_name.c_str())); + PLOG_IF(ERROR, !handle.IsValid()) + << "Cannot create event '" << updater::kTestEventToSignal << "'"; + base::WaitableEvent event(std::move(handle)); + event.Signal(); + } + + VLOG(1) << "Process ended."; + return 0; +}
diff --git a/chrome/updater/win/test/test_strings.cc b/chrome/updater/win/test/test_strings.cc new file mode 100644 index 0000000..031003f --- /dev/null +++ b/chrome/updater/win/test/test_strings.cc
@@ -0,0 +1,13 @@ +// 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/updater/win/test/test_strings.h" + +namespace updater { + +// Command line switches. +const char kTestSleepMinutesSwitch[] = "test-sleep-minutes"; +const char kTestEventToSignal[] = "test-event-to-signal"; + +} // namespace updater
diff --git a/chrome/updater/win/test/test_strings.h b/chrome/updater/win/test/test_strings.h new file mode 100644 index 0000000..ac95ff90 --- /dev/null +++ b/chrome/updater/win/test/test_strings.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_UPDATER_WIN_TEST_TEST_STRINGS_H_ +#define CHROME_UPDATER_WIN_TEST_TEST_STRINGS_H_ + +#include <windows.h> + +namespace updater { + +// Command line switches. + +// The switch to activate the sleeping action for specified delay in minutes +// before killing the process. +extern const char kTestSleepMinutesSwitch[]; + +// The switch to signal the event with the name given as a switch value. +extern const char kTestEventToSignal[]; + +} // namespace updater + +#endif // CHROME_UPDATER_WIN_TEST_TEST_STRINGS_H_
diff --git a/chrome/updater/win/util.cc b/chrome/updater/win/util.cc index 821c729..c7bf448 100644 --- a/chrome/updater/win/util.cc +++ b/chrome/updater/win/util.cc
@@ -4,13 +4,141 @@ #include "chrome/updater/win/util.h" +#include <aclapi.h> +#include <shlobj.h> #include <windows.h> +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/process/process_iterator.h" + namespace updater { +namespace { + +// The number of iterations to poll if a process is stopped correctly. +const unsigned int kMaxProcessQueryIterations = 50; + +// The sleep time in ms between each poll. +const unsigned int kProcessQueryWaitTimeMs = 100; + +} // namespace + HRESULT HRESULTFromLastError() { const auto error_code = ::GetLastError(); return (error_code != NO_ERROR) ? HRESULT_FROM_WIN32(error_code) : E_FAIL; } +bool IsProcessRunning(const wchar_t* executable) { + base::NamedProcessIterator iter(executable, nullptr); + const base::ProcessEntry* entry = iter.NextProcessEntry(); + return entry != nullptr; +} + +bool WaitForProcessesStopped(const wchar_t* executable) { + DCHECK(executable); + VLOG(1) << "Wait for processes '" << executable << "'."; + + // Wait until the process is completely stopped. + for (unsigned int iteration = 0; iteration < kMaxProcessQueryIterations; + ++iteration) { + if (!IsProcessRunning(executable)) + return true; + ::Sleep(kProcessQueryWaitTimeMs); + } + + // The process didn't terminate. + LOG(ERROR) << "Cannot stop process '" << executable << "', timeout."; + return false; +} + +// This sets up COM security to allow NetworkService, LocalService, and System +// to call back into the process. It is largely inspired by +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa378987.aspx +// static +bool InitializeCOMSecurity() { + // Create the security descriptor explicitly as follows because + // CoInitializeSecurity() will not accept the relative security descriptors + // returned by ConvertStringSecurityDescriptorToSecurityDescriptor(). + const size_t kSidCount = 5; + uint64_t* sids[kSidCount][(SECURITY_MAX_SID_SIZE + sizeof(uint64_t) - 1) / + sizeof(uint64_t)] = { + {}, {}, {}, {}, {}, + }; + + // These are ordered by most interesting ones to try first. + WELL_KNOWN_SID_TYPE sid_types[kSidCount] = { + WinBuiltinAdministratorsSid, // administrator group security identifier + WinLocalServiceSid, // local service security identifier + WinNetworkServiceSid, // network service security identifier + WinSelfSid, // personal account security identifier + WinLocalSystemSid, // local system security identifier + }; + + // This creates a security descriptor that is equivalent to the following + // security descriptor definition language (SDDL) string: + // O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS) + // (A;;0x1;;;SY)(A;;0x1;;;BA) + + // Initialize the security descriptor. + SECURITY_DESCRIPTOR security_desc = {}; + if (!::InitializeSecurityDescriptor(&security_desc, + SECURITY_DESCRIPTOR_REVISION)) + return false; + + DCHECK_EQ(kSidCount, base::size(sids)); + DCHECK_EQ(kSidCount, base::size(sid_types)); + for (size_t i = 0; i < kSidCount; ++i) { + DWORD sid_bytes = sizeof(sids[i]); + if (!::CreateWellKnownSid(sid_types[i], nullptr, sids[i], &sid_bytes)) + return false; + } + + // Setup the access control entries (ACE) for COM. You may need to modify + // the access permissions for your application. COM_RIGHTS_EXECUTE and + // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required. + EXPLICIT_ACCESS explicit_access[kSidCount] = {}; + DCHECK_EQ(kSidCount, base::size(sids)); + DCHECK_EQ(kSidCount, base::size(explicit_access)); + for (size_t i = 0; i < kSidCount; ++i) { + explicit_access[i].grfAccessPermissions = + COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + explicit_access[i].grfAccessMode = SET_ACCESS; + explicit_access[i].grfInheritance = NO_INHERITANCE; + explicit_access[i].Trustee.pMultipleTrustee = nullptr; + explicit_access[i].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + explicit_access[i].Trustee.TrusteeForm = TRUSTEE_IS_SID; + explicit_access[i].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + explicit_access[i].Trustee.ptstrName = reinterpret_cast<LPTSTR>(sids[i]); + } + + // Create an access control list (ACL) using this ACE list, if this succeeds + // make sure to ::LocalFree(acl). + ACL* acl = nullptr; + DWORD acl_result = ::SetEntriesInAcl(base::size(explicit_access), + explicit_access, nullptr, &acl); + if (acl_result != ERROR_SUCCESS || acl == nullptr) + return false; + + HRESULT hr = E_FAIL; + + // Set the security descriptor owner and group to Administrators and set the + // discretionary access control list (DACL) to the ACL. + if (::SetSecurityDescriptorOwner(&security_desc, sids[0], FALSE) && + ::SetSecurityDescriptorGroup(&security_desc, sids[0], FALSE) && + ::SetSecurityDescriptorDacl(&security_desc, TRUE, acl, FALSE)) { + // Initialize COM. You may need to modify the parameters of + // CoInitializeSecurity() for your application. Note that an + // explicit security descriptor is being passed down. + hr = ::CoInitializeSecurity( + &security_desc, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IDENTIFY, nullptr, + EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL, nullptr); + } + + ::LocalFree(acl); + return SUCCEEDED(hr); +} + } // namespace updater
diff --git a/chrome/updater/win/util.h b/chrome/updater/win/util.h index d30e10d9..257c677 100644 --- a/chrome/updater/win/util.h +++ b/chrome/updater/win/util.h
@@ -27,6 +27,16 @@ static_cast<ULONG>(error)); } +// Checks whether a process is running with the image |executable|. Returns true +// if a process is found. +bool IsProcessRunning(const wchar_t* executable); + +// Waits until every running instance of |executable| is stopped. +// Returns true if every running processes are stopped. +bool WaitForProcessesStopped(const wchar_t* executable); + +bool InitializeCOMSecurity(); + } // namespace updater #endif // CHROME_UPDATER_WIN_UTIL_H_
diff --git a/chromeos/cryptohome/homedir_methods.cc b/chromeos/cryptohome/homedir_methods.cc index 26c618a..a104315 100644 --- a/chromeos/cryptohome/homedir_methods.cc +++ b/chromeos/cryptohome/homedir_methods.cc
@@ -70,6 +70,16 @@ weak_ptr_factory_.GetWeakPtr(), callback)); } + void MassRemoveKeys(const Identification& id, + const AuthorizationRequest& auth, + const MassRemoveKeysRequest& request, + const Callback& callback) override { + chromeos::CryptohomeClient::Get()->MassRemoveKeys( + CreateAccountIdentifierFromIdentification(id), auth, request, + base::BindOnce(&HomedirMethodsImpl::OnBaseReplyCallback, + weak_ptr_factory_.GetWeakPtr(), callback)); + } + private: void OnBaseReplyCallback(const Callback& callback, base::Optional<BaseReply> reply) {
diff --git a/chromeos/cryptohome/homedir_methods.h b/chromeos/cryptohome/homedir_methods.h index 8959087..7afae59 100644 --- a/chromeos/cryptohome/homedir_methods.h +++ b/chromeos/cryptohome/homedir_methods.h
@@ -62,6 +62,13 @@ const RemoveKeyRequest& request, const Callback& callback) = 0; + // Asks cryptohomed to remove all keys except those whose labels are exempted + // in MassRemoveKeysRequest, for the user identified by |id| using |auth|. + virtual void MassRemoveKeys(const Identification& id, + const AuthorizationRequest& auth, + const MassRemoveKeysRequest& request, + const Callback& callback) = 0; + // Creates the global HomedirMethods instance. static void Initialize();
diff --git a/chromeos/dbus/cryptohome/cryptohome_client.cc b/chromeos/dbus/cryptohome/cryptohome_client.cc index 70d0fe3..e4f9032 100644 --- a/chromeos/dbus/cryptohome/cryptohome_client.cc +++ b/chromeos/dbus/cryptohome/cryptohome_client.cc
@@ -855,6 +855,22 @@ weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } + void AddDataRestoreKey( + const cryptohome::AccountIdentifier& id, + const cryptohome::AuthorizationRequest& auth, + DBusMethodCallback<cryptohome::BaseReply> callback) override { + const char* method_name = cryptohome::kCryptohomeAddDataRestoreKey; + dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name); + dbus::MessageWriter writer(&method_call); + writer.AppendProtoAsArrayOfBytes(id); + writer.AppendProtoAsArrayOfBytes(auth); + + proxy_->CallMethod( + &method_call, kTpmDBusTimeoutMs, + base::BindOnce(&CryptohomeClientImpl::OnBaseReplyMethod, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + } + void UpdateKeyEx( const cryptohome::AccountIdentifier& id, const cryptohome::AuthorizationRequest& auth, @@ -891,6 +907,24 @@ weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } + void MassRemoveKeys( + const cryptohome::AccountIdentifier& id, + const cryptohome::AuthorizationRequest& auth, + const cryptohome::MassRemoveKeysRequest& request, + DBusMethodCallback<cryptohome::BaseReply> callback) override { + const char* method_name = cryptohome::kCryptohomeMassRemoveKeys; + dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name); + dbus::MessageWriter writer(&method_call); + writer.AppendProtoAsArrayOfBytes(id); + writer.AppendProtoAsArrayOfBytes(auth); + writer.AppendProtoAsArrayOfBytes(request); + + proxy_->CallMethod( + &method_call, kTpmDBusTimeoutMs, + base::BindOnce(&CryptohomeClientImpl::OnBaseReplyMethod, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + } + void GetBootAttribute( const cryptohome::GetBootAttributeRequest& request, DBusMethodCallback<cryptohome::BaseReply> callback) override {
diff --git a/chromeos/dbus/cryptohome/cryptohome_client.h b/chromeos/dbus/cryptohome/cryptohome_client.h index 85a300b..5e06f76 100644 --- a/chromeos/dbus/cryptohome/cryptohome_client.h +++ b/chromeos/dbus/cryptohome/cryptohome_client.h
@@ -30,6 +30,7 @@ class GetSupportedKeyPoliciesRequest; class GetTpmStatusRequest; class LockToSingleUserMountUntilRebootRequest; +class MassRemoveKeysRequest; class MigrateKeyRequest; class MigrateToDircryptoRequest; class MountGuestRequest; @@ -533,6 +534,16 @@ const cryptohome::AddKeyRequest& request, DBusMethodCallback<cryptohome::BaseReply> callback) = 0; + // Asynchronously calls AddDataRestoreKey method. |callback| is called after + // method call, and with reply protobuf. + // AddDataRestoreKey generates data_restore_key in OS and adds it to the + // given key set. The reply protobuf needs to be extended to + // AddDataRestoreKeyReply so that caller gets raw bytes of data_restore_key + virtual void AddDataRestoreKey( + const cryptohome::AccountIdentifier& id, + const cryptohome::AuthorizationRequest& auth, + DBusMethodCallback<cryptohome::BaseReply> callback) = 0; + // Asynchronously calls UpdateKeyEx method. |callback| is called after method // call, and with reply protobuf. Reply will contain MountReply extension. // UpdateKeyEx replaces key used for authorization, without affecting any @@ -553,6 +564,16 @@ const cryptohome::RemoveKeyRequest& request, DBusMethodCallback<cryptohome::BaseReply> callback) = 0; + // Asynchronously calls MassRemoveKeys method. |callback| is called after + // method call, and with reply protobuf. + // MassRemoveKeys removes all keys except those whose labels are exempted + // in MassRemoveKeysRequest. + virtual void MassRemoveKeys( + const cryptohome::AccountIdentifier& id, + const cryptohome::AuthorizationRequest& auth, + const cryptohome::MassRemoveKeysRequest& request, + DBusMethodCallback<cryptohome::BaseReply> callback) = 0; + // Asynchronously calls GetBootAttribute method. |callback| is called after // method call, and with reply protobuf. // GetBootAttribute gets the value of the specified boot attribute.
diff --git a/chromeos/dbus/cryptohome/fake_cryptohome_client.cc b/chromeos/dbus/cryptohome/fake_cryptohome_client.cc index 7489035..500a38d 100644 --- a/chromeos/dbus/cryptohome/fake_cryptohome_client.cc +++ b/chromeos/dbus/cryptohome/fake_cryptohome_client.cc
@@ -639,6 +639,13 @@ ReturnProtobufMethodCallback(cryptohome::BaseReply(), std::move(callback)); } +void FakeCryptohomeClient::AddDataRestoreKey( + const cryptohome::AccountIdentifier& cryptohome_id, + const cryptohome::AuthorizationRequest& auth, + DBusMethodCallback<cryptohome::BaseReply> callback) { + ReturnProtobufMethodCallback(cryptohome::BaseReply(), std::move(callback)); +} + void FakeCryptohomeClient::RemoveKeyEx( const cryptohome::AccountIdentifier& cryptohome_id, const cryptohome::AuthorizationRequest& auth, @@ -661,6 +668,14 @@ ReturnProtobufMethodCallback(cryptohome::BaseReply(), std::move(callback)); } +void FakeCryptohomeClient::MassRemoveKeys( + const cryptohome::AccountIdentifier& cryptohome_id, + const cryptohome::AuthorizationRequest& auth, + const cryptohome::MassRemoveKeysRequest& request, + DBusMethodCallback<cryptohome::BaseReply> callback) { + ReturnProtobufMethodCallback(cryptohome::BaseReply(), std::move(callback)); +} + void FakeCryptohomeClient::GetBootAttribute( const cryptohome::GetBootAttributeRequest& request, DBusMethodCallback<cryptohome::BaseReply> callback) {
diff --git a/chromeos/dbus/cryptohome/fake_cryptohome_client.h b/chromeos/dbus/cryptohome/fake_cryptohome_client.h index 74aa9104..25feced 100644 --- a/chromeos/dbus/cryptohome/fake_cryptohome_client.h +++ b/chromeos/dbus/cryptohome/fake_cryptohome_client.h
@@ -194,6 +194,10 @@ const cryptohome::AuthorizationRequest& auth, const cryptohome::AddKeyRequest& request, DBusMethodCallback<cryptohome::BaseReply> callback) override; + void AddDataRestoreKey( + const cryptohome::AccountIdentifier& cryptohome_id, + const cryptohome::AuthorizationRequest& auth, + DBusMethodCallback<cryptohome::BaseReply> callback) override; void UpdateKeyEx(const cryptohome::AccountIdentifier& cryptohome_id, const cryptohome::AuthorizationRequest& auth, const cryptohome::UpdateKeyRequest& request, @@ -202,6 +206,11 @@ const cryptohome::AuthorizationRequest& auth, const cryptohome::RemoveKeyRequest& request, DBusMethodCallback<cryptohome::BaseReply> callback) override; + void MassRemoveKeys( + const cryptohome::AccountIdentifier& cryptohome_id, + const cryptohome::AuthorizationRequest& auth, + const cryptohome::MassRemoveKeysRequest& request, + DBusMethodCallback<cryptohome::BaseReply> callback) override; void GetBootAttribute( const cryptohome::GetBootAttributeRequest& request, DBusMethodCallback<cryptohome::BaseReply> callback) override;
diff --git a/chromeos/network/BUILD.gn b/chromeos/network/BUILD.gn index f14d80d2..48637684 100644 --- a/chromeos/network/BUILD.gn +++ b/chromeos/network/BUILD.gn
@@ -21,6 +21,7 @@ "//chromeos/services/network_config/public/mojom", "//components/account_id", "//components/certificate_matching", + "//components/crx_file", "//components/device_event_log", "//components/onc", "//components/pref_registry", @@ -113,6 +114,8 @@ "network_ui_data.h", "network_util.cc", "network_util.h", + "onc/certificate_scope.cc", + "onc/certificate_scope.h", "onc/onc_certificate_importer.h", "onc/onc_certificate_importer_impl.cc", "onc/onc_certificate_importer_impl.h",
diff --git a/chromeos/network/DEPS b/chromeos/network/DEPS index d3e6536..50ad652 100644 --- a/chromeos/network/DEPS +++ b/chromeos/network/DEPS
@@ -10,6 +10,7 @@ "+chromeos/services/network_config/public", "+components/account_id", "+components/certificate_matching", + "+components/crx_file", "+components/device_event_log", "+components/onc", "+components/pref_registry",
diff --git a/chromeos/network/client_cert_resolver.cc b/chromeos/network/client_cert_resolver.cc index 9a39df2c..430c82a 100644 --- a/chromeos/network/client_cert_resolver.cc +++ b/chromeos/network/client_cert_resolver.cc
@@ -97,7 +97,7 @@ std::string firstSANEmail; if (!names.empty()) firstSANEmail = names[0]; - substitutions[onc::substitutes::kCertSANEmail] = firstSANEmail; + substitutions[::onc::substitutes::kCertSANEmail] = firstSANEmail; } { @@ -107,10 +107,10 @@ std::string firstSANUPN; if (!names.empty()) firstSANUPN = names[0]; - substitutions[onc::substitutes::kCertSANUPN] = firstSANUPN; + substitutions[::onc::substitutes::kCertSANUPN] = firstSANUPN; } - substitutions[onc::substitutes::kCertSubjectCommonName] = + substitutions[::onc::substitutes::kCertSubjectCommonName] = certificate::GetCertAsciiSubjectCommonName(cert); return substitutions; @@ -616,7 +616,7 @@ if (network->profile_path().empty()) continue; - onc::ONCSource onc_source = onc::ONC_SOURCE_NONE; + ::onc::ONCSource onc_source = ::onc::ONC_SOURCE_NONE; const base::DictionaryValue* policy = managed_network_config_handler_->FindPolicyByGuidAndProfile( network->guid(), network->profile_path(), &onc_source);
diff --git a/chromeos/network/managed_network_configuration_handler_impl.h b/chromeos/network/managed_network_configuration_handler_impl.h index e9aa9c6..49a252f 100644 --- a/chromeos/network/managed_network_configuration_handler_impl.h +++ b/chromeos/network/managed_network_configuration_handler_impl.h
@@ -82,7 +82,7 @@ const base::Closure& callback, const network_handler::ErrorCallback& error_callback) const override; - void SetPolicy(onc::ONCSource onc_source, + void SetPolicy(::onc::ONCSource onc_source, const std::string& userhash, const base::ListValue& network_configs_onc, const base::DictionaryValue& global_network_config) override; @@ -92,7 +92,7 @@ const base::DictionaryValue* FindPolicyByGUID( const std::string userhash, const std::string& guid, - onc::ONCSource* onc_source) const override; + ::onc::ONCSource* onc_source) const override; const GuidToPolicyMap* GetNetworkConfigsFromPolicy( const std::string& userhash) const override; @@ -103,7 +103,7 @@ const base::DictionaryValue* FindPolicyByGuidAndProfile( const std::string& guid, const std::string& profile_path, - onc::ONCSource* onc_source) const override; + ::onc::ONCSource* onc_source) const override; bool AllowOnlyPolicyNetworksToConnect() const override; bool AllowOnlyPolicyNetworksToConnectIfAvailable() const override;
diff --git a/chromeos/network/network_cert_loader.cc b/chromeos/network/network_cert_loader.cc index 69318c9..1ae5fd63 100644 --- a/chromeos/network/network_cert_loader.cc +++ b/chromeos/network/network_cert_loader.cc
@@ -18,6 +18,8 @@ #include "base/strings/string_number_conversions.h" #include "base/task/post_task.h" #include "chromeos/network/certificate_helper.h" +#include "chromeos/network/onc/certificate_scope.h" +#include "chromeos/network/policy_certificate_provider.h" #include "crypto/nss_util.h" #include "crypto/scoped_nss_types.h" #include "net/cert/cert_database.h" @@ -45,8 +47,8 @@ return NetworkCertType::kOther; } -// Returns all authority certificats provided by |policy_certificate_provider| -// as a list of NetworkCerts. +// Returns all authority certificates with default (not restricted) scope +// provided by |policy_certificate_provider| as a list of NetworkCerts. NetworkCertLoader::NetworkCertList GetPolicyProvidedAuthorities( const PolicyCertificateProvider* policy_certificate_provider, bool device_wide) { @@ -54,7 +56,8 @@ if (!policy_certificate_provider) return result; for (const auto& certificate : - policy_certificate_provider->GetAllAuthorityCertificates()) { + policy_certificate_provider->GetAllAuthorityCertificates( + chromeos::onc::CertificateScope::Default())) { net::ScopedCERTCertificate x509_cert = net::x509_util::CreateCERTCertificateFromX509Certificate( certificate.get());
diff --git a/chromeos/network/network_cert_loader_unittest.cc b/chromeos/network/network_cert_loader_unittest.cc index ab54d71..56cb70a 100644 --- a/chromeos/network/network_cert_loader_unittest.cc +++ b/chromeos/network/network_cert_loader_unittest.cc
@@ -13,6 +13,8 @@ #include "base/files/file_util.h" #include "base/macros.h" #include "base/test/scoped_task_environment.h" +#include "chromeos/network/onc/certificate_scope.h" +#include "chromeos/network/policy_certificate_provider.h" #include "crypto/scoped_nss_types.h" #include "crypto/scoped_test_nss_db.h" #include "net/cert/nss_cert_database_chromeos.h" @@ -35,28 +37,42 @@ observer_list_.RemoveObserver(observer); } - net::CertificateList GetAllServerAndAuthorityCertificates() const override { + net::CertificateList GetAllServerAndAuthorityCertificates( + const chromeos::onc::CertificateScope& scope) const override { // NetworkCertLoader does not call this. NOTREACHED(); return net::CertificateList(); } - net::CertificateList GetAllAuthorityCertificates() const override { + net::CertificateList GetAllAuthorityCertificates( + const chromeos::onc::CertificateScope& scope) const override { + // NetworkCertLoader only retrieves profile-wide certificates. + EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope); + return authority_certificates_; } - net::CertificateList GetWebTrustedCertificates() const override { + net::CertificateList GetWebTrustedCertificates( + const chromeos::onc::CertificateScope& scope) const override { // NetworkCertLoader does not call this. NOTREACHED(); return net::CertificateList(); } - net::CertificateList GetCertificatesWithoutWebTrust() const override { + net::CertificateList GetCertificatesWithoutWebTrust( + const chromeos::onc::CertificateScope& scope) const override { // NetworkCertLoader does not call this. NOTREACHED(); return net::CertificateList(); } + const std::set<std::string>& GetExtensionIdsWithPolicyCertificates() + const override { + // NetworkCertLoader does not call this. + NOTREACHED(); + return kNoExtensions; + } + void SetAuthorityCertificates( const net::CertificateList& authority_certificates) { authority_certificates_ = authority_certificates; @@ -71,6 +87,7 @@ base::ObserverList<PolicyCertificateProvider::Observer, true /* check_empty */>::Unchecked observer_list_; net::CertificateList authority_certificates_; + const std::set<std::string> kNoExtensions = {}; }; bool IsCertInCertificateList(
diff --git a/chromeos/network/network_connection_handler_impl.cc b/chromeos/network/network_connection_handler_impl.cc index 014c51d..167d178 100644 --- a/chromeos/network/network_connection_handler_impl.cc +++ b/chromeos/network/network_connection_handler_impl.cc
@@ -465,7 +465,7 @@ std::string profile; service_properties.GetStringWithoutPathExpansion(shill::kProfileProperty, &profile); - ::onc::ONCSource onc_source = onc::ONC_SOURCE_NONE; + ::onc::ONCSource onc_source = ::onc::ONC_SOURCE_NONE; const base::DictionaryValue* policy = managed_configuration_handler_->FindPolicyByGuidAndProfile(guid, profile, &onc_source); @@ -509,7 +509,7 @@ // kL2tpIpsecClientCertIdProperty here (and also in VPNConfigView). if (!vpn_client_cert_id.empty() || cert_config_from_policy.client_cert_type != - onc::client_cert::kClientCertTypeNone) { + ::onc::client_cert::kClientCertTypeNone) { client_cert_type = client_cert::CONFIG_TYPE_IPSEC; } } @@ -541,7 +541,7 @@ // Check certificate properties from policy. if (cert_config_from_policy.client_cert_type == - onc::client_cert::kPattern) { + ::onc::client_cert::kPattern) { if (!ClientCertResolver::ResolveClientCertificateSync( client_cert_type, cert_config_from_policy, &config_properties)) { NET_LOG(ERROR) << "Non matching certificate for: " << service_path;
diff --git a/chromeos/network/onc/certificate_scope.cc b/chromeos/network/onc/certificate_scope.cc new file mode 100644 index 0000000..5da02e2 --- /dev/null +++ b/chromeos/network/onc/certificate_scope.cc
@@ -0,0 +1,66 @@ +// 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 "chromeos/network/onc/certificate_scope.h" + +#include <base/values.h> +#include <tuple> + +#include "components/onc/onc_constants.h" + +namespace chromeos { +namespace onc { + +CertificateScope::CertificateScope(const CertificateScope& other) = default; +CertificateScope::CertificateScope(CertificateScope&& other) = default; +CertificateScope::~CertificateScope() = default; + +CertificateScope::CertificateScope(const std::string& extension_id) + : extension_id_(extension_id) {} + +// static +CertificateScope CertificateScope::ForExtension( + const std::string& extension_id) { + return CertificateScope(/*extension_id=*/extension_id); +} + +// static +CertificateScope CertificateScope::Default() { + return CertificateScope(/*extension_id=*/std::string()); +} + +// static +base::Optional<CertificateScope> CertificateScope::ParseFromOncValue( + const base::Value& scope_dict) { + const std::string* scope_type_str = + scope_dict.FindStringKey(::onc::scope::kType); + const std::string* scope_id_str = scope_dict.FindStringKey(::onc::scope::kId); + + if (!scope_type_str || !scope_id_str) + return base::nullopt; + + if (*scope_type_str == ::onc::scope::kDefault) + return Default(); + if (*scope_type_str == ::onc::scope::kExtension) + return ForExtension(*scope_id_str); + + return base::nullopt; +} + +CertificateScope& CertificateScope::operator=(const CertificateScope& other) = + default; + +bool CertificateScope::operator<(const CertificateScope& other) const { + return extension_id_ < other.extension_id_; +} +bool CertificateScope::operator==(const CertificateScope& other) const { + return extension_id_ == other.extension_id_; +} + +bool CertificateScope::operator!=(const CertificateScope& other) const { + return !(*this == other); +} + +} // namespace onc +} // namespace chromeos
diff --git a/chromeos/network/onc/certificate_scope.h b/chromeos/network/onc/certificate_scope.h new file mode 100644 index 0000000..8654954 --- /dev/null +++ b/chromeos/network/onc/certificate_scope.h
@@ -0,0 +1,67 @@ +// 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 CHROMEOS_NETWORK_ONC_CERTIFICATE_SCOPE_H_ +#define CHROMEOS_NETWORK_ONC_CERTIFICATE_SCOPE_H_ + +#include <string> + +#include "base/component_export.h" +#include "base/macros.h" +#include "base/optional.h" + +namespace base { +class Value; +} + +namespace chromeos { +namespace onc { + +// Describes the scope a policy-provided certificate should be applied in. +class COMPONENT_EXPORT(CHROMEOS_NETWORK) CertificateScope { + public: + CertificateScope(const CertificateScope& other); + CertificateScope(CertificateScope&& other); + ~CertificateScope(); + + // Creates a CertificateScope for a chrome extension with the id + // |extension_id|. + static CertificateScope ForExtension(const std::string& extension_id); + + // Creates a CertificateScope for certificates that should apply in the + // default scope. + // For Chrome OS user ONC policy, this means that they apply in the whole user + // Profile. + // For Chrome OS device ONC policy, this means that they apply in the context + // of the sign-in webview and all sign-in screen extensions (however, only + // certificates without trust are respected as default-scoped device ONC + // policy specified certificates). + static CertificateScope Default(); + + // Parses a CertificateScope from |scope_dict|, which should be a dictionary + // containing the ONC "Scope" object. + static base::Optional<CertificateScope> ParseFromOncValue( + const base::Value& scope_dict); + + CertificateScope& operator=(const CertificateScope& other); + bool operator<(const CertificateScope& other) const; + bool operator==(const CertificateScope& other) const; + bool operator!=(const CertificateScope& other) const; + + bool is_extension_scoped() const { return !extension_id_.empty(); } + const std::string& extension_id() const { return extension_id_; } + + private: + // If |extension_id| is empty, it means that the scope should not be + // restricted. + CertificateScope(const std::string& extension_id); + + // If empty, it means that the scope should not be restricted to an extension. + std::string extension_id_; +}; + +} // namespace onc +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_CERTIFICATE_SCOPE_H_
diff --git a/chromeos/network/onc/onc_parsed_certificates.cc b/chromeos/network/onc/onc_parsed_certificates.cc index 67605dc..3457489c 100644 --- a/chromeos/network/onc/onc_parsed_certificates.cc +++ b/chromeos/network/onc/onc_parsed_certificates.cc
@@ -22,6 +22,19 @@ enum class CertificateType { kServer, kAuthority, kClient }; +// Parses the "Scope" of a policy-provided certificate. +// If a Scope element is not present, returns CertificateScope::Default(). +// If a Scope element is present but malformed, returns an empty base::Optional. +base::Optional<CertificateScope> ParseCertScope( + const base::Value& onc_certificate) { + const base::Value* scope_dict = onc_certificate.FindKeyOfType( + ::onc::certificate::kScope, base::Value::Type::DICTIONARY); + if (!scope_dict) + return CertificateScope::Default(); + + return CertificateScope::ParseFromOncValue(*scope_dict); +} + // Returns true if the certificate described by |onc_certificate| requests web // trust. bool HasWebTrustFlag(const base::Value& onc_certificate) { @@ -74,11 +87,13 @@ OncParsedCertificates::ServerOrAuthorityCertificate:: ServerOrAuthorityCertificate( + CertificateScope scope, Type type, const std::string& guid, const scoped_refptr<net::X509Certificate>& certificate, bool web_trust_requested) - : type_(type), + : scope_(scope), + type_(type), guid_(guid), certificate_(certificate), web_trust_requested_(web_trust_requested) {} @@ -100,6 +115,9 @@ bool OncParsedCertificates::ServerOrAuthorityCertificate::operator==( const ServerOrAuthorityCertificate& other) const { + if (scope() != other.scope()) + return false; + if (type() != other.type()) return false; @@ -217,6 +235,12 @@ ServerOrAuthorityCertificate::Type type, const std::string& guid, const base::Value& onc_certificate) { + base::Optional<CertificateScope> scope = ParseCertScope(onc_certificate); + if (!scope) { + LOG(ERROR) << "Certificate has malformed 'Scope'"; + return false; + } + bool web_trust_requested = HasWebTrustFlag(onc_certificate); const base::Value* x509_data_key = onc_certificate.FindKeyOfType( ::onc::certificate::kX509, base::Value::Type::STRING); @@ -241,7 +265,7 @@ } server_or_authority_certificates_.push_back(ServerOrAuthorityCertificate( - type, guid, certificate, web_trust_requested)); + scope.value(), type, guid, certificate, web_trust_requested)); return true; }
diff --git a/chromeos/network/onc/onc_parsed_certificates.h b/chromeos/network/onc/onc_parsed_certificates.h index 7f4337e2..606aaca77 100644 --- a/chromeos/network/onc/onc_parsed_certificates.h +++ b/chromeos/network/onc/onc_parsed_certificates.h
@@ -11,6 +11,7 @@ #include "base/component_export.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "chromeos/network/onc/certificate_scope.h" namespace base { class Value; @@ -33,6 +34,7 @@ enum class Type { kServer, kAuthority }; ServerOrAuthorityCertificate( + CertificateScope scope, Type type, const std::string& guid, const scoped_refptr<net::X509Certificate>& certificate, @@ -47,6 +49,8 @@ bool operator==(const ServerOrAuthorityCertificate& other) const; bool operator!=(const ServerOrAuthorityCertificate& other) const; + CertificateScope scope() const { return scope_; } + Type type() const { return type_; } const std::string& guid() const { return guid_; } @@ -58,6 +62,7 @@ bool web_trust_requested() const { return web_trust_requested_; } private: + CertificateScope scope_; Type type_; std::string guid_; scoped_refptr<net::X509Certificate> certificate_;
diff --git a/chromeos/network/onc/onc_parsed_certificates_unittest.cc b/chromeos/network/onc/onc_parsed_certificates_unittest.cc index 9d5f426..2473fed 100644 --- a/chromeos/network/onc/onc_parsed_certificates_unittest.cc +++ b/chromeos/network/onc/onc_parsed_certificates_unittest.cc
@@ -175,6 +175,7 @@ EXPECT_EQ( OncParsedCertificates::ServerOrAuthorityCertificate::Type::kAuthority, trusted_authority_cert.type()); + EXPECT_EQ(CertificateScope::Default(), trusted_authority_cert.scope()); EXPECT_EQ("{trusted-cert}", trusted_authority_cert.guid()); EXPECT_TRUE(trusted_authority_cert.web_trust_requested()); EXPECT_EQ("Test Root CA", @@ -186,12 +187,65 @@ EXPECT_EQ( OncParsedCertificates::ServerOrAuthorityCertificate::Type::kAuthority, trusted_authority_cert.type()); + EXPECT_EQ(CertificateScope::Default(), trusted_authority_cert.scope()); EXPECT_EQ("{untrusted-cert}", untrusted_authority_cert.guid()); EXPECT_FALSE(untrusted_authority_cert.web_trust_requested()); EXPECT_EQ("127.0.0.1", untrusted_authority_cert.certificate()->subject().common_name); } +TEST_F(OncParsedCertificatesTest, AuthorityCertsScope) { + const char onc_certificates_json[] = R"( + [ + { "GUID": "{extension-scoped-cert}", + "Type": "Authority", + "TrustBits": [ + "Web" + ], + "Scope": { + "Type": "Extension", + "Id": "fake-extension-id" + }, + "X509": "-----BEGIN CERTIFICATE-----\n + MIIC8zCCAdugAwIBAgIJALF9qhLor0+aMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV\n + BAMMDFRlc3QgUm9vdCBDQTAeFw0xNDA4MTQwMzA1MjlaFw0yNDA4MTEwMzA1Mjla\n + MBcxFTATBgNVBAMMDFRlc3QgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP\n + ADCCAQoCggEBALZJQeNCAVGofzx6cdP7zZE1F4QajvY2x9FwHfqG8267dm/oMi43\n + /TiSPWjkin1CMxRGG9wE9pFuVEDECgn97C1i4l7huiycwbFgTNrH+CJcgiBlQh5W\n + d3VP65AsSupXDiKNbJWsEerM1+72cA0J3aY1YV3Jdm2w8h6/MIbYd1I2lZcO0UbF\n + 7YE9G7DyYZU8wUA4719dumGf7yucn4WJdHBj1XboNX7OAeHzERGQHA31/Y3OEGyt\n + fFUaIW/XLfR4FeovOL2RnjwdB0b1Q8GCi68SU2UZimlpZgay2gv6KgChKhWESfEB\n + v5swBtAVoB+dUZFH4VNf717swmF5whSfxOMCAwEAAaNCMEAwDwYDVR0TAQH/BAUw\n + AwEB/zAdBgNVHQ4EFgQUvPcw0TzA8nn675/JbFyT84poq4MwDgYDVR0PAQH/BAQD\n + AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBXByn7f+j/sObYWGrDkKE4HLTzaLHs6Ikj\n + JNeo8iHDYOSkSVwAv9/HgniAKxj3rd3QYl6nsMzwqrTOcBJZZWd2BQAYmv/EKhfj\n + 8VXYvlxe68rLU4cQ1QkyNqdeQfRT2n5WYNJ+TpqlCF9ddennMMsi6e8ZSYOlI6H4\n + YEzlNtU5eBjxXr/OqgtTgSx4qQpr2xMQIRR/G3A9iRpAigYsXVAZYvnHRYnyPWYF\n + PX11W1UegEJyoZp8bQp09u6mIWw6mPt3gl/ya1bm3ZuOUPDGrv3qpgUHqSYGVrOy\n + 2bI3oCE+eQYfuVG+9LFJTZC1M+UOx15bQMVqBNFDepRqpE9h/ILg\n + -----END CERTIFICATE-----" } + ])"; + + std::unique_ptr<OncParsedCertificates> onc_parsed_certificates; + ASSERT_TRUE(ReadFromJSON(onc_certificates_json, &onc_parsed_certificates)); + + EXPECT_FALSE(onc_parsed_certificates->has_error()); + ASSERT_EQ(1u, + onc_parsed_certificates->server_or_authority_certificates().size()); + + const OncParsedCertificates::ServerOrAuthorityCertificate& authority_cert = + onc_parsed_certificates->server_or_authority_certificates()[0]; + EXPECT_EQ( + OncParsedCertificates::ServerOrAuthorityCertificate::Type::kAuthority, + authority_cert.type()); + EXPECT_EQ(CertificateScope::ForExtension("fake-extension-id"), + authority_cert.scope()); + EXPECT_EQ("{extension-scoped-cert}", authority_cert.guid()); + EXPECT_TRUE(authority_cert.web_trust_requested()); + EXPECT_EQ("Test Root CA", + authority_cert.certificate()->subject().common_name); +} + TEST_F(OncParsedCertificatesTest, UnknownTrustBitsIgnored) { const char onc_certificates_json[] =R"( [
diff --git a/chromeos/network/onc/onc_signature.cc b/chromeos/network/onc/onc_signature.cc index 58576d8e..475d9c8 100644 --- a/chromeos/network/onc/onc_signature.cc +++ b/chromeos/network/onc/onc_signature.cc
@@ -362,6 +362,7 @@ const OncFieldSignature certificate_fields[] = { {::onc::certificate::kGUID, &kStringSignature}, + {::onc::certificate::kScope, &kScopeSignature}, {::onc::certificate::kPKCS12, &kStringSignature}, {::onc::kRemove, &kBoolSignature}, {::onc::certificate::kTrustBits, &kStringListSignature}, @@ -369,6 +370,11 @@ {::onc::certificate::kX509, &kStringSignature}, {NULL}}; +const OncFieldSignature scope_fields[] = { + {::onc::scope::kType, &kStringSignature}, + {::onc::scope::kId, &kStringSignature}, + {nullptr}}; + const OncFieldSignature toplevel_configuration_fields[] = { {::onc::toplevel_config::kCertificates, &kCertificateListSignature}, {::onc::toplevel_config::kNetworkConfigurations, @@ -430,6 +436,8 @@ wifi_fields, NULL}; const OncValueSignature kCertificateSignature = {base::Value::Type::DICTIONARY, certificate_fields, NULL}; +const OncValueSignature kScopeSignature = {base::Value::Type::DICTIONARY, + scope_fields, nullptr}; const OncValueSignature kNetworkConfigurationSignature = { base::Value::Type::DICTIONARY, network_configuration_fields, NULL}; const OncValueSignature kGlobalNetworkConfigurationSignature = {
diff --git a/chromeos/network/onc/onc_signature.h b/chromeos/network/onc/onc_signature.h index c900920..5693c8d 100644 --- a/chromeos/network/onc/onc_signature.h +++ b/chromeos/network/onc/onc_signature.h
@@ -80,6 +80,8 @@ COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const OncValueSignature kCertificateSignature; COMPONENT_EXPORT(CHROMEOS_NETWORK) +extern const OncValueSignature kScopeSignature; +COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const OncValueSignature kNetworkConfigurationSignature; COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const OncValueSignature kGlobalNetworkConfigurationSignature;
diff --git a/chromeos/network/onc/onc_validator.cc b/chromeos/network/onc/onc_validator.cc index 3dd6f07..b8da2d6 100644 --- a/chromeos/network/onc/onc_validator.cc +++ b/chromeos/network/onc/onc_validator.cc
@@ -19,6 +19,7 @@ #include "base/strings/string_util.h" #include "base/values.h" #include "chromeos/network/onc/onc_signature.h" +#include "components/crx_file/id_util.h" namespace chromeos { namespace onc { @@ -162,6 +163,8 @@ valid = ValidateEAP(repaired.get()); } else if (&signature == &kCertificateSignature) { valid = ValidateCertificate(repaired.get()); + } else if (&signature == &kScopeSignature) { + valid = ValidateScope(repaired.get()); } else if (&signature == &kTetherWithStateSignature) { valid = ValidateTether(repaired.get()); } @@ -1117,6 +1120,33 @@ return !error_on_missing_field_ || all_required_exist; } +bool Validator::ValidateScope(base::DictionaryValue* result) { + using namespace ::onc::scope; + + const char* const kValidTypes[] = {kDefault, kExtension}; + const std::vector<const char*> valid_types(toVector(kValidTypes)); + if (FieldExistsAndHasNoValidValue(*result, kType, valid_types) || + FieldExistsAndIsEmpty(*result, kId)) { + return false; + } + + bool all_required_exist = RequireField(*result, kType); + const std::string* type_string = result->FindStringKey(kType); + if (type_string && *type_string == kExtension) { + all_required_exist &= RequireField(*result, kId); + // Check Id validity for type 'Extension'. + const std::string* id_string = result->FindStringKey(kId); + if (id_string && !crx_file::id_util::IdIsValid(*id_string)) { + std::ostringstream msg; + msg << "Field '" << kId << "' is not a valid extension id."; + AddValidationIssue(false /* is_error */, msg.str()); + return false; + } + } + + return !error_on_missing_field_ || all_required_exist; +} + bool Validator::ValidateTether(base::DictionaryValue* result) { using namespace ::onc::tether;
diff --git a/chromeos/network/onc/onc_validator.h b/chromeos/network/onc/onc_validator.h index 1588226a..beadc85 100644 --- a/chromeos/network/onc/onc_validator.h +++ b/chromeos/network/onc/onc_validator.h
@@ -191,6 +191,7 @@ bool ValidateProxyLocation(base::DictionaryValue* result); bool ValidateEAP(base::DictionaryValue* result); bool ValidateCertificate(base::DictionaryValue* result); + bool ValidateScope(base::DictionaryValue* result); bool ValidateTether(base::DictionaryValue* result); bool IsValidValue(const std::string& field_value,
diff --git a/chromeos/network/onc/onc_validator_unittest.cc b/chromeos/network/onc/onc_validator_unittest.cc index 7ac37b1..40ad0e4 100644 --- a/chromeos/network/onc/onc_validator_unittest.cc +++ b/chromeos/network/onc/onc_validator_unittest.cc
@@ -234,7 +234,11 @@ &kNetworkConfigurationSignature, false), OncParams("arc_vpn.onc", &kNetworkConfigurationSignature, false), - OncParams("tether.onc", &kNetworkWithStateSignature, false))); + OncParams("tether.onc", &kNetworkWithStateSignature, false), + OncParams("cert_with_valid_scope.onc", &kCertificateSignature, false), + OncParams("cert_with_explicit_default_scope.onc", + &kCertificateSignature, + false))); namespace { @@ -575,7 +579,28 @@ std::make_pair(OncParams("tether-signal-strength-over-100", &kNetworkWithStateSignature, true), - ExpectBothNotValid("", "")))); + ExpectBothNotValid("", "")), + std::make_pair( + OncParams("invalid-scope-due-to-type", &kScopeSignature, true), + ExpectBothNotValid("", "")), + std::make_pair(OncParams("invalid-scope-due-to-missing-id", + &kScopeSignature, + true), + ExpectBothNotValid("", + "invalid-scope-due-to-missing-id")), + std::make_pair(OncParams("invalid-scope-due-to-invalid-id-length", + &kScopeSignature, + true), + ExpectBothNotValid("", "")), + std::make_pair(OncParams("invalid-scope-due-to-invalid-id-character", + &kScopeSignature, + true), + ExpectBothNotValid("", "")), + std::make_pair( + OncParams("invalid-scope-due-to-missing-type", + &kScopeSignature, + true), + ExpectBothNotValid("", "invalid-scope-due-to-missing-type")))); } // namespace onc } // namespace chromeos
diff --git a/chromeos/network/policy_certificate_provider.h b/chromeos/network/policy_certificate_provider.h index e03998b..fd49d7e 100644 --- a/chromeos/network/policy_certificate_provider.h +++ b/chromeos/network/policy_certificate_provider.h
@@ -5,10 +5,13 @@ #ifndef CHROMEOS_NETWORK_POLICY_CERTIFICATE_PROVIDER_H_ #define CHROMEOS_NETWORK_POLICY_CERTIFICATE_PROVIDER_H_ +#include <set> +#include <string> #include <vector> #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "chromeos/network/onc/certificate_scope.h" namespace net { class X509Certificate; @@ -38,25 +41,34 @@ // Returns all server and authority certificates successfully parsed from ONC, // independent of their trust bits. - virtual net::CertificateList GetAllServerAndAuthorityCertificates() const = 0; + virtual net::CertificateList GetAllServerAndAuthorityCertificates( + const chromeos::onc::CertificateScope& scope) const = 0; // Returns all authority certificates successfully parsed from ONC, // independent of their trust bits. - virtual net::CertificateList GetAllAuthorityCertificates() const = 0; + virtual net::CertificateList GetAllAuthorityCertificates( + const chromeos::onc::CertificateScope& scope) const = 0; // Returns the server and authority certificates which were successfully // parsed from ONC and were granted web trust. This means that the // certificates had the "Web" trust bit set, and this // UserNetworkConfigurationUpdater instance was created with // |allow_trusted_certs_from_policy| = true. - virtual net::CertificateList GetWebTrustedCertificates() const = 0; + virtual net::CertificateList GetWebTrustedCertificates( + const chromeos::onc::CertificateScope& scope) const = 0; // Returns the server and authority certificates which were successfully // parsed from ONC and did not request or were not granted web trust. // This is equivalent to calling |GetAllServerAndAuthorityCertificates| and // then removing all certificates returned by |GetWebTrustedCertificates| from // the result. - virtual net::CertificateList GetCertificatesWithoutWebTrust() const = 0; + virtual net::CertificateList GetCertificatesWithoutWebTrust( + const chromeos::onc::CertificateScope& scope) const = 0; + + // Lists extension IDs for which policy-provided certificates have been + // specified. + virtual const std::set<std::string>& GetExtensionIdsWithPolicyCertificates() + const = 0; }; } // namespace chromeos
diff --git a/chromeos/services/secure_channel/ble_scanner_impl_unittest.cc b/chromeos/services/secure_channel/ble_scanner_impl_unittest.cc index 2a31d56..2b6550e 100644 --- a/chromeos/services/secure_channel/ble_scanner_impl_unittest.cc +++ b/chromeos/services/secure_channel/ble_scanner_impl_unittest.cc
@@ -160,12 +160,12 @@ service_data, expected_remote_device, is_background_advertisement); size_t num_results_before_call = results.size(); - FakeBluetoothDevice* fake_bluetooth_device = + std::unique_ptr<FakeBluetoothDevice> fake_bluetooth_device = SimulateScanResult(service_data); EXPECT_EQ(num_results_before_call + 1u, results.size()); EXPECT_EQ(expected_remote_device, std::get<0>(results.back())); - EXPECT_EQ(fake_bluetooth_device, std::get<1>(results.back())); + EXPECT_EQ(fake_bluetooth_device.get(), std::get<1>(results.back())); EXPECT_EQ(is_background_advertisement ? ConnectionRole::kListenerRole : ConnectionRole::kInitiatorRole, std::get<2>(results.back())); @@ -209,7 +209,8 @@ } private: - FakeBluetoothDevice* SimulateScanResult(const std::string& service_data) { + std::unique_ptr<FakeBluetoothDevice> SimulateScanResult( + const std::string& service_data) { static const int16_t kFakeRssi = -70; static const std::vector<uint8_t> kFakeEir; @@ -228,7 +229,7 @@ kFakeRssi, kFakeEir); } - return fake_bluetooth_device.get(); + return fake_bluetooth_device; } const multidevice::RemoteDeviceRefList test_devices_;
diff --git a/chromeos/test/data/network/cert_with_explicit_default_scope.onc b/chromeos/test/data/network/cert_with_explicit_default_scope.onc new file mode 100644 index 0000000..b18244e5 --- /dev/null +++ b/chromeos/test/data/network/cert_with_explicit_default_scope.onc
@@ -0,0 +1,9 @@ +{ + "GUID": "{f998f760-272b-6939-4c2beffe428697ab}", + "Scope": { + "Type": "Default" + }, + "Type": "Authority", + "X509": "MIIDojCCAwugAwIBAgIJAKGvi5ZgEWDVMA0GCSqGSIb3DQEBBAUAMIGTMRUwEwYDVQQKEwxHb29nbGUsIEluYy4xETAPBgNVBAsTCENocm9tZU9TMSIwIAYJKoZIhvcNAQkBFhNnc3BlbmNlckBnb29nbGUuY29tMRowGAYDVQQHExFNb3VudGFpbiBWaWV3LCBDQTELMAkGA1UECBMCQ0ExCzAJBgNVBAYTAlVTMQ0wCwYDVQQDEwRsbWFvMB4XDTExMDMxNjIzNDcxMFoXDTEyMDMxNTIzNDcxMFowgZMxFTATBgNVBAoTDEdvb2dsZSwgSW5jLjERMA8GA1UECxMIQ2hyb21lT1MxIjAgBgkqhkiG9w0BCQEWE2dzcGVuY2VyQGdvb2dsZS5jb20xGjAYBgNVBAcTEU1vdW50YWluIFZpZXcsIENBMQswCQYDVQQIEwJDQTELMAkGA1UEBhMCVVMxDTALBgNVBAMTBGxtYW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMDX6BQz2JUzIAVjetiXxDznd2wdqVqVHfNkbSRW+xBywgqUaIXmFEGUol7VzPfmeFV8o8ok/eFlQB0h6ycqgwwMd0KjtJs2ys/k0F5GuN0G7fsgr+NRnhVgxj21yF6gYTN/8a9kscla/svdmp8ekexbALFnghbLBx3CgcqUxT+tAgMBAAGjgfswgfgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUbYygbSkl4kpjCNuxoezFGupA97UwgcgGA1UdIwSBwDCBvYAUbYygbSkl4kpjCNuxoezFGupA97WhgZmkgZYwgZMxFTATBgNVBAoTDEdvb2dsZSwgSW5jLjERMA8GA1UECxMIQ2hyb21lT1MxIjAgBgkqhkiG9w0BCQEWE2dzcGVuY2VyQGdvb2dsZS5jb20xGjAYBgNVBAcTEU1vdW50YWluIFZpZXcsIENBMQswCQYDVQQIEwJDQTELMAkGA1UEBhMCVVMxDTALBgNVBAMTBGxtYW+CCQChr4uWYBFg1TANBgkqhkiG9w0BAQQFAAOBgQCDq9wiQ4uVuf1CQU3sXfXCy1yqi5m8AsO9FxHvah5/SVFNwKllqTfedpCaWEswJ55YAojW9e+pY2Fh3Fo/Y9YkF88KCtLuBjjqDKCRLxF4LycjHODKyQQ7mN/t5AtP9yKOsNvWF+M4IfReg51kohau6FauQx87by5NIRPdkNPvkQ==" + } +
diff --git a/chromeos/test/data/network/cert_with_valid_scope.onc b/chromeos/test/data/network/cert_with_valid_scope.onc new file mode 100644 index 0000000..fcb30cb --- /dev/null +++ b/chromeos/test/data/network/cert_with_valid_scope.onc
@@ -0,0 +1,9 @@ +{ + "GUID": "{f998f760-272b-6939-4c2beffe428697ab}", + "Scope": { + "Type": "Extension", + "Id": "ngjobkbdodapjbbncmagbccommkggmnj" + }, + "Type": "Authority", + "X509": "MIIDojCCAwugAwIBAgIJAKGvi5ZgEWDVMA0GCSqGSIb3DQEBBAUAMIGTMRUwEwYDVQQKEwxHb29nbGUsIEluYy4xETAPBgNVBAsTCENocm9tZU9TMSIwIAYJKoZIhvcNAQkBFhNnc3BlbmNlckBnb29nbGUuY29tMRowGAYDVQQHExFNb3VudGFpbiBWaWV3LCBDQTELMAkGA1UECBMCQ0ExCzAJBgNVBAYTAlVTMQ0wCwYDVQQDEwRsbWFvMB4XDTExMDMxNjIzNDcxMFoXDTEyMDMxNTIzNDcxMFowgZMxFTATBgNVBAoTDEdvb2dsZSwgSW5jLjERMA8GA1UECxMIQ2hyb21lT1MxIjAgBgkqhkiG9w0BCQEWE2dzcGVuY2VyQGdvb2dsZS5jb20xGjAYBgNVBAcTEU1vdW50YWluIFZpZXcsIENBMQswCQYDVQQIEwJDQTELMAkGA1UEBhMCVVMxDTALBgNVBAMTBGxtYW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMDX6BQz2JUzIAVjetiXxDznd2wdqVqVHfNkbSRW+xBywgqUaIXmFEGUol7VzPfmeFV8o8ok/eFlQB0h6ycqgwwMd0KjtJs2ys/k0F5GuN0G7fsgr+NRnhVgxj21yF6gYTN/8a9kscla/svdmp8ekexbALFnghbLBx3CgcqUxT+tAgMBAAGjgfswgfgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUbYygbSkl4kpjCNuxoezFGupA97UwgcgGA1UdIwSBwDCBvYAUbYygbSkl4kpjCNuxoezFGupA97WhgZmkgZYwgZMxFTATBgNVBAoTDEdvb2dsZSwgSW5jLjERMA8GA1UECxMIQ2hyb21lT1MxIjAgBgkqhkiG9w0BCQEWE2dzcGVuY2VyQGdvb2dsZS5jb20xGjAYBgNVBAcTEU1vdW50YWluIFZpZXcsIENBMQswCQYDVQQIEwJDQTELMAkGA1UEBhMCVVMxDTALBgNVBAMTBGxtYW+CCQChr4uWYBFg1TANBgkqhkiG9w0BAQQFAAOBgQCDq9wiQ4uVuf1CQU3sXfXCy1yqi5m8AsO9FxHvah5/SVFNwKllqTfedpCaWEswJ55YAojW9e+pY2Fh3Fo/Y9YkF88KCtLuBjjqDKCRLxF4LycjHODKyQQ7mN/t5AtP9yKOsNvWF+M4IfReg51kohau6FauQx87by5NIRPdkNPvkQ==" + }
diff --git a/chromeos/test/data/network/invalid_settings_with_repairs.json b/chromeos/test/data/network/invalid_settings_with_repairs.json index a3c19bfc..e42ece4 100644 --- a/chromeos/test/data/network/invalid_settings_with_repairs.json +++ b/chromeos/test/data/network/invalid_settings_with_repairs.json
@@ -582,5 +582,23 @@ "Certificates": [], "Type": "UnencryptedConfiguration", "UnknownField3": [], + }, + "invalid-scope-due-to-type": { + "Type": "Bla", + "Id": "ngjobkbdodapjbbncmagbccommkggmnj" + }, + "invalid-scope-due-to-missing-id": { + "Type": "Extension", + }, + "invalid-scope-due-to-invalid-id-length": { + "Type": "Extension", + "Id": "blabla" + }, + "invalid-scope-due-to-invalid-id-character": { + "Type": "Extension", + "Id": "9gjobkbdodapjbbncmagbccommkggmnj" + }, + "invalid-scope-due-to-missing-type": { + "Id": "ngjobkbdodapjbbncmagbccommkggmnj" } }
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager.cc b/components/autofill/core/browser/payments/local_card_migration_manager.cc index 3fb4f87..f3dae8c54 100644 --- a/components/autofill/core/browser/payments/local_card_migration_manager.cc +++ b/components/autofill/core/browser/payments/local_card_migration_manager.cc
@@ -389,7 +389,7 @@ // Pops up a larger, modal dialog showing the local cards to be uploaded. client_->ConfirmMigrateLocalCardToCloud( std::move(legal_message_), - client_->GetIdentityManager()->GetPrimaryAccountInfo().email, + personal_data_manager_->GetAccountInfoForPaymentsServer().email, migratable_credit_cards_, base::BindOnce( &LocalCardMigrationManager::OnUserAcceptedMainMigrationDialog,
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc index 988ea45..c97ddfc 100644 --- a/components/feature_engagement/public/feature_constants.cc +++ b/components/feature_engagement/public/feature_constants.cc
@@ -105,7 +105,7 @@ const base::Feature kIPHNewIncognitoTabTipFeature{ "IPH_NewIncognitoTabTip", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kIPHBadgedReadingListFeature{ - "IPH_BadgedReadingList", base::FEATURE_ENABLED_BY_DEFAULT}; + "IPH_BadgedReadingList", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kIPHBadgedTranslateManualTriggerFeature{ "IPH_BadgedTranslateManualTrigger", base::FEATURE_DISABLED_BY_DEFAULT}; #endif // defined(OS_IOS)
diff --git a/components/infobars/core/infobar_delegate.cc b/components/infobars/core/infobar_delegate.cc index 59ae71e..52e5e42 100644 --- a/components/infobars/core/infobar_delegate.cc +++ b/components/infobars/core/infobar_delegate.cc
@@ -84,38 +84,15 @@ return nullptr; } -InsecureContentInfoBarDelegate* - InfoBarDelegate::AsInsecureContentInfoBarDelegate() { - return nullptr; -} - -NativeAppInfoBarDelegate* InfoBarDelegate::AsNativeAppInfoBarDelegate() { - return nullptr; -} - PopupBlockedInfoBarDelegate* InfoBarDelegate::AsPopupBlockedInfoBarDelegate() { return nullptr; } -RegisterProtocolHandlerInfoBarDelegate* - InfoBarDelegate::AsRegisterProtocolHandlerInfoBarDelegate() { - return nullptr; -} - -ScreenCaptureInfoBarDelegate* - InfoBarDelegate::AsScreenCaptureInfoBarDelegate() { - return nullptr; -} - ThemeInstalledInfoBarDelegate* InfoBarDelegate::AsThemePreviewInfobarDelegate() { return nullptr; } -ThreeDAPIInfoBarDelegate* InfoBarDelegate::AsThreeDAPIInfoBarDelegate() { - return nullptr; -} - translate::TranslateInfoBarDelegate* InfoBarDelegate::AsTranslateInfoBarDelegate() { return nullptr;
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h index d0a528d8..a4d6f445 100644 --- a/components/infobars/core/infobar_delegate.h +++ b/components/infobars/core/infobar_delegate.h
@@ -12,13 +12,8 @@ class ConfirmInfoBarDelegate; class HungRendererInfoBarDelegate; -class InsecureContentInfoBarDelegate; -class NativeAppInfoBarDelegate; class PopupBlockedInfoBarDelegate; -class RegisterProtocolHandlerInfoBarDelegate; -class ScreenCaptureInfoBarDelegate; class ThemeInstalledInfoBarDelegate; -class ThreeDAPIInfoBarDelegate; #if defined(OS_ANDROID) namespace offline_pages { @@ -232,14 +227,8 @@ // Type-checking downcast routines: virtual ConfirmInfoBarDelegate* AsConfirmInfoBarDelegate(); virtual HungRendererInfoBarDelegate* AsHungRendererInfoBarDelegate(); - virtual InsecureContentInfoBarDelegate* AsInsecureContentInfoBarDelegate(); - virtual NativeAppInfoBarDelegate* AsNativeAppInfoBarDelegate(); virtual PopupBlockedInfoBarDelegate* AsPopupBlockedInfoBarDelegate(); - virtual RegisterProtocolHandlerInfoBarDelegate* - AsRegisterProtocolHandlerInfoBarDelegate(); - virtual ScreenCaptureInfoBarDelegate* AsScreenCaptureInfoBarDelegate(); virtual ThemeInstalledInfoBarDelegate* AsThemePreviewInfobarDelegate(); - virtual ThreeDAPIInfoBarDelegate* AsThreeDAPIInfoBarDelegate(); virtual translate::TranslateInfoBarDelegate* AsTranslateInfoBarDelegate(); #if defined(OS_ANDROID) virtual offline_pages::OfflinePageInfoBarDelegate*
diff --git a/components/keyed_service/content/browser_context_keyed_service_factory.h b/components/keyed_service/content/browser_context_keyed_service_factory.h index 1df7c684..5a3b2ccf 100644 --- a/components/keyed_service/content/browser_context_keyed_service_factory.h +++ b/components/keyed_service/content/browser_context_keyed_service_factory.h
@@ -110,7 +110,7 @@ // and the default implementation removes it from |mapping_| and deletes // the pointer. virtual void BrowserContextShutdown(content::BrowserContext* context); - virtual void BrowserContextDestroyed(content::BrowserContext* context); + void BrowserContextDestroyed(content::BrowserContext* context); private: friend class BrowserContextDependencyManagerUnittests;
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc index 3a6d677..313bac8 100644 --- a/components/omnibox/browser/autocomplete_match.cc +++ b/components/omnibox/browser/autocomplete_match.cc
@@ -388,13 +388,57 @@ } // static -bool AutocompleteMatch::MoreRelevant(const AutocompleteMatch& elem1, - const AutocompleteMatch& elem2) { +bool AutocompleteMatch::MoreRelevant(const AutocompleteMatch& match1, + const AutocompleteMatch& match2) { // For equal-relevance matches, we sort alphabetically, so that providers // who return multiple elements at the same priority get a "stable" sort // across multiple updates. - return (elem1.relevance == elem2.relevance) ? - (elem1.contents < elem2.contents) : (elem1.relevance > elem2.relevance); + return (match1.relevance == match2.relevance) + ? (match1.contents < match2.contents) + : (match1.relevance > match2.relevance); +} + +// static +bool AutocompleteMatch::BetterDuplicate(const AutocompleteMatch& match1, + const AutocompleteMatch& match2) { + // Prefer the Entity Match over the non-entity match, if they have the same + // |fill_into_edit| value. + if (match1.type == AutocompleteMatchType::SEARCH_SUGGEST_ENTITY && + match2.type != AutocompleteMatchType::SEARCH_SUGGEST_ENTITY && + match1.fill_into_edit == match2.fill_into_edit) { + return true; + } + if (match1.type != AutocompleteMatchType::SEARCH_SUGGEST_ENTITY && + match2.type == AutocompleteMatchType::SEARCH_SUGGEST_ENTITY && + match1.fill_into_edit == match2.fill_into_edit) { + return false; + } + + // Prefer matches allowed to be the default match. + if (match1.allowed_to_be_default_match && !match2.allowed_to_be_default_match) + return true; + if (!match1.allowed_to_be_default_match && match2.allowed_to_be_default_match) + return false; + + // Prefer document suggestions. + if (match1.type == AutocompleteMatchType::DOCUMENT_SUGGESTION && + match2.type != AutocompleteMatchType::DOCUMENT_SUGGESTION) { + return true; + } + if (match1.type != AutocompleteMatchType::DOCUMENT_SUGGESTION && + match2.type == AutocompleteMatchType::DOCUMENT_SUGGESTION) { + return false; + } + + // By default, simply prefer the more relevant match. + return MoreRelevant(match1, match2); +} + +// static +bool AutocompleteMatch::BetterDuplicateByIterator( + const std::vector<AutocompleteMatch>::const_iterator it1, + const std::vector<AutocompleteMatch>::const_iterator it2) { + return BetterDuplicate(*it1, *it2); } // static @@ -995,6 +1039,25 @@ return ShouldShowTabMatch(); } +void AutocompleteMatch::UpgradeMatchWithPropertiesFrom( + const AutocompleteMatch& duplicate_match) { + // For Entity Matches, absorb the duplicate match's |allowed_to_be_default| + // and |inline_autocomplete| properties. + if (type == AutocompleteMatchType::SEARCH_SUGGEST_ENTITY && + fill_into_edit == duplicate_match.fill_into_edit && + duplicate_match.allowed_to_be_default_match) { + allowed_to_be_default_match = true; + if (inline_autocompletion.empty()) + inline_autocompletion = duplicate_match.inline_autocompletion; + } + + // And always absorb the higher relevance score of duplicates. + if (duplicate_match.relevance > relevance) { + RecordAdditionalInfo(kACMatchPropertyScoreBoostedFrom, relevance); + relevance = duplicate_match.relevance; + } +} + #if DCHECK_IS_ON() void AutocompleteMatch::Validate() const { ValidateClassifications(contents, contents_class);
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h index 6c471d4..210451c0 100644 --- a/components/omnibox/browser/autocomplete_match.h +++ b/components/omnibox/browser/autocomplete_match.h
@@ -170,8 +170,16 @@ // Comparison function for determining whether the first match is better than // the second. - static bool MoreRelevant(const AutocompleteMatch& elem1, - const AutocompleteMatch& elem2); + static bool MoreRelevant(const AutocompleteMatch& match1, + const AutocompleteMatch& match2); + + // Comparison functions for determining whether the first match is preferred + // over the second when choosing between candidate duplicates. + static bool BetterDuplicate(const AutocompleteMatch& match1, + const AutocompleteMatch& match2); + static bool BetterDuplicateByIterator( + const std::vector<AutocompleteMatch>::const_iterator it1, + const std::vector<AutocompleteMatch>::const_iterator it2); // Helper functions for classes creating matches: // Fills in the classifications for |text|, using |style| as the base style @@ -424,6 +432,11 @@ // Returns true if the suggestion should show a tab match button or pedal. bool ShouldShowButton() const; + // Upgrades this match by absorbing the best properties from + // |duplicate_match|. For instance: if |duplicate_match| has a higher + // relevance score, this match's own relevance score will be upgraded. + void UpgradeMatchWithPropertiesFrom(const AutocompleteMatch& duplicate_match); + // The provider of this match, used to remember which provider the user had // selected when the input changes. This may be NULL, in which case there is // no provider (or memory of the user's selection). @@ -575,8 +588,11 @@ // property and associated value and which is presented in chrome://omnibox. AdditionalInfo additional_info; - // A list of matches culled during de-duplication process, retained to - // ensure if a match is deleted, the duplicates are deleted as well. + // A vector of matches culled during de-duplication process, sorted from + // second-best to worst according to the de-duplication preference criteria. + // This vector is retained so that if the user deletes a match, all the + // duplicates are deleted as well. This is also used for re-duping Search + // Entity vs. plain Search suggestions. std::vector<AutocompleteMatch> duplicate_matches; // So users of AutocompleteMatch can use the same ellipsis that it uses.
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc index 5be1c28..36dc87e 100644 --- a/components/omnibox/browser/autocomplete_result.cc +++ b/components/omnibox/browser/autocomplete_result.cc
@@ -9,6 +9,7 @@ #include <iterator> #include <string> #include <unordered_set> +#include <vector> #include "base/command_line.h" #include "base/logging.h" @@ -175,7 +176,7 @@ MaybeCullTailSuggestions(&matches_); } #endif - SortAndDedupMatches(input.current_page_classification(), &matches_); + DeduplicateMatches(input.current_page_classification(), &matches_); DemoteOnDeviceSearchSuggestions(); @@ -565,11 +566,11 @@ : GURL(); } -void AutocompleteResult::SortAndDedupMatches( +void AutocompleteResult::DeduplicateMatches( metrics::OmniboxEventProto::PageClassification page_classification, ACMatches* matches) { // Group matches by stripped URL and whether it's a calculator suggestion. - std::unordered_map<std::pair<GURL, bool>, std::list<ACMatches::iterator>, + std::unordered_map<std::pair<GURL, bool>, std::vector<ACMatches::iterator>, MatchGURLHash> url_to_matches; for (auto i = matches->begin(); i != matches->end(); ++i) { @@ -581,35 +582,39 @@ for (auto& group : url_to_matches) { const auto& key = group.first; const GURL& gurl = key.first; - // The list of matches whose URL are equivalent. - std::list<ACMatches::iterator>& duplicate_matches = group.second; + // The vector of matches whose URL are equivalent. + std::vector<ACMatches::iterator>& duplicate_matches = group.second; if (gurl.is_empty() || duplicate_matches.size() == 1) continue; - // Find the best match. - auto best_match = duplicate_matches.begin(); - for (auto i = std::next(best_match); i != duplicate_matches.end(); ++i) { - best_match = BetterDuplicate(i, best_match); - } + // Sort the matches best to worst, according to the deduplication criteria. + std::sort(duplicate_matches.begin(), duplicate_matches.end(), + &AutocompleteMatch::BetterDuplicateByIterator); + AutocompleteMatch& best_match = **duplicate_matches.begin(); - // Rotate the chosen match to be first, if necessary, so we know to keep it. - if (best_match != duplicate_matches.begin()) { - duplicate_matches.splice(duplicate_matches.begin(), duplicate_matches, - best_match); - } + // Process all the duplicate matches (from second-best to worst). + std::vector<AutocompleteMatch> duplicates_of_duplicates; + for (auto i = std::next(duplicate_matches.begin()); + i != duplicate_matches.end(); ++i) { + AutocompleteMatch& duplicate_match = **i; - // For each duplicate match, append its duplicates to that of the best - // match, then append it, before we erase it. - for (auto i = std::next(best_match); i != duplicate_matches.end(); ++i) { - auto& match = **i; - for (auto& dup_match : match.duplicate_matches) - (*best_match)->duplicate_matches.push_back(std::move(dup_match)); - // Erase the duplicates before copying it. We don't need them any more. - match.duplicate_matches.erase(match.duplicate_matches.begin(), - match.duplicate_matches.end()); - // Copy, don't move, because we need these below. - (*best_match)->duplicate_matches.push_back(match); + // Each duplicate match may also have its own duplicates. Move those to + // a temporary list, which will be eventually added to the end of + // |best_match.duplicate_matches|. Clear out the original list too. + std::move(duplicate_match.duplicate_matches.begin(), + duplicate_match.duplicate_matches.end(), + std::back_inserter(duplicates_of_duplicates)); + duplicate_match.duplicate_matches.clear(); + + best_match.UpgradeMatchWithPropertiesFrom(duplicate_match); + + // This should be a copy, not a move, since we don't erase duplicate + // matches from the master list until the very end. + DCHECK(duplicate_match.duplicate_matches.empty()); // Should be cleared. + best_match.duplicate_matches.push_back(duplicate_match); } + std::move(duplicates_of_duplicates.begin(), duplicates_of_duplicates.end(), + std::back_inserter(best_match.duplicate_matches)); } // Erase duplicate matches. @@ -651,91 +656,6 @@ } // static -std::list<ACMatches::iterator>::iterator AutocompleteResult::BetterDuplicate( - std::list<ACMatches::iterator>::iterator first, - std::list<ACMatches::iterator>::iterator second) { - std::list<ACMatches::iterator>::iterator preferred_match; - std::list<ACMatches::iterator>::iterator non_preferred_match; - // The following logic enforces constraints we care about regarding the - // the characteristics of the candidate matches. In order of priority: - // - // Entity suggestions: - // Entity suggestions are always preferred over non-entity suggestions, - // assuming both candidates have the same fill_into_edit value. In these - // cases, because the fill_into_edit value is the same in both and the - // selection of the entity suggestion appears to the user as simply a - // "promotion" of an equivalent suggestion by adding additional decoration, - // the entity suggestion is allowed to inherit the - // allowed_to_be_default_match and inline_autocompletion values from the - // other suggestion. - // - // allowed_to_be_default_match: - // A suggestion that is allowed to be the default match is always preferred - // over one that is not. - // - // Note that together these two constraints enforce an overall constraint, - // that if either candidate has allowed_to_be_default_match = true, the match - // which is preferred will always have allowed_to_be_default_match = true. - // - // Document suggestions: - // The icon and display of document suggestions are preferred over - // history, bookmark, etc. items. The actual URLs may be different, but - // logically dedupe to the same entity to which we'll navigate. - if ((*first)->type == ACMatchType::SEARCH_SUGGEST_ENTITY && - (*second)->type != ACMatchType::SEARCH_SUGGEST_ENTITY && - (*first)->fill_into_edit == (*second)->fill_into_edit) { - preferred_match = first; - non_preferred_match = second; - if ((*non_preferred_match)->allowed_to_be_default_match) { - (*preferred_match)->allowed_to_be_default_match = true; - (*preferred_match)->inline_autocompletion = - (*non_preferred_match)->inline_autocompletion; - } - } else if ((*first)->type != ACMatchType::SEARCH_SUGGEST_ENTITY && - (*second)->type == ACMatchType::SEARCH_SUGGEST_ENTITY && - (*first)->fill_into_edit == (*second)->fill_into_edit) { - preferred_match = second; - non_preferred_match = first; - if ((*non_preferred_match)->allowed_to_be_default_match) { - (*preferred_match)->allowed_to_be_default_match = true; - (*preferred_match)->inline_autocompletion = - (*non_preferred_match)->inline_autocompletion; - } - } else if ((*first)->allowed_to_be_default_match && - !(*second)->allowed_to_be_default_match) { - preferred_match = first; - non_preferred_match = second; - } else if ((*second)->allowed_to_be_default_match && - !(*first)->allowed_to_be_default_match) { - preferred_match = second; - non_preferred_match = first; - } else if ((*first)->type == ACMatchType::DOCUMENT_SUGGESTION && - (*second)->type != ACMatchType::DOCUMENT_SUGGESTION) { - preferred_match = first; - non_preferred_match = second; - } else if ((*first)->type != ACMatchType::DOCUMENT_SUGGESTION && - (*second)->type == ACMatchType::DOCUMENT_SUGGESTION) { - preferred_match = second; - non_preferred_match = first; - } else { - // By default, simply prefer the match with the higher relevance. Note that - // we do not apply type-based demotion here (CompareWithDemoteByType) - // because we only apply demotion when ordering the final set of matches. - return (*first)->relevance >= (*second)->relevance ? first : second; - } - - // If a match is preferred despite having a lower score, boost its score - // to that of the other match. - if ((*non_preferred_match)->relevance > (*preferred_match)->relevance) { - (*preferred_match) - ->RecordAdditionalInfo(kACMatchPropertyScoreBoostedFrom, - (*preferred_match)->relevance); - (*preferred_match)->relevance = (*non_preferred_match)->relevance; - } - return preferred_match; -} - -// static bool AutocompleteResult::HasMatchByDestination(const AutocompleteMatch& match, const ACMatches& matches) { for (auto i(matches.begin()); i != matches.end(); ++i) {
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h index b2fe7e7..2595cb5 100644 --- a/components/omnibox/browser/autocomplete_result.h +++ b/components/omnibox/browser/autocomplete_result.h
@@ -7,7 +7,6 @@ #include <stddef.h> -#include <list> #include <map> #include "base/macros.h" @@ -155,23 +154,13 @@ typedef ACMatches::iterator::difference_type matches_difference_type; #endif - // Sort |matches| by destination, taking into account demotions based on - // |page_classification| when resolving ties about which of several - // duplicates to keep. The matches are also deduplicated. Duplicate matches - // are stored in the |duplicate_matches| vector of the corresponding - // AutocompleteMatch. - static void SortAndDedupMatches( + // Modifies |matches| such that any duplicate matches are coalesced into + // representative "best" matches. The erased matches are moved into the + // |duplicate_matches| members of their representative matches. + static void DeduplicateMatches( metrics::OmniboxEventProto::PageClassification page_classification, ACMatches* matches); - // Examines |first| and |second| and returns the match that is preferred when - // choosing between candidate duplicates. Note that this may modify the - // relevance, allowed_to_be_default_match, or inline_autocompletion values of - // the returned match. - static std::list<ACMatches::iterator>::iterator BetterDuplicate( - std::list<ACMatches::iterator>::iterator first, - std::list<ACMatches::iterator>::iterator second); - // Returns true if |matches| contains a match with the same destination as // |match|. static bool HasMatchByDestination(const AutocompleteMatch& match,
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc index 3106e18f..56b7886a 100644 --- a/components/omnibox/browser/autocomplete_result_unittest.cc +++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -635,15 +635,15 @@ // Expect 3 unique results after SortAndCull(). ASSERT_EQ(3U, result.size()); - // Check that 3rd and 4th result got added to the first result as dups + // Check that 3rd and 4th result got added to the first result as duplicates // and also duplicates of the 4th match got copied. ASSERT_EQ(4U, result.match_at(0)->duplicate_matches.size()); const AutocompleteMatch* first_match = result.match_at(0); EXPECT_EQ(matches[2].destination_url, first_match->duplicate_matches.at(1).destination_url); - EXPECT_EQ(dup_match.destination_url, - first_match->duplicate_matches.at(2).destination_url); EXPECT_EQ(matches[3].destination_url, + first_match->duplicate_matches.at(2).destination_url); + EXPECT_EQ(dup_match.destination_url, first_match->duplicate_matches.at(3).destination_url); // Check that 6th result started a new list of dups for the second result. @@ -1765,8 +1765,8 @@ FakeAutocompleteProviderClient client; result.AppendDedicatedPedalMatches(&client, input); - result.SortAndDedupMatches(metrics::OmniboxEventProto::OTHER, - &result.matches_); + result.DeduplicateMatches(metrics::OmniboxEventProto::OTHER, + &result.matches_); // Exactly 2 (not 3) unique Pedals should be added with relevance close to max // of the triggering suggestions. @@ -1781,8 +1781,8 @@ // no duplicates are added, but the existing Pedal suggestion is updated. result.match_at(3)->contents = base::UTF8ToUTF16("open incognito tab"); result.AppendDedicatedPedalMatches(&client, input); - result.SortAndDedupMatches(metrics::OmniboxEventProto::OTHER, - &result.matches_); + result.DeduplicateMatches(metrics::OmniboxEventProto::OTHER, + &result.matches_); EXPECT_EQ(result.size(), 6u); EXPECT_NE(result.match_at(4)->pedal, nullptr); EXPECT_NE(result.match_at(5)->pedal, nullptr);
diff --git a/components/omnibox/browser/history_url_provider_unittest.cc b/components/omnibox/browser/history_url_provider_unittest.cc index d8b9d2810..70a17f0 100644 --- a/components/omnibox/browser/history_url_provider_unittest.cc +++ b/components/omnibox/browser/history_url_provider_unittest.cc
@@ -327,8 +327,8 @@ for (auto i = matches_.begin(); i != matches_.end(); ++i) { i->ComputeStrippedDestinationURL(input, service); } - AutocompleteResult::SortAndDedupMatches(input.current_page_classification(), - &matches_); + AutocompleteResult::DeduplicateMatches(input.current_page_classification(), + &matches_); std::sort(matches_.begin(), matches_.end(), &AutocompleteMatch::MoreRelevant); }
diff --git a/components/omnibox/browser/match_compare.h b/components/omnibox/browser/match_compare.h index 7cb6b5f8..c7f54fd9 100644 --- a/components/omnibox/browser/match_compare.h +++ b/components/omnibox/browser/match_compare.h
@@ -6,22 +6,23 @@ #define COMPONENTS_OMNIBOX_BROWSER_MATCH_COMPARE_H_ #include "components/omnibox/browser/omnibox_field_trial.h" +#include "third_party/metrics_proto/omnibox_event.pb.h" + +using PageClassification = metrics::OmniboxEventProto::PageClassification; // This class implements a special version of AutocompleteMatch::MoreRelevant // that allows matches of particular types to be demoted in AutocompleteResult. template <class Match> class CompareWithDemoteByType { public: - CompareWithDemoteByType( - metrics::OmniboxEventProto::PageClassification page_classification) { + CompareWithDemoteByType(PageClassification page_classification) { OmniboxFieldTrial::GetDemotionsByType(page_classification, &demotions_); } // Returns the relevance score of |match| demoted appropriately by // |demotions_by_type_|. int GetDemotedRelevance(const Match& match) const { - OmniboxFieldTrial::DemotionMultipliers::const_iterator demotion_it = - demotions_.find(match.type); + auto demotion_it = demotions_.find(match.type); return (demotion_it == demotions_.end()) ? match.relevance : (match.relevance * demotion_it->second); @@ -54,8 +55,7 @@ template <class Match> class DestinationSort { public: - DestinationSort( - metrics::OmniboxEventProto::PageClassification page_classification) + DestinationSort(PageClassification page_classification) : demote_by_type_(page_classification) {} bool operator()(const Match& elem1, const Match& elem2) { // Sort identical destination_urls together.
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc index 9b0a2a4..b0ce734 100644 --- a/components/omnibox/common/omnibox_features.cc +++ b/components/omnibox/common/omnibox_features.cc
@@ -246,7 +246,7 @@ // are exceptions in this regard and this experiment makes this more consistent. const base::Feature kUIExperimentShowPlaceholderWhenCaretShowing{ "OmniboxUIExperimentShowPlaceholderWhenCaretShowing", - base::FEATURE_ENABLED_BY_DEFAULT}; + base::FEATURE_DISABLED_BY_DEFAULT}; // Feature used to enable speculatively starting a service worker associated // with the destination of the default match when the user's input looks like a
diff --git a/components/onc/docs/onc_spec.md b/components/onc/docs/onc_spec.md index 08c92219..fde3afbe 100644 --- a/components/onc/docs/onc_spec.md +++ b/components/onc/docs/onc_spec.md
@@ -1647,6 +1647,11 @@ * If *true*, remove this certificate (only GUID should be set). +* **Scope** + * (optional, default Scope if missing) - [Scope](#Scope-type) + * If this is given, it specifies the scope in which the certificate should + be applied. + * **TrustBits** * (optional if **Type** is *Server* @@ -1688,6 +1693,21 @@ results are undefined, so this configuration should be prohibited by the configuration editor. +### Scope type +* **Id** + * (required if **Type** is *Extension*, otherwise ignored) - **string** + * If *Type* is *Extension*, this is the ID of the chrome extension for which + the certificate should be applied. +* **Type** + * (required) - **string** + * Allowed values are: + * *Extension* + * *Default* + * *Extension* indicates that the certificate should only be applied in the + scope of a chrome extension. + *Default* indicates that the scope the certificate applies in should not + be restricted. + ## Encrypted Configuration
diff --git a/components/onc/onc_constants.cc b/components/onc/onc_constants.cc index 9fda651..38e0e00d 100644 --- a/components/onc/onc_constants.cc +++ b/components/onc/onc_constants.cc
@@ -271,6 +271,7 @@ const char kClient[] = "Client"; const char kGUID[] = "GUID"; const char kPKCS12[] = "PKCS12"; +const char kScope[] = "Scope"; const char kServer[] = "Server"; const char kTrustBits[] = "TrustBits"; const char kType[] = "Type"; @@ -278,6 +279,13 @@ const char kX509[] = "X509"; } // namespace certificate +namespace scope { +const char kDefault[] = "Default"; +const char kExtension[] = "Extension"; +const char kId[] = "Id"; +const char kType[] = "Type"; +} // namespace scope + namespace encrypted { const char kAES256[] = "AES256"; const char kCipher[] = "Cipher";
diff --git a/components/onc/onc_constants.h b/components/onc/onc_constants.h index bea11275..868df30 100644 --- a/components/onc/onc_constants.h +++ b/components/onc/onc_constants.h
@@ -283,6 +283,7 @@ ONC_EXPORT extern const char kClient[]; ONC_EXPORT extern const char kGUID[]; ONC_EXPORT extern const char kPKCS12[]; +ONC_EXPORT extern const char kScope[]; ONC_EXPORT extern const char kServer[]; ONC_EXPORT extern const char kTrustBits[]; ONC_EXPORT extern const char kType[]; @@ -290,6 +291,13 @@ ONC_EXPORT extern const char kX509[]; } // namespace certificate +namespace scope { +ONC_EXPORT extern const char kDefault[]; +ONC_EXPORT extern const char kExtension[]; +ONC_EXPORT extern const char kId[]; +ONC_EXPORT extern const char kType[]; +} // namespace scope + namespace encrypted { ONC_EXPORT extern const char kAES256[]; ONC_EXPORT extern const char kCipher[];
diff --git a/components/optimization_guide/hints_fetcher.h b/components/optimization_guide/hints_fetcher.h index f8bca07..e7a05f5f 100644 --- a/components/optimization_guide/hints_fetcher.h +++ b/components/optimization_guide/hints_fetcher.h
@@ -24,6 +24,12 @@ namespace optimization_guide { +// Callback to inform the caller that the remote hints have been fetched and +// to pass back the fetched hints response from the remote Optimization Guide +// Service. +using HintsFetchedCallback = base::OnceCallback<void( + base::Optional<std::unique_ptr<proto::GetHintsResponse>>)>; + // A class to handle requests for optimization hints from a remote Optimization // Guide Service. // @@ -31,12 +37,6 @@ // Owner must ensure that |hint_cache| remains alive for the lifetime of // |HintsFetcher|. class HintsFetcher { - // Callback to inform the caller that the remote hints have been fetched and - // to pass back the fetched hints response from the remote Optimization Guide - // Service. - using HintsFetchedCallback = base::OnceCallback<void( - base::Optional<std::unique_ptr<proto::GetHintsResponse>>)>; - public: HintsFetcher( scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
diff --git a/components/password_manager/core/browser/leak_detection/encryption_utils.h b/components/password_manager/core/browser/leak_detection/encryption_utils.h index 9360163..88b1d53 100644 --- a/components/password_manager/core/browser/leak_detection/encryption_utils.h +++ b/components/password_manager/core/browser/leak_detection/encryption_utils.h
@@ -24,7 +24,7 @@ std::string HashUsername(base::StringPiece canonicalized_username); // Bucketizes |canonicalized_username| by hashing it and returning a prefix of -// 24 bits. +// |kUsernameHashPrefixLength| bits. std::string BucketizeUsername(base::StringPiece canonicalized_username); // Produces the username/password pair hash using scrypt algorithm.
diff --git a/components/password_manager/core/browser/leak_detection/leak_detection_request_utils.cc b/components/password_manager/core/browser/leak_detection/leak_detection_request_utils.cc index 4afb6d3..a58fbe71 100644 --- a/components/password_manager/core/browser/leak_detection/leak_detection_request_utils.cc +++ b/components/password_manager/core/browser/leak_detection/leak_detection_request_utils.cc
@@ -8,15 +8,30 @@ #include "base/containers/span.h" #include "base/strings/string_number_conversions.h" +#include "base/task/post_task.h" #include "components/password_manager/core/browser/leak_detection/encryption_utils.h" #include "components/password_manager/core/browser/leak_detection/leak_detection_api.pb.h" #include "components/password_manager/core/browser/leak_detection/single_lookup_response.h" namespace password_manager { +namespace { using google::internal::identity::passwords::leak::check::v1:: LookupSingleLeakRequest; +// Despite the function is short, it executes long. That's why it should be done +// asynchronously. +LookupSingleLeakData PrepareLookupSingleLeakData(const std::string& username, + const std::string& password) { + LookupSingleLeakData data; + data.username_hash_prefix = BucketizeUsername(CanonicalizeUsername(username)); + data.encrypted_payload = CipherEncrypt( + ScryptHashUsernameAndPassword(username, password), &data.encryption_key); + return data; +} + +} // namespace + LookupSingleLeakRequest MakeLookupSingleLeakRequest( base::StringPiece username, base::StringPiece password) { @@ -38,6 +53,17 @@ return request; } +void PrepareSingleLeakRequestData(const std::string& username, + const std::string& password, + SingleLeakRequestDataCallback callback) { + base::PostTaskAndReplyWithResult( + FROM_HERE, + {base::ThreadPool(), base::TaskPriority::USER_VISIBLE, + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, + base::BindOnce(&PrepareLookupSingleLeakData, username, password), + std::move(callback)); +} + bool ParseLookupSingleLeakResponse(const SingleLookupResponse& response) { // TODO(crbug.com/086298): Implement decrypting the response and checking // whether the credential was actually leaked.
diff --git a/components/password_manager/core/browser/leak_detection/leak_detection_request_utils.h b/components/password_manager/core/browser/leak_detection/leak_detection_request_utils.h index 5cf9960..c44cb3e 100644 --- a/components/password_manager/core/browser/leak_detection/leak_detection_request_utils.h +++ b/components/password_manager/core/browser/leak_detection/leak_detection_request_utils.h
@@ -5,6 +5,7 @@ #ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_LEAK_DETECTION_REQUEST_UTILS_H_ #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_LEAK_DETECTION_REQUEST_UTILS_H_ +#include "base/callback.h" #include "base/strings/string_piece_forward.h" namespace google { @@ -27,12 +28,38 @@ struct SingleLookupResponse; +// Stores all the data needed for one credential lookup. +struct LookupSingleLeakData { + LookupSingleLeakData() = default; + LookupSingleLeakData(LookupSingleLeakData&& other) = default; + LookupSingleLeakData& operator=(LookupSingleLeakData&& other) = default; + ~LookupSingleLeakData() = default; + + LookupSingleLeakData(const LookupSingleLeakData&) = delete; + LookupSingleLeakData& operator=(const LookupSingleLeakData&) = delete; + + std::string username_hash_prefix; + std::string encrypted_payload; + + std::string encryption_key; +}; + +using SingleLeakRequestDataCallback = + base::OnceCallback<void(LookupSingleLeakData)>; + // Constructs a LookupSingleLeakRequest from the provided |username| and // |password|. google::internal::identity::passwords::leak::check::v1::LookupSingleLeakRequest MakeLookupSingleLeakRequest(base::StringPiece username, base::StringPiece password); +// Asynchronously creates a data payload for single credential check. +// Callback is invoked on the calling thread with the protobuf and the +// encryption key used. +void PrepareSingleLeakRequestData(const std::string& username, + const std::string& password, + SingleLeakRequestDataCallback callback); + // Processes the provided |response| and returns whether the relevant credential // was leaked. bool ParseLookupSingleLeakResponse(const SingleLookupResponse& response);
diff --git a/components/password_manager/core/browser/leak_detection/leak_detection_request_utils_unittest.cc b/components/password_manager/core/browser/leak_detection/leak_detection_request_utils_unittest.cc index 209903b..979f05be 100644 --- a/components/password_manager/core/browser/leak_detection/leak_detection_request_utils_unittest.cc +++ b/components/password_manager/core/browser/leak_detection/leak_detection_request_utils_unittest.cc
@@ -5,12 +5,17 @@ #include "components/password_manager/core/browser/leak_detection/leak_detection_request_utils.h" #include "base/strings/string_piece.h" +#include "base/test/mock_callback.h" +#include "base/test/scoped_task_environment.h" #include "components/password_manager/core/browser/leak_detection/leak_detection_api.pb.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace password_manager { +using ::testing::ElementsAre; +using ::testing::Field; + TEST(LeakDetectionRequestUtils, MakeLookupSingleLeakRequest) { // Derived from test case used by the server-side implementation: // go/passwords-leak-test @@ -19,4 +24,19 @@ ::testing::ElementsAreArray({0x3D, 0x70, 0xD3})); } +TEST(LeakDetectionRequestUtils, PrepareSingleLeakRequestData) { + base::test::ScopedTaskEnvironment task_env; + base::MockCallback<SingleLeakRequestDataCallback> callback; + + PrepareSingleLeakRequestData("jonsnow", "1234", callback.Get()); + EXPECT_CALL( + callback, + Run(AllOf( + Field(&LookupSingleLeakData::username_hash_prefix, + ElementsAre(61, 112, -45)), + Field(&LookupSingleLeakData::encrypted_payload, testing::Ne("")), + Field(&LookupSingleLeakData::encryption_key, testing::Ne(""))))); + task_env.RunUntilIdle(); +} + } // namespace password_manager
diff --git a/components/previews/content/previews_optimization_guide_unittest.cc b/components/previews/content/previews_optimization_guide_unittest.cc index 6f7a58c..3c2b106 100644 --- a/components/previews/content/previews_optimization_guide_unittest.cc +++ b/components/previews/content/previews_optimization_guide_unittest.cc
@@ -5,6 +5,7 @@ #include "components/previews/content/previews_optimization_guide.h" #include <memory> +#include <utility> #include "base/base64.h" #include "base/bind.h" @@ -120,11 +121,6 @@ // A mock class implementation of HintsFetcher for unittesting // previews_optimization_guide. class TestHintsFetcher : public optimization_guide::HintsFetcher { - using HintsFetchedCallback = base::OnceCallback<void( - base::Optional< - std::unique_ptr<optimization_guide::proto::GetHintsResponse>>)>; - using HintsFetcher::FetchOptimizationGuideServiceHints; - public: TestHintsFetcher( scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, @@ -135,7 +131,8 @@ bool FetchOptimizationGuideServiceHints( const std::vector<std::string>& hosts, - HintsFetchedCallback hints_fetched_callback) override { + optimization_guide::HintsFetchedCallback hints_fetched_callback) + override { switch (fetch_state_) { case HintsFetcherEndState::kFetchFailed: std::move(hints_fetched_callback).Run(base::nullopt);
diff --git a/components/resources/OWNERS b/components/resources/OWNERS index 7038831..697f205 100644 --- a/components/resources/OWNERS +++ b/components/resources/OWNERS
@@ -23,6 +23,5 @@ per-file proximity_auth*=tengs@chromium.org per-file printing_resources.grdp=file://printing/OWNERS per-file security_interstitials_resources.grdp=file://components/security_interstitials/OWNERS -per-file supervised_user_error_page_resources.grdp=file://components/supervised_user_error_page/OWNERS per-file sync_driver_resources.grdp=file://components/sync/OWNERS per-file version_ui*=file://ui/webui/PLATFORM_OWNERS
diff --git a/components/search_engines/prepopulated_engines.json b/components/search_engines/prepopulated_engines.json index d834782..18bdc1f3 100644 --- a/components/search_engines/prepopulated_engines.json +++ b/components/search_engines/prepopulated_engines.json
@@ -34,20 +34,16 @@ // The following engines are included in country lists and are added to the // list of search engines on the first run depending on user's country. "elements": { - // Ask and Ask UK have suggestion URLs reachable over HTTPS, but they - // throw a certificate error, so those will remain as HTTP for now. "ask": { "name": "Ask", "keyword": "ask.com", "favicon_url": "https://sp.ask.com/sh/i/a16/favicon/favicon.ico", "search_url": "https://www.ask.com/web?q={searchTerms}", - "suggest_url": "http://ss.ask.com/query?q={searchTerms}&li=ff", + "suggest_url": "https://lss.sse-iacapps.com/query?q={searchTerms}&li=ff", "type": "SEARCH_ENGINE_ASK", "id": 4 }, - // Baidu's suggestion URL is not reachable over HTTPS, so it remains as - // HTTP for now. "baidu": { "name": "\u767e\u5ea6", "keyword": "baidu.com", @@ -58,7 +54,7 @@ "https://www.baidu.com/s?ie={inputEncoding}&word={searchTerms}", "https://www.baidu.com/{google:pathWildcard}/s?ie={inputEncoding}&word={searchTerms}" ], - "suggest_url": "http://suggestion.baidu.com/su?wd={searchTerms}&action=opensearch&ie={inputEncoding}", + "suggest_url": "https://suggestion.baidu.com/su?wd={searchTerms}&action=opensearch&ie={inputEncoding}", "type": "SEARCH_ENGINE_BAIDU", "id": 21 },
diff --git a/components/sync/engine/net/http_bridge.cc b/components/sync/engine/net/http_bridge.cc index 22e4655..e0dab5a 100644 --- a/components/sync/engine/net/http_bridge.cc +++ b/components/sync/engine/net/http_bridge.cc
@@ -267,8 +267,8 @@ resource_request->url = url_for_request_; resource_request->method = "POST"; resource_request->load_flags = - net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | - net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES; + net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; if (!extra_headers_.empty()) resource_request->headers.AddHeadersFromString(extra_headers_);
diff --git a/components/translate/core/browser/translate_script_unittest.cc b/components/translate/core/browser/translate_script_unittest.cc index f5cedd00..1ef1e10 100644 --- a/components/translate/core/browser/translate_script_unittest.cc +++ b/components/translate/core/browser/translate_script_unittest.cc
@@ -94,11 +94,8 @@ EXPECT_EQ(expected_url.GetOrigin().spec(), url.GetOrigin().spec()); EXPECT_EQ(expected_url.path(), url.path()); - int load_flags = last_resource_request.load_flags; - EXPECT_EQ(net::LOAD_DO_NOT_SEND_COOKIES, - load_flags & net::LOAD_DO_NOT_SEND_COOKIES); - EXPECT_EQ(net::LOAD_DO_NOT_SAVE_COOKIES, - load_flags & net::LOAD_DO_NOT_SAVE_COOKIES); + EXPECT_EQ(network::mojom::CredentialsMode::kOmit, + last_resource_request.credentials_mode); std::string expected_extra_headers = base::StringPrintf("%s\r\n\r\n", TranslateScript::kRequestHeader);
diff --git a/components/translate/core/browser/translate_url_fetcher.cc b/components/translate/core/browser/translate_url_fetcher.cc index f98f41a..c3cbb8f 100644 --- a/components/translate/core/browser/translate_url_fetcher.cc +++ b/components/translate/core/browser/translate_url_fetcher.cc
@@ -95,8 +95,7 @@ // Create and initialize URL loader. auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = url_; - resource_request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; if (!extra_request_header_.empty()) resource_request->headers.AddHeaderFromString(extra_request_header_);
diff --git a/components/translate/ios/browser/translate_controller.mm b/components/translate/ios/browser/translate_controller.mm index 677e096..b433524 100644 --- a/components/translate/ios/browser/translate_controller.mm +++ b/components/translate/ios/browser/translate_controller.mm
@@ -226,8 +226,7 @@ auto request = std::make_unique<network::ResourceRequest>(); request->method = method; request->url = GURL(url); - request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + request->credentials_mode = network::mojom::CredentialsMode::kOmit; auto fetcher = network::SimpleURLLoader::Create(std::move(request), NO_TRAFFIC_ANNOTATION_YET); fetcher->AttachStringForUpload(body, "application/x-www-form-urlencoded");
diff --git a/components/update_client/net/network_impl.cc b/components/update_client/net/network_impl.cc index c78853e..1e241a6 100644 --- a/components/update_client/net/network_impl.cc +++ b/components/update_client/net/network_impl.cc
@@ -106,9 +106,8 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = url; resource_request->method = "POST"; - resource_request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES | - net::LOAD_DISABLE_CACHE; + resource_request->load_flags = net::LOAD_DISABLE_CACHE; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; for (const auto& header : post_additional_headers) resource_request->headers.SetHeader(header.first, header.second); simple_url_loader_ = network::SimpleURLLoader::Create( @@ -149,9 +148,8 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = url; resource_request->method = "GET"; - resource_request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES | - net::LOAD_DISABLE_CACHE; + resource_request->load_flags = net::LOAD_DISABLE_CACHE; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; simple_url_loader_ = network::SimpleURLLoader::Create( std::move(resource_request), traffic_annotation); simple_url_loader_->SetRetryOptions(
diff --git a/components/web_resource/web_resource_service.cc b/components/web_resource/web_resource_service.cc index e5eec285..d1c0d4e 100644 --- a/components/web_resource/web_resource_service.cc +++ b/components/web_resource/web_resource_service.cc
@@ -154,9 +154,8 @@ resource_request->url = web_resource_server; // Do not let url fetcher affect existing state in system context // (by setting cookies, for example). - resource_request->load_flags = net::LOAD_DISABLE_CACHE | - net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES; + resource_request->load_flags = net::LOAD_DISABLE_CACHE; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; simple_url_loader_ = network::SimpleURLLoader::Create( std::move(resource_request), traffic_annotation_); simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc index 947d466..c18971eb 100644 --- a/content/app/content_main_runner_impl.cc +++ b/content/app/content_main_runner_impl.cc
@@ -909,10 +909,10 @@ StartBrowserThreadPool(); } - tracing::InitTracingPostThreadPoolStart(); - BrowserTaskExecutor::PostFeatureListSetup(); + tracing::InitTracingPostThreadPoolStartAndFeatureList(); + delegate_->PostTaskSchedulerStart(); if (should_start_service_manager_only)
diff --git a/content/browser/background_fetch/background_fetch_service_impl.cc b/content/browser/background_fetch/background_fetch_service_impl.cc index 9a8c1ef..1635f42 100644 --- a/content/browser/background_fetch/background_fetch_service_impl.cc +++ b/content/browser/background_fetch/background_fetch_service_impl.cc
@@ -35,10 +35,14 @@ // static void BackgroundFetchServiceImpl::CreateForWorker( - blink::mojom::BackgroundFetchServiceRequest request, - RenderProcessHost* render_process_host, - const url::Origin& origin) { + const ServiceWorkerRunningInfo& info, + mojo::PendingReceiver<blink::mojom::BackgroundFetchService> receiver) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + RenderProcessHost* render_process_host = + RenderProcessHost::FromID(info.process_id); + + if (!render_process_host) + return; base::PostTask( FROM_HERE, {BrowserThread::IO}, @@ -47,21 +51,19 @@ WrapRefCounted(static_cast<StoragePartitionImpl*>( render_process_host->GetStoragePartition()) ->GetBackgroundFetchContext()), - origin, /* render_frame_tree_node_id= */ 0, - /* wc_getter= */ base::NullCallback(), std::move(request))); + url::Origin::Create(info.script_url), + /* render_frame_tree_node_id= */ 0, + /* wc_getter= */ base::NullCallback(), std::move(receiver))); } // static void BackgroundFetchServiceImpl::CreateForFrame( - RenderProcessHost* render_process_host, - int render_frame_id, - blink::mojom::BackgroundFetchServiceRequest request) { + RenderFrameHost* render_frame_host, + mojo::PendingReceiver<blink::mojom::BackgroundFetchService> receiver) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - - DCHECK(render_process_host); - auto* render_frame_host = - RenderFrameHost::FromID(render_process_host->GetID(), render_frame_id); DCHECK(render_frame_host); + RenderProcessHost* render_process_host = render_frame_host->GetProcess(); + DCHECK(render_process_host); WebContents::Getter wc_getter = base::NullCallback(); @@ -81,7 +83,7 @@ ->GetBackgroundFetchContext()), render_frame_host->GetLastCommittedOrigin(), render_frame_host->GetFrameTreeNodeId(), std::move(wc_getter), - std::move(request))); + std::move(receiver))); } // static
diff --git a/content/browser/background_fetch/background_fetch_service_impl.h b/content/browser/background_fetch/background_fetch_service_impl.h index 42aa62f..9269874d 100644 --- a/content/browser/background_fetch/background_fetch_service_impl.h +++ b/content/browser/background_fetch/background_fetch_service_impl.h
@@ -20,7 +20,6 @@ namespace content { class BackgroundFetchContext; -class RenderProcessHost; class CONTENT_EXPORT BackgroundFetchServiceImpl : public blink::mojom::BackgroundFetchService { @@ -33,14 +32,12 @@ ~BackgroundFetchServiceImpl() override; static void CreateForWorker( - blink::mojom::BackgroundFetchServiceRequest request, - RenderProcessHost* render_process_host, - const url::Origin& origin); + const ServiceWorkerRunningInfo& info, + mojo::PendingReceiver<blink::mojom::BackgroundFetchService> receiver); static void CreateForFrame( - RenderProcessHost* render_process_host, - int render_frame_id, - blink::mojom::BackgroundFetchServiceRequest request); + RenderFrameHost* render_frame_host, + mojo::PendingReceiver<blink::mojom::BackgroundFetchService> receiver); // blink::mojom::BackgroundFetchService implementation. void Fetch(int64_t service_worker_registration_id,
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc index 167aabf..dfcd97c 100644 --- a/content/browser/browser_interface_binders.cc +++ b/content/browser/browser_interface_binders.cc
@@ -3,8 +3,10 @@ // found in the LICENSE file. #include "content/browser/browser_interface_binders.h" +#include "content/browser/background_fetch/background_fetch_service_impl.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/browser/service_worker/service_worker_provider_host.h" #include "content/browser/worker_host/dedicated_worker_host.h" #include "content/browser/worker_host/shared_worker_host.h" #include "content/browser/worker_host/shared_worker_instance.h" @@ -13,67 +15,100 @@ namespace content { namespace internal { -void PopulateFrameBinders(RenderFrameHostImpl* rfhi, +// Documents/frames +void PopulateFrameBinders(RenderFrameHostImpl* host, service_manager::BinderMap* map) { map->Add<blink::mojom::AudioContextManager>(base::BindRepeating( - &RenderFrameHostImpl::GetAudioContextManager, base::Unretained(rfhi))); + &RenderFrameHostImpl::GetAudioContextManager, base::Unretained(host))); map->Add<blink::mojom::FileSystemManager>(base::BindRepeating( - &RenderFrameHostImpl::GetFileSystemManager, base::Unretained(rfhi))); + &RenderFrameHostImpl::GetFileSystemManager, base::Unretained(host))); } void PopulateBinderMapWithContext( - RenderFrameHostImpl* rfhi, - service_manager::BinderMapWithContext<RenderFrameHost*>* map) {} + RenderFrameHostImpl* host, + service_manager::BinderMapWithContext<RenderFrameHost*>* map) { + map->Add<blink::mojom::BackgroundFetchService>( + base::BindRepeating(&BackgroundFetchServiceImpl::CreateForFrame)); +} -void PopulateBinderMap(RenderFrameHostImpl* rfhi, +void PopulateBinderMap(RenderFrameHostImpl* host, service_manager::BinderMap* map) { - PopulateFrameBinders(rfhi, map); + PopulateFrameBinders(host, map); } -RenderFrameHost* GetContextForHost(RenderFrameHostImpl* rfhi) { - return rfhi; +RenderFrameHost* GetContextForHost(RenderFrameHostImpl* host) { + return host; } -void PopulateDedicatedWorkerBinders(DedicatedWorkerHost* dwh, +// Dedicated workers +const url::Origin& GetContextForHost(DedicatedWorkerHost* host) { + return host->GetOrigin(); +} + +void PopulateDedicatedWorkerBinders(DedicatedWorkerHost* host, service_manager::BinderMap* map) {} -const url::Origin& GetContextForHost(DedicatedWorkerHost* dwh) { - return dwh->GetOrigin(); -} - void PopulateBinderMapWithContext( - DedicatedWorkerHost* dwh, + DedicatedWorkerHost* host, service_manager::BinderMapWithContext<const url::Origin&>* map) { // TODO(https://crbug.com/873661): Pass origin to FileSystemManager. map->Add<blink::mojom::FileSystemManager>( base::BindRepeating(&RenderProcessHost::BindFileSystemManager, - base::Unretained(dwh->GetProcessHost()))); + base::Unretained(host->GetProcessHost()))); } -void PopulateBinderMap(DedicatedWorkerHost* dwh, +void PopulateBinderMap(DedicatedWorkerHost* host, service_manager::BinderMap* map) { - PopulateDedicatedWorkerBinders(dwh, map); + PopulateDedicatedWorkerBinders(host, map); } -url::Origin GetContextForHost(SharedWorkerHost* swh) { - return url::Origin::Create(swh->instance()->url()); +// Shared workers +url::Origin GetContextForHost(SharedWorkerHost* host) { + return url::Origin::Create(host->instance()->url()); } -void PopulateSharedWorkerBinders(SharedWorkerHost* swh, +void PopulateSharedWorkerBinders(SharedWorkerHost* host, service_manager::BinderMap* map) {} void PopulateBinderMapWithContext( - SharedWorkerHost* swh, + SharedWorkerHost* host, service_manager::BinderMapWithContext<const url::Origin&>* map) { // TODO(https://crbug.com/873661): Pass origin to FileSystemManager. map->Add<blink::mojom::FileSystemManager>( base::BindRepeating(&RenderProcessHost::BindFileSystemManager, - base::Unretained(swh->GetProcessHost()))); + base::Unretained(host->GetProcessHost()))); } -void PopulateBinderMap(SharedWorkerHost* swh, service_manager::BinderMap* map) { - PopulateSharedWorkerBinders(swh, map); +void PopulateBinderMap(SharedWorkerHost* host, + service_manager::BinderMap* map) { + PopulateSharedWorkerBinders(host, map); +} + +// Service workers +ServiceWorkerRunningInfo GetContextForHost(ServiceWorkerProviderHost* host) { + // TODO(crbug.com/993409): pass Origin instead of GURL + return {host->running_hosted_version()->script_origin().GetURL(), + host->running_hosted_version()->version_id(), host->process_id()}; +} + +void PopulateServiceWorkerBinders(ServiceWorkerProviderHost* host, + service_manager::BinderMap* map) {} + +void PopulateBinderMapWithContext( + ServiceWorkerProviderHost* host, + service_manager::BinderMapWithContext<const ServiceWorkerRunningInfo&>* + map) { + // Using a task runner since ServiceWorkerProviderHost lives on the IO thread, + // and CreateForWorker() needs to be called on the UI thread. + map->Add<blink::mojom::BackgroundFetchService>( + base::BindRepeating(&BackgroundFetchServiceImpl::CreateForWorker), + base::CreateSingleThreadTaskRunnerWithTraits(BrowserThread::UI)); +} + +void PopulateBinderMap(ServiceWorkerProviderHost* host, + service_manager::BinderMap* map) { + PopulateServiceWorkerBinders(host, map); } } // namespace internal
diff --git a/content/browser/browser_interface_binders.h b/content/browser/browser_interface_binders.h index a6476c1a..4715337 100644 --- a/content/browser/browser_interface_binders.h +++ b/content/browser/browser_interface_binders.h
@@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_BROWSER_INTERFACE_BINDERS_H_ #define CONTENT_BROWSER_BROWSER_INTERFACE_BINDERS_H_ +#include "content/public/browser/service_worker_running_info.h" #include "services/service_manager/public/cpp/binder_map.h" #include "url/origin.h" @@ -14,6 +15,7 @@ class RenderFrameHostImpl; class DedicatedWorkerHost; class SharedWorkerHost; +class ServiceWorkerProviderHost; namespace internal { @@ -27,27 +29,36 @@ // handling InterfaceProvider's GetInterface() calls (see crbug.com/718652). // Registers the handlers for interfaces requested by frames. -void PopulateBinderMap(RenderFrameHostImpl* rfhi, +void PopulateBinderMap(RenderFrameHostImpl* host, service_manager::BinderMap* map); void PopulateBinderMapWithContext( - RenderFrameHostImpl* rfhi, + RenderFrameHostImpl* host, service_manager::BinderMapWithContext<RenderFrameHost*>* map); -RenderFrameHost* GetContextForHost(RenderFrameHostImpl* rfhi); +RenderFrameHost* GetContextForHost(RenderFrameHostImpl* host); // Registers the handlers for interfaces requested by dedicated workers. -void PopulateBinderMap(DedicatedWorkerHost* dwh, +void PopulateBinderMap(DedicatedWorkerHost* host, service_manager::BinderMap* map); void PopulateBinderMapWithContext( - DedicatedWorkerHost* dwh, + DedicatedWorkerHost* host, service_manager::BinderMapWithContext<const url::Origin&>* map); -const url::Origin& GetContextForHost(DedicatedWorkerHost* dwh); +const url::Origin& GetContextForHost(DedicatedWorkerHost* host); // Registers the handlers for interfaces requested by shared workers. -void PopulateBinderMap(SharedWorkerHost* swh, service_manager::BinderMap* map); +void PopulateBinderMap(SharedWorkerHost* host, service_manager::BinderMap* map); void PopulateBinderMapWithContext( - SharedWorkerHost* swh, + SharedWorkerHost* host, service_manager::BinderMapWithContext<const url::Origin&>* map); -url::Origin GetContextForHost(SharedWorkerHost* swh); +url::Origin GetContextForHost(SharedWorkerHost* host); + +// Registers the handlers for interfaces requested by service workers. +void PopulateBinderMap(ServiceWorkerProviderHost* host, + service_manager::BinderMap* map); +void PopulateBinderMapWithContext( + ServiceWorkerProviderHost* host, + service_manager::BinderMapWithContext<const ServiceWorkerRunningInfo&>* + map); +ServiceWorkerRunningInfo GetContextForHost(ServiceWorkerProviderHost* host); } // namespace internal } // namespace content
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h index 6c11c0c1..6c0d4f8 100644 --- a/content/browser/devtools/protocol/network_handler.h +++ b/content/browser/devtools/protocol/network_handler.h
@@ -202,10 +202,6 @@ const network::ResourceRequest& request, const std::string& cookie_line); - void WillSendNavigationRequest(net::HttpRequestHeaders* headers, - bool* skip_service_worker, - bool* disable_cache); - private: void RequestIntercepted(std::unique_ptr<InterceptedRequestInfo> request_info); void SetNetworkConditions(network::mojom::NetworkConditionsPtr conditions);
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 7f08c89..34bdc77 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -36,7 +36,6 @@ #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_state_impl.h" #include "content/browser/appcache/appcache_navigation_handle.h" -#include "content/browser/background_fetch/background_fetch_service_impl.h" #include "content/browser/bluetooth/web_bluetooth_service_impl.h" #include "content/browser/browser_main_loop.h" #include "content/browser/child_process_security_policy_impl.h" @@ -4344,9 +4343,6 @@ GetProcess()->GetStoragePartition()->GetFileSystemContext(), ChromeBlobStorageContext::GetFor(GetProcess()->GetBrowserContext()))); - registry_->AddInterface(base::BindRepeating( - &BackgroundFetchServiceImpl::CreateForFrame, GetProcess(), routing_id_)); - registry_->AddInterface(base::BindRepeating(&ContactsManagerImpl::Create, base::Unretained(this)));
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc index f12b1960..abccdad4 100644 --- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc +++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -36,6 +36,7 @@ #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/navigation_handle_observer.h" +#include "content/public/test/simple_url_loader_test_helper.h" #include "content/public/test/test_frame_navigation_observer.h" #include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_utils.h" @@ -45,13 +46,20 @@ #include "content/test/did_commit_navigation_interceptor.h" #include "content/test/frame_host_test_interface.mojom.h" #include "content/test/test_content_browser_client.h" +#include "net/base/net_errors.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" #include "services/network/test/test_url_loader_factory.h" #include "services/service_manager/public/mojom/interface_provider.mojom-test-utils.h" #include "testing/gmock/include/gmock/gmock.h" +#include "url/gurl.h" +#include "url/origin.h" #if defined(OS_ANDROID) #include "base/android/build_info.h" @@ -247,6 +255,33 @@ SetBrowserClientForTesting(old_client); } +// Check that the URLLoaderFactories created by RenderFrameHosts for renderers +// are not trusted. +IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, + URLLoaderFactoryNotTrusted) { + EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL("/echo"))); + network::mojom::URLLoaderFactoryPtr url_loader_factory; + shell()->web_contents()->GetMainFrame()->CreateNetworkServiceDefaultFactory( + mojo::MakeRequest(&url_loader_factory)); + + std::unique_ptr<network::ResourceRequest> request = + std::make_unique<network::ResourceRequest>(); + request->url = embedded_test_server()->GetURL("/echo"); + request->request_initiator = + url::Origin::Create(embedded_test_server()->base_url()); + request->trusted_params = network::ResourceRequest::TrustedParams(); + + content::SimpleURLLoaderTestHelper simple_loader_helper; + std::unique_ptr<network::SimpleURLLoader> simple_loader = + network::SimpleURLLoader::Create(std::move(request), + TRAFFIC_ANNOTATION_FOR_TESTS); + simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie( + url_loader_factory.get(), simple_loader_helper.GetCallback()); + simple_loader_helper.WaitForCallback(); + EXPECT_FALSE(simple_loader_helper.response_body()); + EXPECT_EQ(net::ERR_INVALID_ARGUMENT, simple_loader->NetError()); +} + namespace { class TestJavaScriptDialogManager : public JavaScriptDialogManager,
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc index d547331..5174402 100644 --- a/content/browser/loader/navigation_url_loader_impl.cc +++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -174,14 +174,16 @@ new_request->method = request_info->common_params->method; new_request->url = request_info->common_params->url; new_request->site_for_cookies = request_info->site_for_cookies; - new_request->trusted_network_isolation_key = + new_request->trusted_params = network::ResourceRequest::TrustedParams(); + new_request->trusted_params->network_isolation_key = request_info->network_isolation_key; if (request_info->is_main_frame) { - new_request->update_network_isolation_key_on_redirect = network::mojom:: - UpdateNetworkIsolationKeyOnRedirect::kUpdateTopFrameAndFrameOrigin; + new_request->trusted_params->update_network_isolation_key_on_redirect = + network::mojom::UpdateNetworkIsolationKeyOnRedirect:: + kUpdateTopFrameAndFrameOrigin; } else { - new_request->update_network_isolation_key_on_redirect = + new_request->trusted_params->update_network_isolation_key_on_redirect = network::mojom::UpdateNetworkIsolationKeyOnRedirect::kUpdateFrameOrigin; } @@ -330,7 +332,6 @@ return options; } - // This can be called on the UI or IO thread. void Start(std::unique_ptr<network::SharedURLLoaderFactoryInfo> network_loader_factory_info, ServiceWorkerNavigationHandle* @@ -736,16 +737,17 @@ if (resource_request_->resource_type == static_cast<int>(ResourceType::kMainFrame)) { url::Origin origin = url::Origin::Create(resource_request_->url); - resource_request_->trusted_network_isolation_key = + resource_request_->trusted_params->network_isolation_key = net::NetworkIsolationKey(origin, origin); } else { DCHECK_EQ(static_cast<int>(ResourceType::kSubFrame), resource_request_->resource_type); url::Origin subframe_origin = url::Origin::Create(resource_request_->url); base::Optional<url::Origin> top_frame_origin = - resource_request_->trusted_network_isolation_key.GetTopFrameOrigin(); + resource_request_->trusted_params->network_isolation_key + .GetTopFrameOrigin(); DCHECK(top_frame_origin); - resource_request_->trusted_network_isolation_key = + resource_request_->trusted_params->network_isolation_key = net::NetworkIsolationKey(top_frame_origin.value(), subframe_origin); } @@ -1435,6 +1437,7 @@ network::mojom::URLLoaderFactoryParams::New(); params->header_client = std::move(header_client); params->process_id = network::mojom::kBrowserProcessId; + params->is_trusted = true; params->is_corb_enabled = false; params->disable_web_security = base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc index 10b8267f..2aad945 100644 --- a/content/browser/loader/navigation_url_loader_impl_unittest.cc +++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -329,8 +329,10 @@ delegate.WaitForRequestStarted(); ASSERT_TRUE(most_recent_resource_request_); - EXPECT_EQ(net::NetworkIsolationKey(origin, origin), - most_recent_resource_request_->trusted_network_isolation_key); + ASSERT_TRUE(most_recent_resource_request_->trusted_params); + EXPECT_EQ( + net::NetworkIsolationKey(origin, origin), + most_recent_resource_request_->trusted_params->network_isolation_key); } TEST_F(NavigationURLLoaderImplTest, @@ -343,8 +345,10 @@ HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.GetOrigin().spec()); - EXPECT_EQ(net::NetworkIsolationKey(origin, origin), - most_recent_resource_request_->trusted_network_isolation_key); + ASSERT_TRUE(most_recent_resource_request_->trusted_params); + EXPECT_EQ( + net::NetworkIsolationKey(origin, origin), + most_recent_resource_request_->trusted_params->network_isolation_key); } TEST_F(NavigationURLLoaderImplTest, Redirect301Tests) {
diff --git a/content/browser/media/url_provision_fetcher.cc b/content/browser/media/url_provision_fetcher.cc index e72f880..0c61dbb 100644 --- a/content/browser/media/url_provision_fetcher.cc +++ b/content/browser/media/url_provision_fetcher.cc
@@ -77,8 +77,7 @@ })"); auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = GURL(request_string); - resource_request->load_flags = - net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; resource_request->method = "POST"; resource_request->headers.SetHeader("User-Agent", "Widevine CDM v1.0"); simple_url_loader_ = network::SimpleURLLoader::Create(
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc index fd1c4542..98576f6c 100644 --- a/content/browser/navigation_browsertest.cc +++ b/content/browser/navigation_browsertest.cc
@@ -290,11 +290,14 @@ URLLoaderInterceptor interceptor(base::BindLambdaForTesting( [&](URLLoaderInterceptor::RequestParams* params) -> bool { base::AutoLock top_frame_origins_lock(lock); - (*network_isolation_keys)[params->url_request.url] = - params->url_request.trusted_network_isolation_key; - (*update_network_isolation_key_on_redirects)[params->url_request - .url] = - params->url_request.update_network_isolation_key_on_redirect; + if (params->url_request.trusted_params) { + (*network_isolation_keys)[params->url_request.url] = + params->url_request.trusted_params->network_isolation_key; + (*update_network_isolation_key_on_redirects)[params->url_request + .url] = + params->url_request.trusted_params + ->update_network_isolation_key_on_redirect; + } if (params->url_request.url == final_resource) run_loop.Quit();
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index 66edee6..cb1bf72 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -343,8 +343,7 @@ /////////////////////////////////////////////////////////////////////////////// // RenderWidgetHostViewMac, RenderWidgetHostView implementation: -void RenderWidgetHostViewMac::InitAsChild( - gfx::NativeView parent_view) { +void RenderWidgetHostViewMac::InitAsChild(gfx::NativeView parent_view) { DCHECK_EQ(widget_type_, WidgetType::kFrame); } @@ -375,15 +374,15 @@ } RenderWidgetHostViewBase* - RenderWidgetHostViewMac::GetFocusedViewForTextSelection() { +RenderWidgetHostViewMac::GetFocusedViewForTextSelection() { // We obtain the TextSelection from focused RWH which is obtained from the // frame tree. BrowserPlugin-based guests' RWH is not part of the frame tree // and the focused RWH will be that of the embedder which is incorrect. In // this case we should use TextSelection for |this| since RWHV for guest // forwards text selection information to its platform view. - return is_guest_view_hack_ ? this : GetFocusedWidget() - ? GetFocusedWidget()->GetView() - : nullptr; + return is_guest_view_hack_ + ? this + : GetFocusedWidget() ? GetFocusedWidget()->GetView() : nullptr; } RenderWidgetHostDelegate* @@ -441,8 +440,6 @@ is_visible_ = true; ns_view_->SetVisible(is_visible_); browser_compositor_->SetViewVisible(is_visible_); - browser_compositor_->SetRenderWidgetHostIsHidden(false); - WasUnOccluded(); } @@ -450,11 +447,13 @@ is_visible_ = false; ns_view_->SetVisible(is_visible_); browser_compositor_->SetViewVisible(is_visible_); - host()->WasHidden(); - browser_compositor_->SetRenderWidgetHostIsHidden(true); + WasOccluded(); } void RenderWidgetHostViewMac::WasUnOccluded() { + if (!host()->is_hidden()) + return; + browser_compositor_->SetRenderWidgetHostIsHidden(false); DelegatedFrameHost* delegated_frame_host = @@ -483,6 +482,9 @@ } void RenderWidgetHostViewMac::WasOccluded() { + if (host()->is_hidden()) + return; + host()->WasHidden(); browser_compositor_->SetRenderWidgetHostIsHidden(true); } @@ -636,8 +638,8 @@ // the same as the caret position if the selection is collapsed. That's // what we want to try to keep centered on-screen if possible. gfx::Rect gfx_caret_rect(region->focus.edge_top_rounded().x(), - region->focus.edge_top_rounded().y(), - 1, region->focus.GetHeight()); + region->focus.edge_top_rounded().y(), 1, + region->focus.GetHeight()); gfx_caret_rect += view_bounds_in_window_dip_.OffsetFromOrigin(); gfx_caret_rect += window_frame_in_screen_dip_.OffsetFromOrigin();
diff --git a/content/browser/renderer_interface_binders.cc b/content/browser/renderer_interface_binders.cc index 95bed38..6edb128 100644 --- a/content/browser/renderer_interface_binders.cc +++ b/content/browser/renderer_interface_binders.cc
@@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/no_destructor.h" -#include "content/browser/background_fetch/background_fetch_service_impl.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/content_index/content_index_service_impl.h" #include "content/browser/cookie_store/cookie_store_context.h" @@ -204,8 +203,6 @@ ->CreateService(origin, std::move(request)); })); parameterized_binder_registry_.AddInterface( - base::BindRepeating(&BackgroundFetchServiceImpl::CreateForWorker)); - parameterized_binder_registry_.AddInterface( base::BindRepeating(&QuotaDispatcherHost::CreateForWorker)); parameterized_binder_registry_.AddInterface(base::BindRepeating( [](blink::mojom::CookieStoreRequest request, RenderProcessHost* host,
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc index 4d1a3938..03fba97 100644 --- a/content/browser/service_worker/embedded_worker_instance.cc +++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -794,7 +794,9 @@ // The host must be alive as long as |params->provider_info| is alive. owner_version_->provider_host()->CompleteStartWorkerPreparation( - process_id(), MakeRequest(¶ms->provider_info->interface_provider)); + process_id(), MakeRequest(¶ms->provider_info->interface_provider), + params->provider_info->browser_interface_broker + .InitWithNewPipeAndPassReceiver()); client_->StartWorker(std::move(params)); starting_phase_ = is_script_streaming ? SCRIPT_STREAMING : SENT_START_WORKER;
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc index 75ad35ed..510d586 100644 --- a/content/browser/service_worker/service_worker_provider_host.cc +++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -664,8 +664,9 @@ void ServiceWorkerProviderHost::CompleteStartWorkerPreparation( int process_id, - service_manager::mojom::InterfaceProviderRequest - interface_provider_request) { + service_manager::mojom::InterfaceProviderRequest interface_provider_request, + mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> + broker_receiver) { DCHECK(context_); DCHECK_EQ(ChildProcessHost::kInvalidUniqueID, render_process_id_); DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id); @@ -676,6 +677,8 @@ interface_provider_binding_.Bind(FilterRendererExposedInterfaces( blink::mojom::kNavigation_ServiceWorkerSpec, process_id, std::move(interface_provider_request))); + + broker_receiver_.Bind(std::move(broker_receiver)); } void ServiceWorkerProviderHost::CompleteWebWorkerPreparation() {
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h index 66ac098..425da59d 100644 --- a/content/browser/service_worker/service_worker_provider_host.h +++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -20,6 +20,7 @@ #include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "base/unguessable_token.h" +#include "content/browser/browser_interface_broker_impl.h" #include "content/browser/service_worker/service_worker_object_host.h" #include "content/browser/service_worker/service_worker_registration.h" #include "content/common/content_export.h" @@ -324,7 +325,9 @@ void CompleteStartWorkerPreparation( int process_id, service_manager::mojom::InterfaceProviderRequest - interface_provider_request); + interface_provider_request, + mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> + broker_receiver); // Called when the web worker main script resource has finished loading. // After this is called, is_response_committed() and is_execution_ready() @@ -685,6 +688,11 @@ // For service worker execution contexts. mojo::Binding<service_manager::mojom::InterfaceProvider> interface_provider_binding_; + BrowserInterfaceBrokerImpl<ServiceWorkerProviderHost, + const ServiceWorkerRunningInfo&> + broker_{this}; + mojo::Receiver<blink::mojom::BrowserInterfaceBroker> broker_receiver_{ + &broker_}; // For service worker clients. ClientPhase client_phase_ = ClientPhase::kInitial;
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.cc b/content/browser/service_worker/service_worker_single_script_update_checker.cc index 74c9892..f5a2f0ec 100644 --- a/content/browser/service_worker/service_worker_single_script_update_checker.cc +++ b/content/browser/service_worker/service_worker_single_script_update_checker.cc
@@ -132,7 +132,8 @@ // This key is used to isolate requests from different contexts in accessing // shared network resources like the http cache. - resource_request.trusted_network_isolation_key = + resource_request.trusted_params = network::ResourceRequest::TrustedParams(); + resource_request.trusted_params->network_isolation_key = net::NetworkIsolationKey(origin, origin); if (is_main_script_) {
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc index 9fe2eaa..f34b104 100644 --- a/content/browser/service_worker/service_worker_test_utils.cc +++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -307,7 +307,8 @@ &provider_info); host->CompleteStartWorkerPreparation( - process_id, mojo::MakeRequest(&provider_info->interface_provider)); + process_id, mojo::MakeRequest(&provider_info->interface_provider), + provider_info->browser_interface_broker.InitWithNewPipeAndPassReceiver()); output_endpoint->BindForServiceWorker(std::move(provider_info)); return host; }
diff --git a/content/browser/sms/sms_browsertest.cc b/content/browser/sms/sms_browsertest.cc index b4be6b6..1a9e7411 100644 --- a/content/browser/sms/sms_browsertest.cc +++ b/content/browser/sms/sms_browsertest.cc
@@ -72,20 +72,19 @@ auto* dialog = new NiceMock<MockSmsDialog>(); - base::OnceClosure on_confirm_callback; + SmsDialog::EventHandler hdl; EXPECT_CALL(delegate_, CreateSmsDialog(_)) .WillOnce(Return(ByMove(base::WrapUnique(dialog)))); - EXPECT_CALL(*dialog, Open(_, _, _)) - .WillOnce(Invoke([&on_confirm_callback](content::RenderFrameHost*, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_confirm_callback = std::move(on_confirm); - })); + EXPECT_CALL(*dialog, Open(_, _)) + .WillOnce( + Invoke([&hdl](RenderFrameHost*, SmsDialog::EventHandler handler) { + hdl = std::move(handler); + })); - EXPECT_CALL(*dialog, SmsReceived()).WillOnce(Invoke([&on_confirm_callback]() { - std::move(on_confirm_callback).Run(); + EXPECT_CALL(*dialog, SmsReceived()).WillOnce(Invoke([&hdl]() { + std::move(hdl).Run(SmsDialog::Event::kConfirm); })); auto* provider = new NiceMock<MockSmsProvider>(); @@ -117,20 +116,19 @@ auto* dialog = new NiceMock<MockSmsDialog>(); - base::OnceClosure on_confirm_callback; + SmsDialog::EventHandler hdl; EXPECT_CALL(delegate_, CreateSmsDialog(_)) .WillOnce(Return(ByMove(base::WrapUnique(dialog)))); - EXPECT_CALL(*dialog, Open(_, _, _)) - .WillOnce(Invoke([&on_confirm_callback](content::RenderFrameHost*, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_confirm_callback = std::move(on_confirm); - })); + EXPECT_CALL(*dialog, Open(_, _)) + .WillOnce( + Invoke([&hdl](RenderFrameHost*, SmsDialog::EventHandler handler) { + hdl = std::move(handler); + })); - EXPECT_CALL(*dialog, SmsReceived()).WillOnce(Invoke([&on_confirm_callback]() { - std::move(on_confirm_callback).Run(); + EXPECT_CALL(*dialog, SmsReceived()).WillOnce(Invoke([&hdl]() { + std::move(hdl).Run(SmsDialog::Event::kConfirm); })); auto* provider = new NiceMock<MockSmsProvider>(); @@ -244,7 +242,8 @@ true )"; - base::OnceClosure on_confirm1, on_confirm2; + SmsDialog::EventHandler hdl_1; + SmsDialog::EventHandler hdl_2; { base::RunLoop loop; @@ -260,12 +259,11 @@ loop.Quit(); })); - EXPECT_CALL(*dialog, Open(_, _, _)) - .WillOnce(Invoke([&on_confirm1](content::RenderFrameHost*, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_confirm1 = std::move(on_confirm); - })); + EXPECT_CALL(*dialog, Open(_, _)) + .WillOnce( + Invoke([&hdl_1](RenderFrameHost*, SmsDialog::EventHandler handler) { + hdl_1 = std::move(handler); + })); // First tab registers an observer. EXPECT_EQ(true, EvalJs(tab1, script)); @@ -287,12 +285,11 @@ loop.Quit(); })); - EXPECT_CALL(*dialog, Open(_, _, _)) - .WillOnce(Invoke([&on_confirm2](content::RenderFrameHost*, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_confirm2 = std::move(on_confirm); - })); + EXPECT_CALL(*dialog, Open(_, _)) + .WillOnce( + Invoke([&hdl_2](RenderFrameHost*, SmsDialog::EventHandler handler) { + hdl_2 = std::move(handler); + })); // Second tab registers an observer. EXPECT_EQ(true, EvalJs(tab2, script)); @@ -304,7 +301,7 @@ provider->NotifyReceive(url::Origin::Create(url), "hello1"); - std::move(on_confirm1).Run(); + std::move(hdl_1).Run(SmsDialog::Event::kConfirm); EXPECT_EQ("hello1", EvalJs(tab1, "sms")); @@ -312,7 +309,7 @@ provider->NotifyReceive(url::Origin::Create(url), "hello2"); - std::move(on_confirm2).Run(); + std::move(hdl_2).Run(SmsDialog::Event::kConfirm); EXPECT_EQ("hello2", EvalJs(tab2, "sms")); @@ -356,26 +353,24 @@ auto* dialog1 = new NiceMock<MockSmsDialog>(); auto* dialog2 = new NiceMock<MockSmsDialog>(); - base::OnceClosure on_confirm_callback1; - base::OnceClosure on_confirm_callback2; + SmsDialog::EventHandler hdl_1; + SmsDialog::EventHandler hdl_2; EXPECT_CALL(delegate_, CreateSmsDialog(_)) .WillOnce(Return(ByMove(base::WrapUnique(dialog1)))) .WillOnce(Return(ByMove(base::WrapUnique(dialog2)))); - EXPECT_CALL(*dialog1, Open(_, _, _)) - .WillOnce(Invoke([&on_confirm_callback1](content::RenderFrameHost*, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_confirm_callback1 = std::move(on_confirm); - })); + EXPECT_CALL(*dialog1, Open(_, _)) + .WillOnce( + Invoke([&hdl_1](RenderFrameHost*, SmsDialog::EventHandler handler) { + hdl_1 = std::move(handler); + })); - EXPECT_CALL(*dialog2, Open(_, _, _)) - .WillOnce(Invoke([&on_confirm_callback2](content::RenderFrameHost*, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_confirm_callback2 = std::move(on_confirm); - })); + EXPECT_CALL(*dialog2, Open(_, _)) + .WillOnce( + Invoke([&hdl_2](RenderFrameHost*, SmsDialog::EventHandler handler) { + hdl_2 = std::move(handler); + })); EXPECT_EQ(true, EvalJs(tab1, script)); EXPECT_EQ(true, EvalJs(tab2, script)); @@ -386,7 +381,7 @@ provider->NotifyReceive(url::Origin::Create(url1), "hello1"); - std::move(on_confirm_callback1).Run(); + std::move(hdl_1).Run(SmsDialog::Event::kConfirm); EXPECT_EQ("hello1", EvalJs(tab1, "sms")); @@ -394,7 +389,7 @@ provider->NotifyReceive(url::Origin::Create(url2), "hello2"); - std::move(on_confirm_callback2).Run(); + std::move(hdl_2).Run(SmsDialog::Event::kConfirm); EXPECT_EQ("hello2", EvalJs(tab2, "sms")); @@ -446,13 +441,11 @@ EXPECT_CALL(delegate, CreateSmsDialog(_)) .WillOnce(Return(ByMove(base::WrapUnique(dialog)))); - EXPECT_CALL(*dialog, Open(_, _, _)) - .WillOnce( - Invoke([](content::RenderFrameHost*, base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - // Simulates the user pressing "cancel". - std::move(on_cancel).Run(); - })); + EXPECT_CALL(*dialog, Open(_, _)) + .WillOnce(Invoke([](RenderFrameHost*, SmsDialog::EventHandler handler) { + // Simulates the user pressing "cancel". + std::move(handler).Run(SmsDialog::Event::kCancel); + })); EXPECT_CALL(*dialog, Close()).WillOnce(Return()); @@ -479,6 +472,23 @@ BrowserMainLoop::GetInstance()->SetSmsProviderForTesting( base::WrapUnique(provider)); + StrictMock<MockSmsWebContentsDelegate> delegate; + shell()->web_contents()->SetDelegate(&delegate); + + auto* dialog = new StrictMock<MockSmsDialog>(); + + EXPECT_CALL(delegate, CreateSmsDialog(_)) + .WillOnce(Return(ByMove(base::WrapUnique(dialog)))); + + EXPECT_CALL(*dialog, Open(_, _)) + .WillOnce(Invoke([](content::RenderFrameHost*, + content::SmsDialog::EventHandler event_handler) { + // Simulates the user pressing "Try again". + std::move(event_handler).Run(SmsDialog::Event::kTimeout); + })); + + EXPECT_CALL(*dialog, Close()).WillOnce(Return()); + std::string script = R"( (async () => { try {
diff --git a/content/browser/sms/sms_service.cc b/content/browser/sms/sms_service.cc index e3690f53..8141f78 100644 --- a/content/browser/sms/sms_service.cc +++ b/content/browser/sms/sms_service.cc
@@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/logging.h" #include "base/optional.h" #include "content/browser/sms/sms_metrics.h" #include "content/public/browser/sms_dialog.h" @@ -116,9 +117,7 @@ DCHECK(callback_); DCHECK(!timer_.IsRunning()); - std::move(callback_).Run(blink::mojom::SmsStatus::kTimeout, base::nullopt); - - Dismiss(); + prompt_->SmsTimeout(); } void SmsService::OnConfirm() { @@ -143,16 +142,36 @@ Process(SmsStatus::kCancelled, base::nullopt); } +void SmsService::OnTryAgain() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + Process(SmsStatus::kTimeout, base::nullopt); +} + +void SmsService::OnEvent(SmsDialog::Event event_type) { + switch (event_type) { + case SmsDialog::Event::kConfirm: + OnConfirm(); + return; + case SmsDialog::Event::kCancel: + OnCancel(); + return; + case SmsDialog::Event::kTimeout: + OnTryAgain(); + return; + } + DVLOG(1) << "Unsupported event type: " << event_type; + NOTREACHED(); +} + void SmsService::Prompt() { WebContents* web_contents = - content::WebContents::FromRenderFrameHost(render_frame_host()); + WebContents::FromRenderFrameHost(render_frame_host()); const url::Origin origin = render_frame_host()->GetLastCommittedOrigin(); prompt_ = web_contents->GetDelegate()->CreateSmsDialog(origin); if (prompt_) { - prompt_->Open( - render_frame_host(), - base::BindOnce(&SmsService::OnConfirm, base::Unretained(this)), - base::BindOnce(&SmsService::OnCancel, base::Unretained(this))); + prompt_->Open(render_frame_host(), + base::BindOnce(&SmsService::OnEvent, base::Unretained(this))); } }
diff --git a/content/browser/sms/sms_service.h b/content/browser/sms/sms_service.h index 5426cbe..2c7466ff 100644 --- a/content/browser/sms/sms_service.h +++ b/content/browser/sms/sms_service.h
@@ -15,6 +15,7 @@ #include "content/browser/sms/sms_provider.h" #include "content/common/content_export.h" #include "content/public/browser/frame_service_base.h" +#include "content/public/browser/sms_dialog.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "third_party/blink/public/mojom/sms/sms_receiver.mojom.h" #include "url/origin.h" @@ -60,10 +61,15 @@ // Callback when the |timer_| times out. void OnTimeout(); - // Callback when the user manually clicks 'Confirm' button. + // Called when the user manually clicks 'Confirm' button. void OnConfirm(); - // Callback when the user manually dismisses the dialog. + // Called when the user manually dismisses the dialog. void OnCancel(); + // Callback when the user manually clicks 'Try again' button after a timeout. + void OnTryAgain(); + + // Handles the user's action. + void OnEvent(SmsDialog::Event event_type); // |sms_provider_| is safe because all instances of SmsProvider are owned // by a SmsKeyedService, which is owned by a Profile, which transitively
diff --git a/content/browser/sms/sms_service_unittest.cc b/content/browser/sms/sms_service_unittest.cc index 25529c4..32e0f18 100644 --- a/content/browser/sms/sms_service_unittest.cc +++ b/content/browser/sms/sms_service_unittest.cc
@@ -78,23 +78,22 @@ NiceMock<MockSmsWebContentsDelegate>* delegate() { return &delegate_; } NiceMock<MockSmsProvider>* provider() { return &provider_; } - void SetupSmsDialog(content::RenderFrameHost* rfh) { + void SetupSmsDialog(RenderFrameHost* rfh) { auto* dialog = new NiceMock<MockSmsDialog>(); EXPECT_CALL(*delegate(), CreateSmsDialog(_)) .WillOnce(Return(ByMove(base::WrapUnique(dialog)))); - EXPECT_CALL(*dialog, Open(rfh, _, _)) + EXPECT_CALL(*dialog, Open(rfh, _)) .WillOnce( - Invoke([&](content::RenderFrameHost*, base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_confirm_callback_ = std::move(on_confirm); + Invoke([&](RenderFrameHost*, SmsDialog::EventHandler handler) { + handler_ = std::move(handler); })); EXPECT_CALL(*dialog, SmsReceived()).WillOnce(Invoke([&]() { // Simulates user clicking the "Confirm" button to verify received // sms. - std::move(on_confirm_callback_).Run(); + std::move(handler_).Run(SmsDialog::Event::kConfirm); })); EXPECT_CALL(*dialog, Close()).WillOnce(Return()); @@ -113,7 +112,7 @@ NiceMock<MockSmsProvider> provider_; blink::mojom::SmsReceiverPtr service_ptr_; std::unique_ptr<SmsService> service_; - base::OnceClosure on_confirm_callback_; + SmsDialog::EventHandler handler_; }; class SmsServiceTest : public RenderViewHostTestHarness { @@ -350,6 +349,18 @@ loop.Quit(); })); + auto* dialog = new NiceMock<MockSmsDialog>(); + + EXPECT_CALL(*service.delegate(), CreateSmsDialog(_)) + .WillOnce(Return(ByMove(base::WrapUnique(dialog)))); + + EXPECT_CALL(*dialog, Open(main_rfh(), _)) + .WillOnce(Invoke([](content::RenderFrameHost*, + content::SmsDialog::EventHandler event_handler) { + // Simulates the user pressing "Try again". + std::move(event_handler).Run(SmsDialog::Event::kTimeout); + })); + loop.Run(); } @@ -441,13 +452,11 @@ EXPECT_CALL(*service.delegate(), CreateSmsDialog(_)) .WillOnce(Return(ByMove(base::WrapUnique(dialog)))); - EXPECT_CALL(*dialog, Open(main_rfh(), _, _)) - .WillOnce( - Invoke([](content::RenderFrameHost*, base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - // Simulates the user pressing "Cancel". - std::move(on_cancel).Run(); - })); + EXPECT_CALL(*dialog, Open(main_rfh(), _)) + .WillOnce(Invoke([](RenderFrameHost*, SmsDialog::EventHandler handler) { + // Simulates the user pressing "Cancel". + std::move(handler).Run(SmsDialog::Event::kCancel); + })); loop.Run(); @@ -479,7 +488,12 @@ // Deliberately avoid calling the on_cancel callback, to simulate the // sms being timed out before the user cancels it. - EXPECT_CALL(*dialog, Open(main_rfh(), _, _)).WillOnce(Return()); + EXPECT_CALL(*dialog, Open(main_rfh(), _)) + .WillOnce(Invoke([](content::RenderFrameHost*, + content::SmsDialog::EventHandler event_handler) { + // Simulates the user pressing "Try again". + std::move(event_handler).Run(SmsDialog::Event::kTimeout); + })); EXPECT_CALL(*dialog, Close()).WillOnce(Return()); @@ -496,24 +510,28 @@ auto* dialog1 = new NiceMock<MockSmsDialog>(); auto* dialog2 = new NiceMock<MockSmsDialog>(); - base::OnceClosure on_confirm_callback; + SmsDialog::EventHandler hdl; EXPECT_CALL(*service.delegate(), CreateSmsDialog(_)) .WillOnce(Return(ByMove(base::WrapUnique(dialog1)))) .WillOnce(Return(ByMove(base::WrapUnique(dialog2)))); - EXPECT_CALL(*dialog1, Open(main_rfh(), _, _)) - .WillOnce(Invoke([&on_confirm_callback](content::RenderFrameHost*, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_confirm_callback = std::move(on_confirm); + EXPECT_CALL(*dialog1, Open(main_rfh(), _)) + .WillOnce( + Invoke([&hdl](RenderFrameHost*, SmsDialog::EventHandler handler) { + hdl = std::move(handler); + })); + + EXPECT_CALL(*dialog2, Open(main_rfh(), _)) + .WillOnce(Invoke([](content::RenderFrameHost*, + content::SmsDialog::EventHandler event_handler) { + // Simulates the user pressing "Try again". + std::move(event_handler).Run(SmsDialog::Event::kTimeout); })); - EXPECT_CALL(*dialog2, Open(main_rfh(), _, _)).Times(1); - - EXPECT_CALL(*dialog1, SmsReceived()) - .WillOnce(Invoke( - [&on_confirm_callback]() { std::move(on_confirm_callback).Run(); })); + EXPECT_CALL(*dialog1, SmsReceived()).WillOnce(Invoke([&hdl]() { + std::move(hdl).Run(SmsDialog::Event::kConfirm); + })); EXPECT_CALL(*service.provider(), Retrieve()) .WillOnce(Invoke([&service]() { @@ -601,13 +619,11 @@ EXPECT_CALL(*service.delegate(), CreateSmsDialog(_)) .WillOnce(Return(ByMove(base::WrapUnique(dialog)))); - EXPECT_CALL(*dialog, Open(main_rfh(), _, _)) - .WillOnce( - Invoke([](content::RenderFrameHost*, base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - // Simulates the user pressing "Cancel". - std::move(on_cancel).Run(); - })); + EXPECT_CALL(*dialog, Open(main_rfh(), _)) + .WillOnce(Invoke([](RenderFrameHost*, SmsDialog::EventHandler handler) { + // Simulates the user pressing "Cancel". + std::move(handler).Run(SmsDialog::Event::kCancel); + })); loop.Run(); @@ -637,21 +653,20 @@ })); auto* dialog = new NiceMock<MockSmsDialog>(); - base::OnceClosure on_cancel_callback; + SmsDialog::EventHandler hdl; EXPECT_CALL(*service.delegate(), CreateSmsDialog(_)) .WillOnce(Return(ByMove(base::WrapUnique(dialog)))); - EXPECT_CALL(*dialog, Open(main_rfh(), _, _)) - .WillOnce(Invoke([&on_cancel_callback](content::RenderFrameHost*, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) { - on_cancel_callback = std::move(on_cancel); - })); + EXPECT_CALL(*dialog, Open(main_rfh(), _)) + .WillOnce( + Invoke([&hdl](RenderFrameHost*, SmsDialog::EventHandler handler) { + hdl = std::move(handler); + })); EXPECT_CALL(*dialog, SmsReceived()).WillOnce(Invoke([&]() { // Simulates user clicking the "Cancel" once the SMS has been received. - std::move(on_cancel_callback).Run(); + std::move(hdl).Run(SmsDialog::Event::kCancel); })); loop.Run();
diff --git a/content/browser/sms/test/mock_sms_dialog.h b/content/browser/sms/test/mock_sms_dialog.h index 8daf897..73024b4 100644 --- a/content/browser/sms/test/mock_sms_dialog.h +++ b/content/browser/sms/test/mock_sms_dialog.h
@@ -16,10 +16,10 @@ MockSmsDialog(); ~MockSmsDialog() override; - MOCK_METHOD3(Open, - void(RenderFrameHost*, base::OnceClosure, base::OnceClosure)); + MOCK_METHOD2(Open, void(RenderFrameHost*, EventHandler handler)); MOCK_METHOD0(Close, void()); MOCK_METHOD0(SmsReceived, void()); + MOCK_METHOD0(SmsTimeout, void()); private: DISALLOW_COPY_AND_ASSIGN(MockSmsDialog);
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc index d5b4ce8..4fbc08e 100644 --- a/content/browser/storage_partition_impl.cc +++ b/content/browser/storage_partition_impl.cc
@@ -1801,6 +1801,9 @@ network::mojom::URLLoaderFactoryParams::New(); params->process_id = network::mojom::kBrowserProcessId; params->is_corb_enabled = corb_enabled; + // Corb requests are likely made on behalf of untrusted renderers. + if (!corb_enabled) + params->is_trusted = true; params->disable_web_security = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableWebSecurity);
diff --git a/content/browser/url_loader_factory_getter.cc b/content/browser/url_loader_factory_getter.cc index a30aac9..377ac68 100644 --- a/content/browser/url_loader_factory_getter.cc +++ b/content/browser/url_loader_factory_getter.cc
@@ -285,6 +285,8 @@ return; network::mojom::URLLoaderFactoryParamsPtr params = network::mojom::URLLoaderFactoryParams::New(); + // The browser process is considered trusted. + params->is_trusted = true; params->process_id = network::mojom::kBrowserProcessId; params->is_corb_enabled = is_corb_enabled; params->disable_web_security =
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc index 93d41527..98b244e 100644 --- a/content/browser/worker_host/worker_script_fetch_initiator.cc +++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -119,7 +119,8 @@ resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = script_url; resource_request->site_for_cookies = script_url; - resource_request->trusted_network_isolation_key = + resource_request->trusted_params = network::ResourceRequest::TrustedParams(); + resource_request->trusted_params->network_isolation_key = trusted_network_isolation_key; resource_request->request_initiator = request_initiator; resource_request->referrer = sanitized_referrer.url,
diff --git a/content/child/child_process.cc b/content/child/child_process.cc index 6b0532c..33de333 100644 --- a/content/child/child_process.cc +++ b/content/child/child_process.cc
@@ -16,6 +16,7 @@ #include "base/threading/thread_local.h" #include "build/build_config.h" #include "content/child/child_thread_impl.h" +#include "services/tracing/public/cpp/trace_startup.h" #include "third_party/blink/public/common/features.h" namespace content { @@ -51,6 +52,7 @@ DCHECK(base::ThreadPoolInstance::Get()); initialized_thread_pool_ = true; } + tracing::InitTracingPostThreadPoolStartAndFeatureList(); // We can't recover from failing to start the IO thread. base::Thread::Options thread_options(base::MessagePumpType::IO, 0);
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn index 0e2f82f..ab69d8e 100644 --- a/content/public/android/BUILD.gn +++ b/content/public/android/BUILD.gn
@@ -364,6 +364,7 @@ "//content/public/browser/browsing_data_remover.h", "//content/public/browser/invalidate_type.h", "//content/public/browser/navigation_controller.h", + "//content/public/browser/sms_dialog.h", "//content/public/common/browser_controls_state.h", "//content/public/common/console_message_level.h", "//content/public/common/screen_orientation_values.h",
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc index 23d3741..c7fc99c0 100644 --- a/content/public/app/content_browser_manifest.cc +++ b/content/public/app/content_browser_manifest.cc
@@ -197,7 +197,6 @@ .ExposeInterfaceFilterCapability_Deprecated( "navigation:service_worker", "renderer", std::set<const char*>{ - "blink.mojom.BackgroundFetchService", "blink.mojom.CacheStorage", "blink.mojom.CookieStore", "blink.mojom.ContentIndexService", "blink.mojom.IDBFactory", "blink.mojom.LockManager", @@ -218,7 +217,6 @@ "autofill.mojom.AutofillDriver", "autofill.mojom.PasswordManagerDriver", "blink.mojom.AnchorElementMetricsHost", - "blink.mojom.BackgroundFetchService", "blink.mojom.CacheStorage", "blink.mojom.ColorChooserFactory", "blink.mojom.ContactsManager",
diff --git a/content/public/browser/sms_dialog.h b/content/public/browser/sms_dialog.h index 464c75a0..87d1a2e 100644 --- a/content/public/browser/sms_dialog.h +++ b/content/public/browser/sms_dialog.h
@@ -16,13 +16,25 @@ // messages. class CONTENT_EXPORT SmsDialog { public: + // A Java counterpart will be generated for this enum. + // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.content_public.browser.sms + enum Event { + // User manually clicked the 'Confirm' button. + kConfirm = 0, + // User manually dismissed the SMS dialog. + kCancel = 1, + // User manually clicked 'Try again' button after a timeout. + kTimeout = 2, + }; + + using EventHandler = base::OnceCallback<void(Event)>; + SmsDialog() = default; virtual ~SmsDialog() = default; - virtual void Open(content::RenderFrameHost* host, - base::OnceClosure on_confirm, - base::OnceClosure on_cancel) = 0; + virtual void Open(RenderFrameHost* host, EventHandler handler) = 0; virtual void Close() = 0; virtual void SmsReceived() = 0; + virtual void SmsTimeout() = 0; private: DISALLOW_COPY_AND_ASSIGN(SmsDialog);
diff --git a/content/public/test/DEPS b/content/public/test/DEPS index 40dcfe7..75250ec5 100644 --- a/content/public/test/DEPS +++ b/content/public/test/DEPS
@@ -11,6 +11,7 @@ "+services/audio/public/mojom", "+services/network", "+services/service_manager", + "+services/tracing/public/cpp", "+testing/android/native_test/native_browser_test_support.h", "+ui/ozone/public", "+ui/views/test",
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc index 76c3dfa68..75b806a 100644 --- a/content/public/test/browser_test_base.cc +++ b/content/public/test/browser_test_base.cc
@@ -62,6 +62,7 @@ #include "services/network/public/mojom/network_service_test.mojom.h" #include "services/service_manager/embedder/switches.h" #include "services/service_manager/public/cpp/connector.h" +#include "services/tracing/public/cpp/trace_startup.h" #include "ui/base/platform_window_defaults.h" #include "ui/compositor/compositor_switches.h" #include "ui/display/display_switches.h" @@ -412,6 +413,7 @@ StartBrowserThreadPool(); BrowserTaskExecutor::PostFeatureListSetup(); + tracing::InitTracingPostThreadPoolStartAndFeatureList(); delegate->PostTaskSchedulerStart(); }
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index f632b1e8c..195b893 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -180,16 +180,20 @@ "media/audio/mojo_audio_output_ipc.h", "media/audio_decoder.cc", "media/audio_decoder.h", + "media/batching_media_log.cc", + "media/batching_media_log.h", "media/gpu/gpu_video_accelerator_factories_impl.cc", "media/gpu/gpu_video_accelerator_factories_impl.h", + "media/inspector_media_event_handler.cc", + "media/inspector_media_event_handler.h", "media/media_factory.cc", "media/media_factory.h", "media/media_permission_dispatcher.cc", "media/media_permission_dispatcher.h", "media/render_media_client.cc", "media/render_media_client.h", - "media/render_media_log.cc", - "media/render_media_log.h", + "media/render_media_event_handler.cc", + "media/render_media_event_handler.h", "media/renderer_webaudiodevice_impl.cc", "media/renderer_webaudiodevice_impl.h", "media/renderer_webmediaplayer_delegate.cc",
diff --git a/content/renderer/media/render_media_log.cc b/content/renderer/media/batching_media_log.cc similarity index 83% rename from content/renderer/media/render_media_log.cc rename to content/renderer/media/batching_media_log.cc index a47d0f1..3b593ca 100644 --- a/content/renderer/media/render_media_log.cc +++ b/content/renderer/media/batching_media_log.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/renderer/media/render_media_log.h" +#include "content/renderer/media/batching_media_log.h" #include <sstream> @@ -12,7 +12,6 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_tick_clock.h" -#include "content/common/view_messages.h" #include "content/public/common/content_client.h" #include "content/public/renderer/content_renderer_client.h" #include "content/public/renderer/render_thread.h" @@ -36,22 +35,25 @@ namespace content { -RenderMediaLog::RenderMediaLog( +BatchingMediaLog::BatchingMediaLog( const GURL& security_origin, - scoped_refptr<base::SingleThreadTaskRunner> task_runner) + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + std::unique_ptr<EventHandler> event_handler) : security_origin_(security_origin), task_runner_(std::move(task_runner)), + event_handler_(std::move(event_handler)), tick_clock_(base::DefaultTickClock::GetInstance()), last_ipc_send_time_(tick_clock_->NowTicks()), - ipc_send_pending_(false) { + ipc_send_pending_(false), + weak_factory_(this) { DCHECK(RenderThread::Get()) - << "RenderMediaLog must be constructed on the render thread"; + << "BatchingMediaLog must be constructed on the render thread"; // Pre-bind the WeakPtr on the right thread since we'll receive calls from // other threads and don't want races. weak_this_ = weak_factory_.GetWeakPtr(); } -RenderMediaLog::~RenderMediaLog() { +BatchingMediaLog::~BatchingMediaLog() { DCHECK(task_runner_->BelongsToCurrentThread()); // AddEvent() could be in-flight on some other thread. Wait for it, and make // sure that nobody else calls it. @@ -64,7 +66,7 @@ SendQueuedMediaEvents(); } -void RenderMediaLog::AddEventLocked( +void BatchingMediaLog::AddEventLocked( std::unique_ptr<media::MediaLogEvent> event) { Log(event.get()); @@ -109,7 +111,7 @@ if (delay_for_next_ipc_send > base::TimeDelta()) { task_runner_->PostDelayedTask( FROM_HERE, - base::BindOnce(&RenderMediaLog::SendQueuedMediaEvents, weak_this_), + base::BindOnce(&BatchingMediaLog::SendQueuedMediaEvents, weak_this_), delay_for_next_ipc_send); return; } @@ -121,10 +123,10 @@ } task_runner_->PostTask( FROM_HERE, - base::BindOnce(&RenderMediaLog::SendQueuedMediaEvents, weak_this_)); + base::BindOnce(&BatchingMediaLog::SendQueuedMediaEvents, weak_this_)); } -std::string RenderMediaLog::GetErrorMessageLocked() { +std::string BatchingMediaLog::GetErrorMessageLocked() { // Keep message structure in sync with // HTMLMediaElement::BuildElementErrorMessage(). std::stringstream result; @@ -143,13 +145,13 @@ return result.str(); } -void RenderMediaLog::RecordRapporWithSecurityOriginLocked( +void BatchingMediaLog::RecordRapporWithSecurityOriginLocked( const std::string& metric) { if (!task_runner_->BelongsToCurrentThread()) { // Note that we don't post back to *Locked. task_runner_->PostTask( FROM_HERE, - base::BindOnce(&RenderMediaLog::RecordRapporWithSecurityOrigin, + base::BindOnce(&BatchingMediaLog::RecordRapporWithSecurityOrigin, weak_this_, metric)); return; } @@ -157,7 +159,7 @@ GetContentClient()->renderer()->RecordRapporURL(metric, security_origin_); } -void RenderMediaLog::SendQueuedMediaEvents() { +void BatchingMediaLog::SendQueuedMediaEvents() { DCHECK(task_runner_->BelongsToCurrentThread()); std::vector<media::MediaLogEvent> events_to_send; @@ -178,18 +180,14 @@ if (events_to_send.empty()) return; - RenderThread::Get()->Send(new ViewHostMsg_MediaLogEvents(events_to_send)); + event_handler_->SendQueuedMediaEvents(std::move(events_to_send)); } -void RenderMediaLog::SetTickClockForTesting(const base::TickClock* tick_clock) { +void BatchingMediaLog::SetTickClockForTesting( + const base::TickClock* tick_clock) { base::AutoLock auto_lock(lock_); tick_clock_ = tick_clock; last_ipc_send_time_ = tick_clock_->NowTicks(); } -void RenderMediaLog::SetTaskRunnerForTesting( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { - task_runner_ = task_runner; -} - } // namespace content
diff --git a/content/renderer/media/render_media_log.h b/content/renderer/media/batching_media_log.h similarity index 69% rename from content/renderer/media/render_media_log.h rename to content/renderer/media/batching_media_log.h index 87d24161..6574934 100644 --- a/content/renderer/media/render_media_log.h +++ b/content/renderer/media/batching_media_log.h
@@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_RENDERER_MEDIA_RENDER_MEDIA_LOG_H_ -#define CONTENT_RENDERER_MEDIA_RENDER_MEDIA_LOG_H_ +#ifndef CONTENT_RENDERER_MEDIA_BATCHING_MEDIA_LOG_H_ +#define CONTENT_RENDERER_MEDIA_BATCHING_MEDIA_LOG_H_ #include <string> +#include <utility> #include <vector> #include "base/macros.h" @@ -22,26 +23,27 @@ namespace content { -// RenderMediaLog is an implementation of MediaLog that forwards events to the -// browser process, throttling as necessary. -// -// It also caches the last error events to support renderer-side reporting to -// entities like HTMLMediaElement and devtools console. -// -// To minimize the number of events sent over the wire, only the latest event -// added is sent for high frequency events (e.g., BUFFERED_EXTENTS_CHANGED). +// BatchingMediaLog is an implementation of MediaLog that sends messages +// grouped together in order to reduce IPC pressure. +// In order to subclass it, a subclass of the BatchingMediaLog::EventHandler +// should implement behavior when recieving a group of messages. // // It must be constructed on the render thread. -class CONTENT_EXPORT RenderMediaLog : public media::MediaLog { +class CONTENT_EXPORT BatchingMediaLog : public media::MediaLog { public: - RenderMediaLog(const GURL& security_origin, - scoped_refptr<base::SingleThreadTaskRunner> task_runner); - ~RenderMediaLog() override; + class EventHandler { + public: + virtual ~EventHandler() = default; + virtual void SendQueuedMediaEvents(std::vector<media::MediaLogEvent>) = 0; + }; + + BatchingMediaLog(const GURL& security_origin, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + std::unique_ptr<EventHandler> impl); + ~BatchingMediaLog() override; // Will reset |last_ipc_send_time_| with the value of NowTicks(). void SetTickClockForTesting(const base::TickClock* tick_clock); - void SetTaskRunnerForTesting( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); protected: // MediaLog implementation. @@ -59,6 +61,9 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + // impl for sending queued events. + std::unique_ptr<EventHandler> event_handler_; + // |lock_| protects access to all of the following member variables. It // allows any render process thread to AddEvent(), while preserving their // sequence for throttled send on |task_runner_| and coherent retrieval by @@ -86,12 +91,12 @@ // Holds a copy of the most recent PIPELINE_ERROR, if any. std::unique_ptr<media::MediaLogEvent> last_pipeline_error_; - base::WeakPtr<RenderMediaLog> weak_this_; - base::WeakPtrFactory<RenderMediaLog> weak_factory_{this}; + base::WeakPtr<BatchingMediaLog> weak_this_; + base::WeakPtrFactory<BatchingMediaLog> weak_factory_; - DISALLOW_COPY_AND_ASSIGN(RenderMediaLog); + DISALLOW_COPY_AND_ASSIGN(BatchingMediaLog); }; } // namespace content -#endif // CONTENT_RENDERER_MEDIA_RENDER_MEDIA_LOG_H_ +#endif // CONTENT_RENDERER_MEDIA_BATCHING_MEDIA_LOG_H_
diff --git a/content/renderer/media/render_media_log_unittest.cc b/content/renderer/media/batching_media_log_unittest.cc similarity index 67% rename from content/renderer/media/render_media_log_unittest.cc rename to content/renderer/media/batching_media_log_unittest.cc index ae362c89..2b36faa 100644 --- a/content/renderer/media/render_media_log_unittest.cc +++ b/content/renderer/media/batching_media_log_unittest.cc
@@ -2,33 +2,41 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <tuple> - +#include "content/renderer/media/batching_media_log.h" #include "base/macros.h" #include "base/test/scoped_task_environment.h" #include "base/test/simple_test_tick_clock.h" #include "base/test/test_mock_time_task_runner.h" #include "content/common/view_messages.h" #include "content/public/test/mock_render_thread.h" -#include "content/renderer/media/render_media_log.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" namespace content { -class RenderMediaLogTest : public testing::Test { +class BatchingMediaLogTest; + +class TestEventHandler : public BatchingMediaLog::EventHandler { public: - RenderMediaLogTest() - : log_(GURL("http://foo.com"), - blink::scheduler::GetSingleThreadTaskRunnerForTesting()), - task_runner_(new base::TestMockTimeTaskRunner()) { + explicit TestEventHandler(BatchingMediaLogTest* test_cls) + : test_cls_(test_cls) {} + void SendQueuedMediaEvents(std::vector<media::MediaLogEvent> events) override; + + private: + BatchingMediaLogTest* test_cls_; +}; + +class BatchingMediaLogTest : public testing::Test { + public: + BatchingMediaLogTest() + : task_runner_(new base::TestMockTimeTaskRunner()), + log_(GURL("http://foo.com"), + task_runner_, + std::make_unique<TestEventHandler>(this)) { log_.SetTickClockForTesting(&tick_clock_); - log_.SetTaskRunnerForTesting(task_runner_); } - ~RenderMediaLogTest() override { - task_runner_->ClearPendingTasks(); - } + ~BatchingMediaLogTest() override { task_runner_->ClearPendingTasks(); } void AddEvent(media::MediaLogEvent::Type type) { log_.AddEvent(log_.CreateEvent(type)); @@ -41,32 +49,36 @@ task_runner_->FastForwardBy(delta); } - int message_count() { return render_thread_.sink().message_count(); } + int message_count() { return add_events_count_; } std::vector<media::MediaLogEvent> GetMediaLogEvents() { - const IPC::Message* msg = render_thread_.sink().GetFirstMessageMatching( - ViewHostMsg_MediaLogEvents::ID); - if (!msg) { - ADD_FAILURE() << "Did not find ViewHostMsg_MediaLogEvents IPC message"; - return std::vector<media::MediaLogEvent>(); - } - - std::tuple<std::vector<media::MediaLogEvent>> events; - ViewHostMsg_MediaLogEvents::Read(msg, &events); - return std::get<0>(events); + std::vector<media::MediaLogEvent> return_events = std::move(events_); + return return_events; } private: + friend class TestEventHandler; + void AddEventsForTesting(std::vector<media::MediaLogEvent> events) { + events_.insert(events_.end(), events.begin(), events.end()); + add_events_count_++; + } + int add_events_count_ = 0; + std::vector<media::MediaLogEvent> events_; base::test::ScopedTaskEnvironment task_environment_; MockRenderThread render_thread_; base::SimpleTestTickClock tick_clock_; - RenderMediaLog log_; scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; + BatchingMediaLog log_; - DISALLOW_COPY_AND_ASSIGN(RenderMediaLogTest); + DISALLOW_COPY_AND_ASSIGN(BatchingMediaLogTest); }; -TEST_F(RenderMediaLogTest, ThrottleSendingEvents) { +void TestEventHandler::SendQueuedMediaEvents( + std::vector<media::MediaLogEvent> events) { + test_cls_->AddEventsForTesting(events); +} + +TEST_F(BatchingMediaLogTest, ThrottleSendingEvents) { AddEvent(media::MediaLogEvent::LOAD); EXPECT_EQ(0, message_count()); @@ -90,7 +102,7 @@ EXPECT_EQ(1, message_count()); } -TEST_F(RenderMediaLogTest, EventSentWithoutDelayAfterIpcInterval) { +TEST_F(BatchingMediaLogTest, EventSentWithoutDelayAfterIpcInterval) { AddEvent(media::MediaLogEvent::LOAD); Advance(base::TimeDelta::FromMilliseconds(1000)); EXPECT_EQ(1, message_count()); @@ -102,7 +114,7 @@ EXPECT_EQ(2, message_count()); } -TEST_F(RenderMediaLogTest, DurationChanged) { +TEST_F(BatchingMediaLogTest, DurationChanged) { AddEvent(media::MediaLogEvent::LOAD); AddEvent(media::MediaLogEvent::SEEK);
diff --git a/content/renderer/media/inspector_media_event_handler.cc b/content/renderer/media/inspector_media_event_handler.cc new file mode 100644 index 0000000..700146cd --- /dev/null +++ b/content/renderer/media/inspector_media_event_handler.cc
@@ -0,0 +1,72 @@ +// 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 "content/renderer/media/inspector_media_event_handler.h" + +#include <string> +#include <utility> + +#include "base/json/json_writer.h" + +namespace content { + +namespace { + +blink::WebString ToString(const base::Value& value) { + if (value.is_string()) { + return blink::WebString::FromUTF8(value.GetString()); + } + std::string output_str; + base::JSONWriter::Write(value, &output_str); + return blink::WebString::FromUTF8(output_str); +} + +} // namespace + +InspectorMediaEventHandler::InspectorMediaEventHandler( + blink::MediaInspectorContext* inspector_context) + : inspector_context_(inspector_context), + player_id_(inspector_context_->CreatePlayer()) {} + +// TODO(tmathmeyer) It would be wonderful if the definition for MediaLogEvent +// and InspectorPlayerEvent / InspectorPlayerProperty could be unified so that +// this method is no longer needed. Refactor MediaLogEvent at some point. +void InspectorMediaEventHandler::SendQueuedMediaEvents( + std::vector<media::MediaLogEvent> events_to_send) { + blink::InspectorPlayerEvents events; + blink::InspectorPlayerProperties properties; + + for (media::MediaLogEvent event : events_to_send) { + if (event.type == media::MediaLogEvent::PROPERTY_CHANGE) { + for (auto&& itr : event.params.DictItems()) { + blink::InspectorPlayerProperty prop = { + blink::WebString::FromUTF8(itr.first), ToString(itr.second)}; + properties.emplace_back(prop); + } + } else { + blink::InspectorPlayerEvent::InspectorPlayerEventType event_type = + blink::InspectorPlayerEvent::SYSTEM_EVENT; + + if (event.type == media::MediaLogEvent::MEDIA_ERROR_LOG_ENTRY || + event.type == media::MediaLogEvent::MEDIA_WARNING_LOG_ENTRY || + event.type == media::MediaLogEvent::MEDIA_INFO_LOG_ENTRY || + event.type == media::MediaLogEvent::MEDIA_DEBUG_LOG_ENTRY) { + event_type = blink::InspectorPlayerEvent::MESSAGE_EVENT; + } + for (auto&& itr : event.params.DictItems()) { + blink::InspectorPlayerEvent ev = {event_type, event.time, + blink::WebString::FromUTF8(itr.first), + ToString(itr.second)}; + events.emplace_back(ev); + } + } + } + if (!events.empty()) + inspector_context_->NotifyPlayerEvents(player_id_, events); + + if (!properties.empty()) + inspector_context_->SetPlayerProperties(player_id_, properties); +} + +} // namespace content
diff --git a/content/renderer/media/inspector_media_event_handler.h b/content/renderer/media/inspector_media_event_handler.h new file mode 100644 index 0000000..95b7cb7d6 --- /dev/null +++ b/content/renderer/media/inspector_media_event_handler.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 CONTENT_RENDERER_MEDIA_INSPECTOR_MEDIA_EVENT_HANDLER_H_ +#define CONTENT_RENDERER_MEDIA_INSPECTOR_MEDIA_EVENT_HANDLER_H_ + +#include <vector> + +#include "content/renderer/media/batching_media_log.h" +#include "third_party/blink/public/web/web_media_inspector.h" + +namespace content { + +// InspectorMediaEventHandler is an implementation of +// BatchingMediaLog::EventHandler that reformats events and sends them to +// the devtools inspector. +class CONTENT_EXPORT InspectorMediaEventHandler + : public BatchingMediaLog::EventHandler { + public: + explicit InspectorMediaEventHandler(blink::MediaInspectorContext*); + ~InspectorMediaEventHandler() override = default; + void SendQueuedMediaEvents(std::vector<media::MediaLogEvent>) override; + + private: + blink::MediaInspectorContext* inspector_context_; + blink::WebString player_id_; +}; + +} // namespace content + +#endif // CONTENT_RENDERER_MEDIA_INSPECTOR_MEDIA_EVENT_HANDLER_H_
diff --git a/content/renderer/media/inspector_media_event_handler_unittest.cc b/content/renderer/media/inspector_media_event_handler_unittest.cc new file mode 100644 index 0000000..2564f99 --- /dev/null +++ b/content/renderer/media/inspector_media_event_handler_unittest.cc
@@ -0,0 +1,206 @@ +// 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 <string> +#include <utility> + +#include "content/renderer/media/inspector_media_event_handler.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/web/web_media_inspector.h" + +using ::testing::_; + +namespace content { + +class MockMediaInspectorContext : public blink::MediaInspectorContext { + public: + MockMediaInspectorContext() = default; + virtual ~MockMediaInspectorContext() = default; + + blink::WebString CreatePlayer() override { return "TestPlayer"; } + + void NotifyPlayerEvents(blink::WebString id, + blink::InspectorPlayerEvents events) override { + MockNotifyPlayerEvents(events); + } + + void SetPlayerProperties(blink::WebString id, + blink::InspectorPlayerProperties props) override { + MockSetPlayerProperties(props); + } + + MOCK_METHOD1(MockNotifyPlayerEvents, void(blink::InspectorPlayerEvents)); + MOCK_METHOD1(MockSetPlayerProperties, void(blink::InspectorPlayerProperties)); +}; + +class InspectorMediaEventHandlerTest : public testing::Test { + public: + InspectorMediaEventHandlerTest() { + mock_context_ = std::make_unique<MockMediaInspectorContext>(); + handler_ = + std::make_unique<InspectorMediaEventHandler>(mock_context_.get()); + } + + protected: + std::unique_ptr<InspectorMediaEventHandler> handler_; + std::unique_ptr<MockMediaInspectorContext> mock_context_; + + media::MediaLogEvent CreateEvent(media::MediaLogEvent::Type type) { + media::MediaLogEvent event; + event.id = 0; + event.type = type; + event.time = base::TimeTicks(); + return event; + } + + media::MediaLogEvent CreatePropChangeEvent( + std::vector<std::pair<std::string, std::string>> props) { + auto event = CreateEvent(media::MediaLogEvent::PROPERTY_CHANGE); + for (auto p : props) { + event.params.SetString(std::get<0>(p), std::get<1>(p)); + } + return event; + } + + media::MediaLogEvent CreateLogEvent(std::string msg) { + auto event = CreateEvent(media::MediaLogEvent::MEDIA_WARNING_LOG_ENTRY); + event.params.SetString("warning", msg); + return event; + } + + DISALLOW_COPY_AND_ASSIGN(InspectorMediaEventHandlerTest); +}; + +bool operator==(const blink::InspectorPlayerProperty& lhs, + const blink::InspectorPlayerProperty& rhs) { + return lhs.name == rhs.name && lhs.value == rhs.value; +} + +bool operator!=(const blink::InspectorPlayerProperty& lhs, + const blink::InspectorPlayerProperty& rhs) { + return !(lhs == rhs); +} + +bool operator==(const blink::InspectorPlayerEvent& lhs, + const blink::InspectorPlayerEvent& rhs) { + return lhs.type == rhs.type && lhs.timestamp == rhs.timestamp && + lhs.key == rhs.key && lhs.value == rhs.value; +} + +bool operator!=(const blink::InspectorPlayerEvent& lhs, + const blink::InspectorPlayerEvent& rhs) { + return !(lhs == rhs); +} + +MATCHER_P(PropertiesEqualTo, props, "") { + if (props.size() != arg.size()) + return false; + for (size_t i = 0; i < props.size(); i++) + if (props[i] != arg[i]) + return false; + return true; +} + +MATCHER_P(EventsEqualTo, events, "") { + if (events.size() != arg.size()) + return false; + for (size_t i = 0; i < events.size(); i++) + if (events[i] != arg[i]) + return false; + return true; +} + +TEST_F(InspectorMediaEventHandlerTest, ConvertsProperties) { + std::vector<media::MediaLogEvent> events = { + CreatePropChangeEvent({{"test_key", "test_value"}})}; + + blink::InspectorPlayerProperties expected; + blink::InspectorPlayerProperty prop = { + blink::WebString::FromUTF8("test_key"), + blink::WebString::FromUTF8("test_value")}; + expected.emplace_back(prop); + EXPECT_CALL(*mock_context_, + MockSetPlayerProperties(PropertiesEqualTo(expected))) + .Times(1); + EXPECT_CALL(*mock_context_, MockNotifyPlayerEvents(_)).Times(0); + + handler_->SendQueuedMediaEvents(events); +} + +TEST_F(InspectorMediaEventHandlerTest, SplitsDoubleProperties) { + std::vector<media::MediaLogEvent> events = { + CreatePropChangeEvent({{"test_key", "test_value"}, {"foo", "bar"}})}; + + blink::InspectorPlayerProperties expected; + blink::InspectorPlayerProperty prop_test = { + blink::WebString::FromUTF8("test_key"), + blink::WebString::FromUTF8("test_value")}; + blink::InspectorPlayerProperty prop_foo = {blink::WebString::FromUTF8("foo"), + blink::WebString::FromUTF8("bar")}; + expected.emplace_back(prop_foo); + expected.emplace_back(prop_test); + EXPECT_CALL(*mock_context_, + MockSetPlayerProperties(PropertiesEqualTo(expected))) + .Times(1); + EXPECT_CALL(*mock_context_, MockNotifyPlayerEvents(_)).Times(0); + + handler_->SendQueuedMediaEvents(events); +} + +TEST_F(InspectorMediaEventHandlerTest, ConvertsMessageEvent) { + std::vector<media::MediaLogEvent> events = { + CreateLogEvent("Has Anyone Really Been Far Even as Decided to Use Even " + "Go Want to do Look More Like?")}; + + blink::InspectorPlayerEvents expected; + blink::InspectorPlayerEvent e = { + blink::InspectorPlayerEvent::MESSAGE_EVENT, base::TimeTicks(), + blink::WebString::FromUTF8("warning"), + blink::WebString::FromUTF8("Has Anyone Really Been Far Even as Decided " + "to Use Even Go Want to do Look More Like?")}; + expected.emplace_back(e); + + EXPECT_CALL(*mock_context_, MockSetPlayerProperties(_)).Times(0); + EXPECT_CALL(*mock_context_, MockNotifyPlayerEvents(EventsEqualTo(expected))) + .Times(1); + + handler_->SendQueuedMediaEvents(events); +} + +TEST_F(InspectorMediaEventHandlerTest, ConvertsEventsAndProperties) { + std::vector<media::MediaLogEvent> events = { + CreateLogEvent("100% medically accurate"), + CreatePropChangeEvent( + {{"free_puppies", "all_taken"}, {"illuminati", "confirmed"}})}; + + blink::InspectorPlayerEvents expected_events; + blink::InspectorPlayerEvent e = { + blink::InspectorPlayerEvent::MESSAGE_EVENT, base::TimeTicks(), + blink::WebString::FromUTF8("warning"), + blink::WebString::FromUTF8("100% medically accurate")}; + expected_events.emplace_back(e); + + blink::InspectorPlayerProperties expected_properties; + blink::InspectorPlayerProperty puppies = { + blink::WebString::FromUTF8("free_puppies"), + blink::WebString::FromUTF8("all_taken")}; + blink::InspectorPlayerProperty illumanati = { + blink::WebString::FromUTF8("illuminati"), + blink::WebString::FromUTF8("confirmed")}; + expected_properties.emplace_back(puppies); + expected_properties.emplace_back(illumanati); + + EXPECT_CALL(*mock_context_, + MockSetPlayerProperties(PropertiesEqualTo(expected_properties))) + .Times(1); + EXPECT_CALL(*mock_context_, + MockNotifyPlayerEvents(EventsEqualTo(expected_events))) + .Times(1); + + handler_->SendQueuedMediaEvents(events); +} + +} // namespace content
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc index 615b3b7..69b8d78 100644 --- a/content/renderer/media/media_factory.cc +++ b/content/renderer/media/media_factory.cc
@@ -19,7 +19,9 @@ #include "content/public/common/content_client.h" #include "content/public/renderer/content_renderer_client.h" #include "content/renderer/media/audio/audio_device_factory.h" -#include "content/renderer/media/render_media_log.h" +#include "content/renderer/media/batching_media_log.h" +#include "content/renderer/media/inspector_media_event_handler.h" +#include "content/renderer/media/render_media_event_handler.h" #include "content/renderer/media/renderer_webmediaplayer_delegate.h" #include "content/renderer/render_frame_impl.h" #include "content/renderer/render_thread_impl.h" @@ -267,6 +269,7 @@ blink::WebMediaPlayer* MediaFactory::CreateMediaPlayer( const blink::WebMediaPlayerSource& source, blink::WebMediaPlayerClient* client, + blink::MediaInspectorContext* inspector_context, blink::WebMediaPlayerEncryptedMediaClient* encrypted_client, blink::WebContentDecryptionModule* initial_cdm, const blink::WebString& sink_id, @@ -279,7 +282,8 @@ blink::GetWebMediaStreamFromWebMediaPlayerSource(source); if (!web_stream.IsNull()) return CreateWebMediaPlayerForMediaStream( - client, sink_id, security_origin, web_frame, layer_tree_view, settings); + client, inspector_context, sink_id, security_origin, web_frame, + layer_tree_view, settings); // If |source| was not a MediaStream, it must be a URL. // TODO(guidou): Fix this when support for other srcObject types is added. @@ -317,11 +321,20 @@ media::kMemoryPressureBasedSourceBufferGC, "enable_instant_source_buffer_gc", false); + std::unique_ptr<BatchingMediaLog::EventHandler> event_handler; + if (base::FeatureList::IsEnabled(media::kMediaInspectorLogging)) { + event_handler = + std::make_unique<InspectorMediaEventHandler>(inspector_context); + } else { + event_handler = std::make_unique<RenderMediaEventHandler>(); + } + // This must be created for every new WebMediaPlayer, each instance generates // a new player id which is used to collate logs on the browser side. - std::unique_ptr<media::MediaLog> media_log(new RenderMediaLog( + auto media_log = std::make_unique<BatchingMediaLog>( url::Origin(security_origin).GetURL(), - render_frame_->GetTaskRunner(blink::TaskType::kInternalMedia))); + render_frame_->GetTaskRunner(blink::TaskType::kInternalMedia), + std::move(event_handler)); base::WeakPtr<media::MediaObserver> media_observer; @@ -535,6 +548,7 @@ blink::WebMediaPlayer* MediaFactory::CreateWebMediaPlayerForMediaStream( blink::WebMediaPlayerClient* client, + blink::MediaInspectorContext* inspector_context, const blink::WebString& sink_id, const blink::WebSecurityOrigin& security_origin, blink::WebLocalFrame* frame, @@ -548,11 +562,24 @@ CreateSubmitter(&video_frame_compositor_task_runner, settings); DCHECK(layer_tree_view); + + std::unique_ptr<BatchingMediaLog::EventHandler> event_handler; + if (base::FeatureList::IsEnabled(media::kMediaInspectorLogging)) { + event_handler = + std::make_unique<InspectorMediaEventHandler>(inspector_context); + } else { + event_handler = std::make_unique<RenderMediaEventHandler>(); + } + + // This must be created for every new WebMediaPlayer, each instance generates + // a new player id which is used to collate logs on the browser side. + auto media_log = std::make_unique<BatchingMediaLog>( + url::Origin(security_origin).GetURL(), + render_frame_->GetTaskRunner(blink::TaskType::kInternalMedia), + std::move(event_handler)); + return new blink::WebMediaPlayerMS( - frame, client, GetWebMediaPlayerDelegate(), - std::make_unique<RenderMediaLog>( - url::Origin(security_origin).GetURL(), - render_frame_->GetTaskRunner(blink::TaskType::kInternalMedia)), + frame, client, GetWebMediaPlayerDelegate(), std::move(media_log), CreateMediaStreamRendererFactory(), render_frame_->GetTaskRunner(blink::TaskType::kInternalMedia), render_thread->GetIOTaskRunner(), video_frame_compositor_task_runner,
diff --git a/content/renderer/media/media_factory.h b/content/renderer/media/media_factory.h index b746680d..946c034 100644 --- a/content/renderer/media/media_factory.h +++ b/content/renderer/media/media_factory.h
@@ -20,6 +20,7 @@ #include "third_party/blink/public/platform/web_security_origin.h" #include "third_party/blink/public/platform/web_set_sink_id_callbacks.h" #include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/public/web/web_media_inspector.h" #if BUILDFLAG(ENABLE_MOJO_MEDIA) #include "media/mojo/mojom/interface_factory.mojom.h" // nogncheck @@ -104,6 +105,7 @@ blink::WebMediaPlayer* CreateMediaPlayer( const blink::WebMediaPlayerSource& source, blink::WebMediaPlayerClient* client, + blink::MediaInspectorContext* inspector_context, blink::WebMediaPlayerEncryptedMediaClient* encrypted_client, blink::WebContentDecryptionModule* initial_cdm, const blink::WebString& sink_id, @@ -126,6 +128,7 @@ blink::WebMediaPlayer* CreateWebMediaPlayerForMediaStream( blink::WebMediaPlayerClient* client, + blink::MediaInspectorContext* inspector_context, const blink::WebString& sink_id, const blink::WebSecurityOrigin& security_origin, blink::WebLocalFrame* frame,
diff --git a/content/renderer/media/render_media_event_handler.cc b/content/renderer/media/render_media_event_handler.cc new file mode 100644 index 0000000..ff3c9de --- /dev/null +++ b/content/renderer/media/render_media_event_handler.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 "content/renderer/media/render_media_event_handler.h" +#include "content/common/view_messages.h" +#include "content/public/renderer/render_thread.h" + +namespace content { + +void RenderMediaEventHandler::SendQueuedMediaEvents( + std::vector<media::MediaLogEvent> events_to_send) { + RenderThread::Get()->Send(new ViewHostMsg_MediaLogEvents(events_to_send)); +} + +} // namespace content
diff --git a/content/renderer/media/render_media_event_handler.h b/content/renderer/media/render_media_event_handler.h new file mode 100644 index 0000000..b95af32 --- /dev/null +++ b/content/renderer/media/render_media_event_handler.h
@@ -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. + +#ifndef CONTENT_RENDERER_MEDIA_RENDER_MEDIA_EVENT_HANDLER_H_ +#define CONTENT_RENDERER_MEDIA_RENDER_MEDIA_EVENT_HANDLER_H_ + +#include <vector> + +#include "content/renderer/media/batching_media_log.h" + +namespace content { + +// RenderMediaEventHandler is an implementation of +// BatchingMediaLog::EventHandler that forwards events to the browser process. +class CONTENT_EXPORT RenderMediaEventHandler + : public BatchingMediaLog::EventHandler { + public: + RenderMediaEventHandler() = default; + ~RenderMediaEventHandler() override = default; + void SendQueuedMediaEvents(std::vector<media::MediaLogEvent>) override; +}; + +} // namespace content + +#endif // CONTENT_RENDERER_MEDIA_RENDER_MEDIA_EVENT_HANDLER_H_
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc index 8c25a0d8..12ca71c 100644 --- a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc +++ b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
@@ -23,7 +23,6 @@ #include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" -#include "content/renderer/media/render_media_log.h" #include "media/base/media_log.h" #include "media/base/media_switches.h" #include "media/base/media_util.h"
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 047532d..41ecfe8 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -4172,15 +4172,16 @@ blink::WebMediaPlayer* RenderFrameImpl::CreateMediaPlayer( const blink::WebMediaPlayerSource& source, WebMediaPlayerClient* client, + blink::MediaInspectorContext* inspector_context, WebMediaPlayerEncryptedMediaClient* encrypted_client, WebContentDecryptionModule* initial_cdm, const blink::WebString& sink_id, blink::WebLayerTreeView* layer_tree_view) { const cc::LayerTreeSettings& settings = GetLocalRootRenderWidget()->layer_tree_view()->GetLayerTreeSettings(); - return media_factory_.CreateMediaPlayer(source, client, encrypted_client, - initial_cdm, sink_id, layer_tree_view, - settings); + return media_factory_.CreateMediaPlayer(source, client, inspector_context, + encrypted_client, initial_cdm, + sink_id, layer_tree_view, settings); } std::unique_ptr<blink::WebContentSettingsClient>
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index b331bbaa..48a2648 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h
@@ -675,6 +675,7 @@ blink::WebMediaPlayer* CreateMediaPlayer( const blink::WebMediaPlayerSource& source, blink::WebMediaPlayerClient* client, + blink::MediaInspectorContext* inspector_context, blink::WebMediaPlayerEncryptedMediaClient* encrypted_client, blink::WebContentDecryptionModule* initial_cdm, const blink::WebString& sink_id,
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 5cb02a6..1d95e306 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -15,6 +15,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" +#include "base/debug/crash_logging.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" @@ -275,6 +276,35 @@ histogram->Add(sample); } +void AddCrashKey(v8::CrashKeyId id, const std::string& value) { + namespace bd = base::debug; + switch (id) { + case v8::CrashKeyId::kIsolateAddress: + static bd::CrashKeyString* isolate_address = bd::AllocateCrashKeyString( + "v8_isolate_address", bd::CrashKeySize::Size32); + bd::SetCrashKeyString(isolate_address, value); + break; + case v8::CrashKeyId::kReadonlySpaceFirstPageAddress: + static bd::CrashKeyString* ro_space_firstpage_address = + bd::AllocateCrashKeyString("v8_ro_space_firstpage_address", + bd::CrashKeySize::Size32); + bd::SetCrashKeyString(ro_space_firstpage_address, value); + break; + case v8::CrashKeyId::kMapSpaceFirstPageAddress: + static bd::CrashKeyString* map_space_firstpage_address = + bd::AllocateCrashKeyString("v8_map_space_firstpage_address", + bd::CrashKeySize::Size32); + bd::SetCrashKeyString(map_space_firstpage_address, value); + break; + case v8::CrashKeyId::kCodeSpaceFirstPageAddress: + static bd::CrashKeyString* code_space_firstpage_address = + bd::AllocateCrashKeyString("v8_code_space_firstpage_address", + bd::CrashKeySize::Size32); + bd::SetCrashKeyString(code_space_firstpage_address, value); + break; + } +} + class FrameFactoryImpl : public mojom::FrameFactory { public: explicit FrameFactoryImpl(const service_manager::BindSourceInfo& source_info) @@ -1174,6 +1204,7 @@ v8::Isolate* isolate = blink::MainThreadIsolate(); isolate->SetCreateHistogramFunction(CreateHistogram); isolate->SetAddHistogramSampleFunction(AddHistogramSample); + isolate->SetAddCrashKeyCallback(AddCrashKey); main_thread_compositor_task_runner_ = main_thread_scheduler_->CompositorTaskRunner();
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc index b1016c7..061b18a 100644 --- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc +++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -63,6 +63,10 @@ service_manager::mojom::InterfaceProviderPtrInfo interface_provider = std::move(params->provider_info->interface_provider); + mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> + browser_interface_broker = + std::move(params->provider_info->browser_interface_broker); + service_worker_context_client_ = std::make_unique<ServiceWorkerContextClient>( params->service_worker_version_id, params->scope, params->script_url, !params->installed_scripts_info.is_null(), @@ -104,7 +108,7 @@ service_worker_context_client_.get(), std::move(installed_scripts_manager_params), params->content_settings_proxy.PassHandle(), cache_storage.PassHandle(), - interface_provider.PassHandle()); + interface_provider.PassHandle(), browser_interface_broker.PassPipe()); service_worker_context_client_->StartWorkerContextOnInitiatorThread( std::move(worker), start_data); }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index ab929bad..3e7c6a4 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1917,7 +1917,8 @@ "../renderer/media/audio/mock_audio_device_factory.h", "../renderer/media/audio/mojo_audio_input_ipc_unittest.cc", "../renderer/media/audio/mojo_audio_output_ipc_unittest.cc", - "../renderer/media/render_media_log_unittest.cc", + "../renderer/media/batching_media_log_unittest.cc", + "../renderer/media/inspector_media_event_handler_unittest.cc", "../renderer/media/renderer_webaudiodevice_impl_unittest.cc", "../renderer/media/webrtc/fake_rtc_rtp_transceiver.cc", "../renderer/media/webrtc/fake_rtc_rtp_transceiver.h",
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py index 167d2ba5..dbd07d3 100644 --- a/content/test/gpu/gpu_tests/gpu_integration_test.py +++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -157,9 +157,13 @@ @classmethod def _RestartBrowser(cls, reason): logging.warning('Restarting browser due to '+ reason) - cls.StopBrowser() - cls.SetBrowserOptions(cls._finder_options) - cls.StartBrowser() + if cls.browser is None: + cls.SetBrowserOptions(cls._original_finder_options) + cls.StartBrowser() + else: + cls.StopBrowser() + cls.SetBrowserOptions(cls._finder_options) + cls.StartBrowser() def _RunGpuTest(self, url, test_name, *args): expected_results, should_retry_on_failure = ( @@ -323,6 +327,14 @@ @classmethod def _EnsureTabIsAvailable(cls): try: + # If there is no browser, the previous run may have failed an additional + # time, while trying to recover from an initial failure. + # ChromeBrowserBackend._GetDevToolsClient can cause this if there is a + # crash during browser startup. If this has occurred, reset the options, + # and attempt to bring up a browser for this test. Otherwise failures + # begin to cascade between tests. https://crbug.com/993379 + if cls.browser is None: + cls._RestartBrowser('failure in previous shutdown') cls.tab = cls.browser.tabs[0] except Exception: # restart the browser to make sure a failure in a test doesn't
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn index 02009fbb..a22ed5c 100644 --- a/extensions/BUILD.gn +++ b/extensions/BUILD.gn
@@ -121,6 +121,8 @@ "test/logging_timer.h", "test/result_catcher.cc", "test/result_catcher.h", + "test/test_background_page_first_load_observer.cc", + "test/test_background_page_first_load_observer.h", "test/test_content_utility_client.cc", "test/test_content_utility_client.h", "test/test_extension_dir.cc",
diff --git a/extensions/browser/api/declarative_net_request/BUILD.gn b/extensions/browser/api/declarative_net_request/BUILD.gn index 9d3188e3..ffdd5ce2 100644 --- a/extensions/browser/api/declarative_net_request/BUILD.gn +++ b/extensions/browser/api/declarative_net_request/BUILD.gn
@@ -4,6 +4,8 @@ source_set("declarative_net_request") { sources = [ + "action_tracker.cc", + "action_tracker.h", "composite_matcher.cc", "composite_matcher.h", "constants.cc",
diff --git a/extensions/browser/api/declarative_net_request/action_tracker.cc b/extensions/browser/api/declarative_net_request/action_tracker.cc new file mode 100644 index 0000000..66de566 --- /dev/null +++ b/extensions/browser/api/declarative_net_request/action_tracker.cc
@@ -0,0 +1,79 @@ +// 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 "extensions/browser/api/declarative_net_request/action_tracker.h" + +#include "base/stl_util.h" +#include "extensions/browser/api/declarative_net_request/rules_monitor_service.h" +#include "extensions/browser/api/extensions_api_client.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/common/constants.h" + +namespace extensions { +namespace declarative_net_request { + +ActionTracker::ActionTracker(content::BrowserContext* browser_context) + : browser_context_(browser_context) { + extension_prefs_ = ExtensionPrefs::Get(browser_context_); +} + +ActionTracker::~ActionTracker() { + DCHECK(actions_matched_.empty()); +} + +void ActionTracker::OnRuleMatched(const std::vector<ExtensionId>& extension_ids, + int tab_id) { + if (tab_id == extension_misc::kUnknownTabId) + return; + + for (const auto& extension_id : extension_ids) { + auto key = std::make_pair(extension_id, tab_id); + int action_count = ++actions_matched_[key]; + + if (extension_prefs_->GetDNRUseActionCountAsBadgeText(extension_id)) { + DCHECK(ExtensionsAPIClient::Get()); + ExtensionsAPIClient::Get()->UpdateActionCount( + browser_context_, extension_id, tab_id, action_count); + } + } +} + +void ActionTracker::ClearExtensionData(const ExtensionId& extension_id) { + auto compare_by_extension_id = + [&extension_id](const std::pair<ExtensionTabKey, size_t>& it) { + return it.first.first == extension_id; + }; + + base::EraseIf(actions_matched_, compare_by_extension_id); +} + +void ActionTracker::ClearTabData(int tab_id) { + auto compare_by_tab_id = + [&tab_id](const std::pair<ExtensionTabKey, size_t>& it) { + return it.first.second == tab_id; + }; + + base::EraseIf(actions_matched_, compare_by_tab_id); +} + +void ActionTracker::ResetActionCountForTab(int tab_id) { + RulesMonitorService* rules_monitor_service = + RulesMonitorService::Get(browser_context_); + + DCHECK(rules_monitor_service); + for (const auto& extension_id : + rules_monitor_service->extensions_with_rulesets()) { + auto key = std::make_pair(extension_id, tab_id); + actions_matched_[key] = 0; + + if (extension_prefs_->GetDNRUseActionCountAsBadgeText(extension_id)) { + DCHECK(ExtensionsAPIClient::Get()); + ExtensionsAPIClient::Get()->UpdateActionCount( + browser_context_, extension_id, tab_id, 0 /* action_count */); + } + } +} + +} // namespace declarative_net_request +} // namespace extensions
diff --git a/extensions/browser/api/declarative_net_request/action_tracker.h b/extensions/browser/api/declarative_net_request/action_tracker.h new file mode 100644 index 0000000..76255a27 --- /dev/null +++ b/extensions/browser/api/declarative_net_request/action_tracker.h
@@ -0,0 +1,63 @@ +// 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 EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_ACTION_TRACKER_H_ +#define EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_ACTION_TRACKER_H_ + +#include <map> +#include <vector> + +#include "base/macros.h" +#include "extensions/common/extension_id.h" + +namespace content { +class BrowserContext; +} + +namespace extensions { + +class ExtensionPrefs; + +namespace declarative_net_request { + +class ActionTracker { + public: + explicit ActionTracker(content::BrowserContext* browser_context); + ~ActionTracker(); + + // Called whenever a request matches with a rule. + void OnRuleMatched(const std::vector<ExtensionId>& extension_ids, int tab_id); + + // Clears the action count for the specified |extension_id| for all tabs. + // Called when an extension's ruleset is removed. + void ClearExtensionData(const ExtensionId& extension_id); + + // Clears the action count for every extension for the specified |tab_id|. + // Called when the tab has been closed. + void ClearTabData(int tab_id); + + // Sets the action count for every extension for the specified |tab_id| to 0 + // and notifies the extension action to set the badge text to 0 for that tab. + // Called when the a main-frame navigation to a different document finishes on + // the tab. + void ResetActionCountForTab(int tab_id); + + private: + using ExtensionTabKey = std::pair<ExtensionId, int>; + + // Maps a pair of (extension ID, tab ID) to the number of actions matched for + // the extension and tab specified. + std::map<ExtensionTabKey, int> actions_matched_; + + content::BrowserContext* browser_context_; + + ExtensionPrefs* extension_prefs_; + + DISALLOW_COPY_AND_ASSIGN(ActionTracker); +}; + +} // namespace declarative_net_request +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_ACTION_TRACKER_H_
diff --git a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc index 6d26296..82b0d93 100644 --- a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc +++ b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc
@@ -330,6 +330,11 @@ ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context()); prefs->SetDNRUseActionCountAsBadgeText(extension_id(), params->enable); + + // TODO(crbug.com/979068): If the preference is switched on, update the + // extension's badge text with the number of actions matched for this + // extension. Otherwise, clear the badge text for the extension's icon and + // show the default badge text. return RespondNow(NoArguments()); }
diff --git a/extensions/browser/api/declarative_net_request/rules_monitor_service.h b/extensions/browser/api/declarative_net_request/rules_monitor_service.h index 54c4c2a..1349a41 100644 --- a/extensions/browser/api/declarative_net_request/rules_monitor_service.h +++ b/extensions/browser/api/declarative_net_request/rules_monitor_service.h
@@ -61,6 +61,10 @@ // the given |extension_id|. bool HasRegisteredRuleset(const ExtensionId& extension_id) const; + const std::set<ExtensionId>& extensions_with_rulesets() const { + return extensions_with_rulesets_; + } + // Updates the dynamic rules for the |extension| and then invokes // |callback| with an optional error. using DynamicRuleUpdateUICallback =
diff --git a/extensions/browser/api/declarative_net_request/ruleset_manager.cc b/extensions/browser/api/declarative_net_request/ruleset_manager.cc index 1f0ebc38..a8f1ebd 100644 --- a/extensions/browser/api/declarative_net_request/ruleset_manager.cc +++ b/extensions/browser/api/declarative_net_request/ruleset_manager.cc
@@ -223,7 +223,8 @@ RulesetManager::RulesetManager(content::BrowserContext* browser_context) : browser_context_(browser_context), prefs_(ExtensionPrefs::Get(browser_context)), - permission_helper_(PermissionHelper::Get(browser_context)) { + permission_helper_(PermissionHelper::Get(browser_context)), + action_tracker_(browser_context) { DCHECK(browser_context_); // RulesetManager can be created on any sequence. @@ -268,6 +269,7 @@ << "RemoveRuleset called without a corresponding AddRuleset for " << extension_id; + action_tracker_.ClearExtensionData(extension_id); base::EraseIf(rulesets_, compare_by_id); if (test_observer_) @@ -399,9 +401,11 @@ const RequestParams& params) const { for (const ExtensionRulesetData* ruleset : rulesets) { if (ruleset->matcher->ShouldBlockRequest(params)) { - return ShouldCollapseResourceType(params.element_type) - ? Action(Action::Type::COLLAPSE) - : Action(Action::Type::BLOCK); + Action action = ShouldCollapseResourceType(params.element_type) + ? Action(Action::Type::COLLAPSE) + : Action(Action::Type::BLOCK); + action.extension_ids.push_back(ruleset->extension_id); + return action; } } return base::nullopt; @@ -447,6 +451,7 @@ Action action(Action::Type::REDIRECT); action.redirect_url = std::move(redirect_action.redirect_url); + action.extension_ids.push_back(ruleset->extension_id); return action; } @@ -456,14 +461,19 @@ base::Optional<RulesetManager::Action> RulesetManager::GetRemoveHeadersAction( const std::vector<const ExtensionRulesetData*>& rulesets, const RequestParams& params) const { + Action action(Action::Type::REMOVE_HEADERS); uint8_t mask = 0; - for (const ExtensionRulesetData* ruleset : rulesets) - mask |= ruleset->matcher->GetRemoveHeadersMask(params, mask); + for (const ExtensionRulesetData* ruleset : rulesets) { + uint8_t ruleset_mask = ruleset->matcher->GetRemoveHeadersMask(params, mask); + if (ruleset_mask) + action.extension_ids.push_back(ruleset->extension_id); + + mask |= ruleset_mask; + } if (!mask) return base::nullopt; - Action action(Action::Type::REMOVE_HEADERS); PopulateHeadersFromMask(mask, &action.request_headers_to_remove, &action.response_headers_to_remove); return action;
diff --git a/extensions/browser/api/declarative_net_request/ruleset_manager.h b/extensions/browser/api/declarative_net_request/ruleset_manager.h index 652fe90..7f7bef6 100644 --- a/extensions/browser/api/declarative_net_request/ruleset_manager.h +++ b/extensions/browser/api/declarative_net_request/ruleset_manager.h
@@ -14,6 +14,7 @@ #include "base/optional.h" #include "base/sequence_checker.h" #include "base/time/time.h" +#include "extensions/browser/api/declarative_net_request/action_tracker.h" #include "extensions/browser/api/declarative_net_request/utils.h" #include "extensions/common/extension_id.h" #include "extensions/common/permissions/permissions_data.h" @@ -61,6 +62,11 @@ // Valid iff |type| is |REDIRECT|. base::Optional<GURL> redirect_url; + // The ids of the extensions the action is attributed to. + // TODO(crbug.com/991420): This is not exactly correct for attributing + // an Action to the extension(s) for |REMOVE_HEADERS| rules. + std::vector<ExtensionId> extension_ids; + // Valid iff |type| is |REMOVE_HEADERS|. The vectors point to strings of // static storage duration. std::vector<const char*> request_headers_to_remove; @@ -124,6 +130,9 @@ // Sets the TestObserver. Client maintains ownership of |observer|. void SetObserverForTest(TestObserver* observer); + const ActionTracker& action_tracker() const { return action_tracker_; } + ActionTracker& action_tracker() { return action_tracker_; } + private: struct ExtensionRulesetData { ExtensionRulesetData(const ExtensionId& extension_id, @@ -187,6 +196,10 @@ // Non-owning pointer to TestObserver. TestObserver* test_observer_ = nullptr; + // Mutable because this is updated in multiple const methods where we create + // and return the appropriate action based on the rule matched. + mutable ActionTracker action_tracker_; + SEQUENCE_CHECKER(sequence_checker_); DISALLOW_COPY_AND_ASSIGN(RulesetManager);
diff --git a/extensions/browser/api/extensions_api_client.cc b/extensions/browser/api/extensions_api_client.cc index bac64cb..d5b265f 100644 --- a/extensions/browser/api/extensions_api_client.cc +++ b/extensions/browser/api/extensions_api_client.cc
@@ -54,6 +54,11 @@ int render_frame_id, const ExtensionId& extension_id) {} +void ExtensionsAPIClient::UpdateActionCount(content::BrowserContext* context, + const ExtensionId& extension_id, + int tab_id, + int action_count) {} + AppViewGuestDelegate* ExtensionsAPIClient::CreateAppViewGuestDelegate() const { return NULL; }
diff --git a/extensions/browser/api/extensions_api_client.h b/extensions/browser/api/extensions_api_client.h index 1b6021c..04673c64 100644 --- a/extensions/browser/api/extensions_api_client.h +++ b/extensions/browser/api/extensions_api_client.h
@@ -109,6 +109,12 @@ int render_frame_id, const ExtensionId& extension_id); + // Updates an extension's matched action count stored in an ExtensionAction. + virtual void UpdateActionCount(content::BrowserContext* context, + const ExtensionId& extension_id, + int tab_id, + int action_count); + // Creates the AppViewGuestDelegate. virtual AppViewGuestDelegate* CreateAppViewGuestDelegate() const;
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc index 1e0def6..eeaff91 100644 --- a/extensions/browser/api/web_request/web_request_api.cc +++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -68,6 +68,7 @@ #include "extensions/browser/warning_service.h" #include "extensions/browser/warning_set.h" #include "extensions/common/api/web_request.h" +#include "extensions/common/constants.h" #include "extensions/common/error_utils.h" #include "extensions/common/event_filtering_info.h" #include "extensions/common/extension.h" @@ -476,6 +477,19 @@ return *headers_filtered ? result : response_headers; } +// Helper to record a matched DNR action in RulesetManager's ActionTracker. +void OnDNRActionMatched(content::BrowserContext* browser_context, + const WebRequestInfo& request) { + DCHECK(request.dnr_action.has_value()); + + declarative_net_request::ActionTracker& action_tracker = + declarative_net_request::RulesMonitorService::Get(browser_context) + ->ruleset_manager() + ->action_tracker(); + action_tracker.OnRuleMatched(request.dnr_action->extension_ids, + request.frame_data.tab_id); +} + } // namespace void WebRequestAPI::Proxy::HandleAuthRequest( @@ -1013,12 +1027,15 @@ case Action::Type::NONE: break; case Action::Type::BLOCK: + OnDNRActionMatched(browser_context, *request); return net::ERR_BLOCKED_BY_CLIENT; case Action::Type::COLLAPSE: + OnDNRActionMatched(browser_context, *request); *should_collapse_initiator = true; return net::ERR_BLOCKED_BY_CLIENT; case Action::Type::REDIRECT: DCHECK(action.redirect_url); + OnDNRActionMatched(browser_context, *request); *new_url = action.redirect_url.value(); return net::OK; case Action::Type::REMOVE_HEADERS: @@ -1091,6 +1108,11 @@ } while (headers->HasHeader(header)); } + // TODO(crbug.com/991420): This does not properly associate which headers are + // removed by which extensions. + if (!removed_headers.empty()) + OnDNRActionMatched(browser_context, *request); + bool initialize_blocked_requests = false; initialize_blocked_requests |= @@ -1184,6 +1206,10 @@ // |override_response_headers| don't point to the same object. *override_response_headers = base::MakeRefCounted<net::HttpResponseHeaders>( filtered_response_headers->raw_headers()); + + // TODO(crbug.com/991420): This does not properly associate which headers + // are removed by which extensions. + OnDNRActionMatched(browser_context, *request); } bool initialize_blocked_requests = false;
diff --git a/extensions/browser/content_hash_fetcher.cc b/extensions/browser/content_hash_fetcher.cc index db1c8d02..d0aa506 100644 --- a/extensions/browser/content_hash_fetcher.cc +++ b/extensions/browser/content_hash_fetcher.cc
@@ -83,9 +83,8 @@ })"); auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = fetch_params_.fetch_url; - resource_request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES | - net::LOAD_DISABLE_CACHE; + resource_request->load_flags = net::LOAD_DISABLE_CACHE; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; network::mojom::URLLoaderFactoryPtr url_loader_factory_ptr; url_loader_factory_ptr.Bind(
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index f7343aea..2b10bca0 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h
@@ -210,7 +210,7 @@ DELETED_EXPERIMENTAL_MEDIAGALLERIES_ASSEMBLEMEDIAFILE = 149, BOOKMARKMANAGERPRIVATE_STARTDRAG = 150, BROWSINGDATA_REMOVEPASSWORDS = 151, - DOWNLOADS_DRAG = 152, + DELETED_DOWNLOADS_DRAG = 152, INPUT_IME_SETCOMPOSITION = 153, METRICSPRIVATE_RECORDUSERACTION = 154, USB_RELEASEINTERFACE = 155,
diff --git a/extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc b/extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc index ad27ff6..bc6ec53 100644 --- a/extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc +++ b/extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc
@@ -56,8 +56,7 @@ })"); auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = url_; - resource_request->load_flags = - net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; fetcher_ = network::SimpleURLLoader::Create(std::move(resource_request), traffic_annotation); fetcher_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
diff --git a/extensions/test/test_background_page_first_load_observer.cc b/extensions/test/test_background_page_first_load_observer.cc new file mode 100644 index 0000000..7f8300aa --- /dev/null +++ b/extensions/test/test_background_page_first_load_observer.cc
@@ -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. + +#include "extensions/test/test_background_page_first_load_observer.h" + +#include "base/logging.h" +#include "extensions/browser/extension_host.h" +#include "extensions/browser/process_manager.h" + +namespace extensions { + +TestBackgroundPageFirstLoadObserver::TestBackgroundPageFirstLoadObserver( + content::BrowserContext* browser_context, + const ExtensionId& extension_id) + : extension_id_(extension_id), + process_manager_(ProcessManager::Get(browser_context)), + process_manager_observer_(this) { + process_manager_observer_.Add(process_manager_); + extension_host_ = + process_manager_->GetBackgroundHostForExtension(extension_id_); + if (extension_host_) + OnObtainedExtensionHost(); +} + +TestBackgroundPageFirstLoadObserver::~TestBackgroundPageFirstLoadObserver() { + if (extension_host_) { + static_cast<DeferredStartRenderHost*>(extension_host_) + ->RemoveDeferredStartRenderHostObserver(this); + } +} + +void TestBackgroundPageFirstLoadObserver::Wait() { + if (!extension_host_ || !extension_host_->has_loaded_once()) + run_loop_.Run(); +} + +void TestBackgroundPageFirstLoadObserver::OnBackgroundHostCreated( + ExtensionHost* host) { + if (host->extension_id() == extension_id_) { + DCHECK(!extension_host_); + extension_host_ = host; + OnObtainedExtensionHost(); + } +} + +void TestBackgroundPageFirstLoadObserver:: + OnDeferredStartRenderHostDidStopFirstLoad( + const DeferredStartRenderHost* host) { + run_loop_.Quit(); +} + +void TestBackgroundPageFirstLoadObserver::OnObtainedExtensionHost() { + static_cast<DeferredStartRenderHost*>(extension_host_) + ->AddDeferredStartRenderHostObserver(this); +} + +} // namespace extensions
diff --git a/extensions/test/test_background_page_first_load_observer.h b/extensions/test/test_background_page_first_load_observer.h new file mode 100644 index 0000000..63a2bc5 --- /dev/null +++ b/extensions/test/test_background_page_first_load_observer.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 EXTENSIONS_TEST_TEST_BACKGROUND_PAGE_FIRST_LOAD_OBSERVER_H_ +#define EXTENSIONS_TEST_TEST_BACKGROUND_PAGE_FIRST_LOAD_OBSERVER_H_ + +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/scoped_observer.h" +#include "extensions/browser/deferred_start_render_host_observer.h" +#include "extensions/browser/process_manager_observer.h" +#include "extensions/common/extension_id.h" + +namespace content { +class BrowserContext; +} + +// Allows to wait until the WebContents of an extension's ExtensionHost sees its +// first DidStopLoading(). +namespace extensions { + +class ExtensionHost; +class ProcessManager; + +class TestBackgroundPageFirstLoadObserver + : public ProcessManagerObserver, + public DeferredStartRenderHostObserver { + public: + TestBackgroundPageFirstLoadObserver(content::BrowserContext* browser_context, + const ExtensionId& extension_id); + ~TestBackgroundPageFirstLoadObserver() override; + + void Wait(); + + private: + // ProcessManagerObserver: + void OnBackgroundHostCreated(ExtensionHost* host) override; + + // DeferredStartRenderHostObserver: + void OnDeferredStartRenderHostDidStopFirstLoad( + const DeferredStartRenderHost* host) override; + + void OnObtainedExtensionHost(); + + const ExtensionId extension_id_; + ProcessManager* const process_manager_ = nullptr; + ExtensionHost* extension_host_ = nullptr; + base::RunLoop run_loop_; + ScopedObserver<ProcessManager, ProcessManagerObserver> + process_manager_observer_; + + DISALLOW_COPY_AND_ASSIGN(TestBackgroundPageFirstLoadObserver); +}; + +} // namespace extensions + +#endif // EXTENSIONS_TEST_TEST_BACKGROUND_PAGE_FIRST_LOAD_OBSERVER_H_
diff --git a/gin/converter.cc b/gin/converter.cc index bc95df4..3498080e 100644 --- a/gin/converter.cc +++ b/gin/converter.cc
@@ -6,6 +6,7 @@ #include <stdint.h> +#include "base/strings/string_util.h" #include "v8/include/v8.h" using v8::ArrayBuffer; @@ -151,6 +152,29 @@ return true; } +Local<Value> Converter<base::string16>::ToV8(Isolate* isolate, + const base::string16& val) { + return String::NewFromTwoByte(isolate, + reinterpret_cast<const uint16_t*>(val.data()), + v8::NewStringType::kNormal, val.size()) + .ToLocalChecked(); +} + +bool Converter<base::string16>::FromV8(Isolate* isolate, + Local<Value> val, + base::string16* out) { + if (!val->IsString()) + return false; + Local<String> str = Local<String>::Cast(val); + int length = str->Length(); + // Note that the reinterpret cast is because on Windows string16 is an alias + // to wstring, and hence has character type wchar_t not uint16_t. + str->Write(isolate, + reinterpret_cast<uint16_t*>(base::WriteInto(out, length + 1)), 0, + length); + return true; +} + Local<Value> Converter<Local<Function>>::ToV8(Isolate* isolate, Local<Function> val) { return val.As<Value>(); @@ -240,6 +264,14 @@ .ToLocalChecked(); } +v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate, + const base::StringPiece16& val) { + return String::NewFromTwoByte(isolate, + reinterpret_cast<const uint16_t*>(val.data()), + v8::NewStringType::kInternalized, val.length()) + .ToLocalChecked(); +} + std::string V8ToString(v8::Isolate* isolate, v8::Local<v8::Value> value) { if (value.IsEmpty()) return std::string();
diff --git a/gin/converter.h b/gin/converter.h index 48be87c..27b4d0ac 100644 --- a/gin/converter.h +++ b/gin/converter.h
@@ -12,6 +12,7 @@ #include <vector> #include "base/logging.h" +#include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "gin/gin_export.h" #include "v8/include/v8.h" @@ -119,8 +120,17 @@ std::string* out); }; -template<> -struct GIN_EXPORT Converter<v8::Local<v8::Function> > { +template <> +struct GIN_EXPORT Converter<base::string16> { + static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, + const base::string16& val); + static bool FromV8(v8::Isolate* isolate, + v8::Local<v8::Value> val, + base::string16* out); +}; + +template <> +struct GIN_EXPORT Converter<v8::Local<v8::Function>> { static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, v8::Local<v8::Function> val); static bool FromV8(v8::Isolate* isolate, @@ -263,6 +273,10 @@ GIN_EXPORT v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate, const base::StringPiece& val); +// This crashes when input.size() > v8::String::kMaxLength. +GIN_EXPORT v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate, + const base::StringPiece16& val); + template<typename T> bool ConvertFromV8(v8::Isolate* isolate, v8::Local<v8::Value> input, T* result) {
diff --git a/google_apis/drive/base_requests.cc b/google_apis/drive/base_requests.cc index 49227c7d..ef79519c 100644 --- a/google_apis/drive/base_requests.cc +++ b/google_apis/drive/base_requests.cc
@@ -315,8 +315,8 @@ auto request = std::make_unique<network::ResourceRequest>(); request->url = url; request->method = GetRequestType(); - request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DISABLE_CACHE; + request->load_flags = net::LOAD_DISABLE_CACHE; + request->credentials_mode = network::mojom::CredentialsMode::kOmit; // Add request headers. // Note that SetHeader clears the current headers and sets it to the passed-in
diff --git a/google_apis/gaia/gaia_oauth_client.cc b/google_apis/gaia/gaia_oauth_client.cc index 4e0335bf..4838ad5 100644 --- a/google_apis/gaia/gaia_oauth_client.cc +++ b/google_apis/gaia/gaia_oauth_client.cc
@@ -386,8 +386,7 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = url_; resource_request->method = post_body_.empty() ? "GET" : "POST"; - resource_request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; if (!authorization_header_.empty()) resource_request->headers.SetHeader("Authorization", authorization_header_);
diff --git a/google_apis/gaia/oauth2_access_token_fetcher_impl.cc b/google_apis/gaia/oauth2_access_token_fetcher_impl.cc index 7e0e085..9105555 100644 --- a/google_apis/gaia/oauth2_access_token_fetcher_impl.cc +++ b/google_apis/gaia/oauth2_access_token_fetcher_impl.cc
@@ -120,8 +120,7 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = url; - resource_request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; if (!body.empty()) resource_request->method = "POST";
diff --git a/google_apis/gaia/oauth2_api_call_flow.cc b/google_apis/gaia/oauth2_api_call_flow.cc index 14c637d2..7ef2fdd 100644 --- a/google_apis/gaia/oauth2_api_call_flow.cc +++ b/google_apis/gaia/oauth2_api_call_flow.cc
@@ -89,8 +89,7 @@ auto request = std::make_unique<network::ResourceRequest>(); request->url = CreateApiCallUrl(); request->method = request_type; - request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + request->credentials_mode = network::mojom::CredentialsMode::kOmit; request->headers.SetHeader("Authorization", MakeAuthorizationValue(access_token)); std::unique_ptr<network::SimpleURLLoader> result =
diff --git a/google_apis/gcm/engine/checkin_request.cc b/google_apis/gcm/engine/checkin_request.cc index b2723cd..a0999b6 100644 --- a/google_apis/gcm/engine/checkin_request.cc +++ b/google_apis/gcm/engine/checkin_request.cc
@@ -183,8 +183,7 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = checkin_url_; resource_request->method = "POST"; - resource_request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request), traffic_annotation); url_loader_->AttachStringForUpload(upload_data, kRequestContentType);
diff --git a/google_apis/gcm/engine/registration_request.cc b/google_apis/gcm/engine/registration_request.cc index a99879d..1332d878 100644 --- a/google_apis/gcm/engine/registration_request.cc +++ b/google_apis/gcm/engine/registration_request.cc
@@ -177,8 +177,7 @@ auto request = std::make_unique<network::ResourceRequest>(); request->url = registration_url_; request->method = "POST"; - request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + request->credentials_mode = network::mojom::CredentialsMode::kOmit; BuildRequestHeaders(&request->headers); std::string body;
diff --git a/google_apis/gcm/engine/registration_request_unittest.cc b/google_apis/gcm/engine/registration_request_unittest.cc index d325019..4df9c22 100644 --- a/google_apis/gcm/engine/registration_request_unittest.cc +++ b/google_apis/gcm/engine/registration_request_unittest.cc
@@ -462,8 +462,8 @@ const network::ResourceRequest* pending_request; ASSERT_TRUE( test_url_loader_factory()->IsPending(kRegistrationURL, &pending_request)); - EXPECT_TRUE(pending_request->load_flags & net::LOAD_DO_NOT_SEND_COOKIES); - EXPECT_TRUE(pending_request->load_flags & net::LOAD_DO_NOT_SAVE_COOKIES); + EXPECT_EQ(network::mojom::CredentialsMode::kOmit, + pending_request->credentials_mode); // Verify that authorization header was put together properly. const net::HttpRequestHeaders* headers =
diff --git a/google_apis/gcm/engine/unregistration_request.cc b/google_apis/gcm/engine/unregistration_request.cc index ca1a7c2..e20991b5 100644 --- a/google_apis/gcm/engine/unregistration_request.cc +++ b/google_apis/gcm/engine/unregistration_request.cc
@@ -162,8 +162,7 @@ auto request = std::make_unique<network::ResourceRequest>(); request->url = registration_url_; request->method = "POST"; - request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + request->credentials_mode = network::mojom::CredentialsMode::kOmit; BuildRequestHeaders(&request->headers); std::string body;
diff --git a/google_apis/gcm/engine/unregistration_request_unittest.cc b/google_apis/gcm/engine/unregistration_request_unittest.cc index 16a3543..228fee9 100644 --- a/google_apis/gcm/engine/unregistration_request_unittest.cc +++ b/google_apis/gcm/engine/unregistration_request_unittest.cc
@@ -115,8 +115,8 @@ const network::ResourceRequest* pending_request; ASSERT_TRUE( test_url_loader_factory()->IsPending(kRegistrationURL, &pending_request)); - EXPECT_TRUE(pending_request->load_flags & net::LOAD_DO_NOT_SEND_COOKIES); - EXPECT_TRUE(pending_request->load_flags & net::LOAD_DO_NOT_SAVE_COOKIES); + EXPECT_EQ(network::mojom::CredentialsMode::kOmit, + pending_request->credentials_mode); // Verify that authorization header was put together properly. const net::HttpRequestHeaders* headers =
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg index cc5cc33..19f6bde 100644 --- a/infra/config/cr-buildbucket.cfg +++ b/infra/config/cr-buildbucket.cfg
@@ -1281,6 +1281,23 @@ } builders { + name: "android-sdk-packager" + mixins: "linux-xenial" + mixins: "builderless" + recipe { + name: "android/sdk_packager" + properties_j: <<END + packages: [ + { + "sdk_package_name": "emulator", + "cipd_yaml": "third_party/android_sdk/cipd_emulator.yaml" + } + ] + END + } + } + + builders { name: "Cast Android (dbg)" mixins: "android-ci" mixins: "linux-xenial"
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg index a472ce6..0554925 100644 --- a/infra/config/luci-scheduler.cfg +++ b/infra/config/luci-scheduler.cfg
@@ -885,6 +885,18 @@ } job { + id: "android-sdk-packager" + acl_sets: "default" + # Run weekly, on Sunday morning at midnight. + schedule: "0 7 * * 0 *" + buildbucket: { + server: "cr-buildbucket.appspot.com" + bucket: "luci.chromium.ci" + builder: "android-sdk-packager" + } +} + +job { id: "Cast Android (dbg)" acl_sets: "default" buildbucket: {
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm index 58b6f6c..b966875 100644 --- a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm +++ b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
@@ -655,9 +655,7 @@ - (void)testOpenLinkInNewTab { // TODO(crbug.com/989550) Disable broken context menu tests on Xc11b5. if (@available(iOS 13, *)) { - if ([ChromeEarlGrey isIPadIdiom]) { - EARL_GREY_TEST_DISABLED(@"Test disabled on iPad."); - } + EARL_GREY_TEST_DISABLED(@"Test disabled on iOS13."); } // Create map of canned responses and set up the test HTML server. std::map<GURL, std::string> responses;
diff --git a/ios/chrome/browser/omaha/omaha_service.mm b/ios/chrome/browser/omaha/omaha_service.mm index 931bbc8..821c864 100644 --- a/ios/chrome/browser/omaha/omaha_service.mm +++ b/ios/chrome/browser/omaha/omaha_service.mm
@@ -549,8 +549,7 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = url; resource_request->method = "POST"; - resource_request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; // If this is not the first try, notify the omaha server. if (number_of_tries_ && IsNextPingInstallRetry()) {
diff --git a/ios/chrome/browser/ui/autofill/cells/cvc_item.mm b/ios/chrome/browser/ui/autofill/cells/cvc_item.mm index 81d5721..1f440d42 100644 --- a/ios/chrome/browser/ui/autofill/cells/cvc_item.mm +++ b/ios/chrome/browser/ui/autofill/cells/cvc_item.mm
@@ -5,11 +5,10 @@ #import "ios/chrome/browser/ui/autofill/cells/cvc_item.h" #include "components/strings/grit/components_strings.h" -#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" #import "ios/chrome/browser/ui/util/uikit_ui_util.h" +#import "ios/chrome/common/colors/semantic_color_names.h" #include "ios/chrome/grit/ios_strings.h" #import "ios/public/provider/chrome/browser/chrome_browser_provider.h" -#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h" #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" #include "ui/base/l10n/l10n_util.h" @@ -110,7 +109,7 @@ _instructionsTextLabel = [[UILabel alloc] init]; _instructionsTextLabel.font = [[MDCTypography fontLoader] mediumFontOfSize:14]; - _instructionsTextLabel.textColor = [[MDCPalette greyPalette] tint500]; + _instructionsTextLabel.textColor = [UIColor colorNamed:kTextSecondaryColor]; _instructionsTextLabel.numberOfLines = 0; _instructionsTextLabel.lineBreakMode = NSLineBreakByWordWrapping; _instructionsTextLabel.translatesAutoresizingMaskIntoConstraints = NO; @@ -118,7 +117,7 @@ _errorLabel = [[UILabel alloc] init]; _errorLabel.font = [[MDCTypography fontLoader] regularFontOfSize:12]; - _errorLabel.textColor = [[MDCPalette cr_redPalette] tint500]; + _errorLabel.textColor = [UIColor colorNamed:kRedColor]; _errorLabel.numberOfLines = 0; _errorLabel.lineBreakMode = NSLineBreakByWordWrapping; _errorLabel.translatesAutoresizingMaskIntoConstraints = NO; @@ -157,6 +156,7 @@ [contentView addSubview:_CVCContainerView]; _CVCInput = ios::GetChromeBrowserProvider()->CreateStyledTextField(); + _CVCInput.textColor = [UIColor colorNamed:kTextPrimaryColor]; _CVCInput.placeholder = l10n_util::GetNSString(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC); _CVCInput.accessibilityIdentifier = @"CVC_textField"; @@ -175,7 +175,7 @@ [_buttonForNewCard setTitle:l10n_util::GetNSString(IDS_AUTOFILL_CARD_UNMASK_NEW_CARD_LINK) forState:UIControlStateNormal]; - [_buttonForNewCard setTitleColor:[[MDCPalette cr_bluePalette] tint500] + [_buttonForNewCard setTitleColor:[UIColor colorNamed:kBlueColor] forState:UIControlStateNormal]; _buttonForNewCard.translatesAutoresizingMaskIntoConstraints = NO; [contentView addSubview:_buttonForNewCard];
diff --git a/ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.mm b/ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.mm index a17a9cef..df91db86 100644 --- a/ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.mm +++ b/ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.mm
@@ -5,11 +5,10 @@ #import "ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.h" #include "ios/chrome/browser/ui/collection_view/cells/collection_view_cell_constants.h" -#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" #import "ios/chrome/browser/ui/util/rtl_geometry.h" #import "ios/chrome/browser/ui/util/uikit_ui_util.h" +#import "ios/chrome/common/colors/semantic_color_names.h" #import "ios/chrome/common/ui_util/constraints_ui_util.h" -#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h" #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -79,8 +78,8 @@ } cell.textField.enabled = self.textFieldEnabled; cell.textField.textColor = self.textFieldEnabled - ? [[MDCPalette cr_bluePalette] tint500] - : [[MDCPalette greyPalette] tint500]; + ? [UIColor colorNamed:kBlueColor] + : [UIColor colorNamed:kTextSecondaryColor]; [cell.textField addTarget:self action:@selector(textFieldChanged:) forControlEvents:UIControlEventEditingChanged]; @@ -182,11 +181,11 @@ } else { MaybeSetUILabelScaledFont(withFontScaling, self.textLabel, [[MDCTypography fontLoader] mediumFontOfSize:14]); - self.textLabel.textColor = [[MDCPalette greyPalette] tint900]; + self.textLabel.textColor = [UIColor colorNamed:kTextPrimaryColor]; MaybeSetUITextFieldScaledFont( withFontScaling, self.textField, [[MDCTypography fontLoader] lightFontOfSize:16]); - self.textField.textColor = [[MDCPalette greyPalette] tint500]; + self.textField.textColor = [UIColor colorNamed:kTextSecondaryColor]; } }
diff --git a/ios/chrome/browser/ui/autofill/cells/status_item.mm b/ios/chrome/browser/ui/autofill/cells/status_item.mm index acad67fe..0c8bf81 100644 --- a/ios/chrome/browser/ui/autofill/cells/status_item.mm +++ b/ios/chrome/browser/ui/autofill/cells/status_item.mm
@@ -5,11 +5,10 @@ #import "ios/chrome/browser/ui/autofill/cells/status_item.h" #include "base/logging.h" -#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" #import "ios/chrome/browser/ui/util/uikit_ui_util.h" +#import "ios/chrome/common/colors/semantic_color_names.h" #include "ios/chrome/grit/ios_theme_resources.h" #import "ios/third_party/material_components_ios/src/components/ActivityIndicator/src/MaterialActivityIndicator.h" -#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h" #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -66,7 +65,7 @@ [contentView addSubview:verticalCenteringView]; _activityIndicator = [[MDCActivityIndicator alloc] init]; - _activityIndicator.cycleColors = @[ [[MDCPalette cr_bluePalette] tint500] ]; + _activityIndicator.cycleColors = @[ [UIColor colorNamed:kBlueColor] ]; [_activityIndicator setRadius:10]; _activityIndicator.translatesAutoresizingMaskIntoConstraints = NO; [verticalCenteringView addSubview:_activityIndicator]; @@ -169,21 +168,21 @@ - (void)configureForState:(StatusItemState)state { switch (state) { case StatusItemState::VERIFYING: - self.textLabel.textColor = [[MDCPalette cr_bluePalette] tint500]; + self.textLabel.textColor = [UIColor colorNamed:kBlueColor]; [self.activityIndicator startAnimating]; self.activityIndicator.hidden = NO; self.verifiedImageView.hidden = YES; self.errorImageView.hidden = YES; break; case StatusItemState::VERIFIED: - self.textLabel.textColor = [[MDCPalette cr_bluePalette] tint500]; + self.textLabel.textColor = [UIColor colorNamed:kBlueColor]; [self.activityIndicator stopAnimating]; self.activityIndicator.hidden = YES; self.verifiedImageView.hidden = NO; self.errorImageView.hidden = YES; break; case StatusItemState::ERROR: - self.textLabel.textColor = [[MDCPalette cr_redPalette] tint500]; + self.textLabel.textColor = [UIColor colorNamed:kRedColor]; [self.activityIndicator stopAnimating]; self.activityIndicator.hidden = YES; self.verifiedImageView.hidden = YES;
diff --git a/ios/chrome/browser/ui/badges/BUILD.gn b/ios/chrome/browser/ui/badges/BUILD.gn index 0b0aa5f9..07ee5a8 100644 --- a/ios/chrome/browser/ui/badges/BUILD.gn +++ b/ios/chrome/browser/ui/badges/BUILD.gn
@@ -22,11 +22,14 @@ "badge_consumer.h", "badge_mediator.h", "badge_mediator.mm", + "badge_static_item.h", + "badge_static_item.mm", "badge_view_controller.h", "badge_view_controller.mm", ] deps = [ ":public", + "resources:incognito_badge", "//base:base", "//ios/chrome/browser/infobars:badge", "//ios/chrome/browser/infobars:public", @@ -40,5 +43,6 @@ "//ios/chrome/browser/web_state_list", "//ios/chrome/common/colors", "//ios/chrome/common/ui_util", + "//ios/web/public", ] }
diff --git a/ios/chrome/browser/ui/badges/badge_button_factory.mm b/ios/chrome/browser/ui/badges/badge_button_factory.mm index 98e6a15..249f634 100644 --- a/ios/chrome/browser/ui/badges/badge_button_factory.mm +++ b/ios/chrome/browser/ui/badges/badge_button_factory.mm
@@ -36,6 +36,8 @@ break; case BadgeType::kBadgeTypePasswordUpdate: return [self passwordsUpdateBadgeButton]; + case BadgeType::kBadgeTypeIncognito: + return [self incognitoBadgeButton]; case BadgeType::kBadgeTypeNone: NOTREACHED() << "A badge should not have kBadgeTypeNone"; return nil; @@ -64,6 +66,17 @@ return button; } +- (BadgeButton*)incognitoBadgeButton { + BadgeButton* button = [self createButtonForType:BadgeType::kBadgeTypeIncognito + imageNamed:@"incognito_badge"]; + UIImage* image = [[UIImage imageNamed:@"incognito_badge"] + imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; + [button setImage:image forState:UIControlStateDisabled]; + button.accessibilityTraits &= ~UIAccessibilityTraitButton; + button.enabled = NO; + return button; +} + - (BadgeButton*)createButtonForType:(BadgeType)badgeType imageNamed:(NSString*)imageName { BadgeButton* button = [BadgeButton badgeButtonWithType:badgeType]; @@ -72,6 +85,9 @@ [button setImage:image forState:UIControlStateNormal]; button.translatesAutoresizingMaskIntoConstraints = NO; button.imageView.contentMode = UIViewContentModeScaleAspectFit; + [NSLayoutConstraint + activateConstraints:@[ [button.widthAnchor + constraintEqualToAnchor:button.heightAnchor] ]]; return button; }
diff --git a/ios/chrome/browser/ui/badges/badge_mediator.mm b/ios/chrome/browser/ui/badges/badge_mediator.mm index 7968da1b..9430b004 100644 --- a/ios/chrome/browser/ui/badges/badge_mediator.mm +++ b/ios/chrome/browser/ui/badges/badge_mediator.mm
@@ -9,8 +9,10 @@ #import "ios/chrome/browser/infobars/infobar_type.h" #import "ios/chrome/browser/ui/badges/badge_consumer.h" #import "ios/chrome/browser/ui/badges/badge_item.h" +#import "ios/chrome/browser/ui/badges/badge_static_item.h" #import "ios/chrome/browser/web_state_list/web_state_list.h" #import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h" +#include "ios/web/public/browser_state.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -109,6 +111,11 @@ NSArray* infobar_badges_array = [NSArray arrayWithObjects:&infobar_badges[0] count:infobar_badges.size()]; [self.consumer setupWithBadges:infobar_badges_array]; + if (newWebState->GetBrowserState()->IsOffTheRecord()) { + BadgeStaticItem* incognitoItem = [[BadgeStaticItem alloc] + initWithBadgeType:BadgeType::kBadgeTypeIncognito]; + [self.consumer addBadge:incognitoItem]; + } } @end
diff --git a/ios/chrome/browser/ui/badges/badge_static_item.h b/ios/chrome/browser/ui/badges/badge_static_item.h new file mode 100644 index 0000000..6344645 --- /dev/null +++ b/ios/chrome/browser/ui/badges/badge_static_item.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 IOS_CHROME_BROWSER_UI_BADGES_BADGE_STATIC_ITEM_H_ +#define IOS_CHROME_BROWSER_UI_BADGES_BADGE_STATIC_ITEM_H_ + +#import "ios/chrome/browser/ui/badges/badge_item.h" + +// Holds properties and values needed to configure an BadgeButton that is not +// tappable. +@interface BadgeStaticItem : NSObject <BadgeItem> + +- (instancetype)initWithBadgeType:(BadgeType)badgeType + NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + +@end + +#endif // IOS_CHROME_BROWSER_UI_BADGES_BADGE_STATIC_ITEM_H_
diff --git a/ios/chrome/browser/ui/badges/badge_static_item.mm b/ios/chrome/browser/ui/badges/badge_static_item.mm new file mode 100644 index 0000000..deb3fa8 --- /dev/null +++ b/ios/chrome/browser/ui/badges/badge_static_item.mm
@@ -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. + +#import "ios/chrome/browser/ui/badges/badge_static_item.h" + +#import "ios/chrome/browser/ui/badges/badge_type.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface BadgeStaticItem () + +// The BadgeType of this item. +@property(nonatomic, assign) BadgeType badgeType; + +@end + +@implementation BadgeStaticItem +// Synthesized from protocol. +@synthesize tappable = _tappable; +// Sythesized from protocol. +@synthesize accepted = _accepted; + +- (instancetype)initWithBadgeType:(BadgeType)badgeType { + self = [super init]; + if (self) { + _badgeType = badgeType; + _tappable = NO; + _accepted = NO; + } + return self; +} + +#pragma mark - BadgeItem + +- (BadgeType)badgeType { + return _badgeType; +} + +@end
diff --git a/ios/chrome/browser/ui/badges/badge_type.h b/ios/chrome/browser/ui/badges/badge_type.h index c676a2b8..1bca959 100644 --- a/ios/chrome/browser/ui/badges/badge_type.h +++ b/ios/chrome/browser/ui/badges/badge_type.h
@@ -15,6 +15,8 @@ kBadgeTypePasswordSave = 1, // Badge type for the Update Passwords Infobar. kBadgeTypePasswordUpdate = 2, + // Badge type for the Incognito Badge. + kBadgeTypeIncognito = 3, }; #endif // IOS_CHROME_BROWSER_UI_BADGES_BADGE_TYPE_H_
diff --git a/ios/chrome/browser/ui/badges/badge_view_controller.mm b/ios/chrome/browser/ui/badges/badge_view_controller.mm index 89cdd92..618cd57 100644 --- a/ios/chrome/browser/ui/badges/badge_view_controller.mm +++ b/ios/chrome/browser/ui/badges/badge_view_controller.mm
@@ -20,14 +20,20 @@ // Button factory. @property(nonatomic, strong) BadgeButtonFactory* buttonFactory; -// Badge button to show when FullScreen is in expanded mode (i.e. when the +// BadgeButton to show when in FullScreen (i.e. when the // toolbars are expanded). Setting this property will add the button to the -// view hierarchy. +// StackView. @property(nonatomic, strong) BadgeButton* displayedBadge; +// BadgeButton to show in both FullScreen and non FullScreen. +@property(nonatomic, strong) BadgeButton* fullScreenBadge; + // Array of all available badges. @property(nonatomic, strong) NSMutableArray<BadgeButton*>* badges; +// StackView holding the displayedBadge and fullScreenBadge. +@property(nonatomic, strong) UIStackView* stackView; + @end @implementation BadgeViewController @@ -39,6 +45,11 @@ actionHandler.dispatcher = self.dispatcher; self.buttonFactory = [[BadgeButtonFactory alloc] initWithActionHandler:actionHandler]; + self.stackView = [[UIStackView alloc] init]; + self.stackView.translatesAutoresizingMaskIntoConstraints = NO; + self.stackView.axis = UILayoutConstraintAxisHorizontal; + [self.view addSubview:self.stackView]; + AddSameConstraints(self.view, self.stackView); } #pragma mark - Protocols @@ -49,7 +60,7 @@ if (!self.badges) { self.badges = [[NSMutableArray alloc] init]; } - [self.displayedBadge removeFromSuperview]; + self.fullScreenBadge = nil; self.displayedBadge = nil; [self.badges removeAllObjects]; for (id<BadgeItem> item in badges) { @@ -63,11 +74,17 @@ } - (void)addBadge:(id<BadgeItem>)badgeItem { + BadgeButton* newButton = + [self.buttonFactory getBadgeButtonForBadgeType:badgeItem.badgeType]; + // The incognito badge is one that must be visible at all times and persist + // when fullscreen is expanded and collapsed. + if (badgeItem.badgeType == BadgeType::kBadgeTypeIncognito) { + self.fullScreenBadge = newButton; + return; + } if (!self.badges) { self.badges = [[NSMutableArray alloc] init]; } - BadgeButton* newButton = - [self.buttonFactory getBadgeButtonForBadgeType:badgeItem.badgeType]; // No need to animate this change since it is the initial state. [newButton setAccepted:badgeItem.accepted animated:NO]; [self.badges addObject:newButton]; @@ -103,25 +120,25 @@ #pragma mark - Getter/Setter - (void)setDisplayedBadge:(BadgeButton*)badgeButton { + [self.stackView removeArrangedSubview:_displayedBadge]; [_displayedBadge removeFromSuperview]; if (!badgeButton) { _displayedBadge = nil; return; } - _displayedBadge = badgeButton; - [self.view addSubview:_displayedBadge]; - [NSLayoutConstraint activateConstraints:@[ - [_displayedBadge.widthAnchor - constraintEqualToAnchor:_displayedBadge.heightAnchor], - [_displayedBadge.topAnchor constraintEqualToAnchor:self.view.topAnchor], - [_displayedBadge.bottomAnchor - constraintEqualToAnchor:self.view.bottomAnchor], - [_displayedBadge.leadingAnchor - constraintEqualToAnchor:self.view.leadingAnchor], - [_displayedBadge.trailingAnchor - constraintEqualToAnchor:self.view.trailingAnchor], - ]]; + [self.stackView addArrangedSubview:_displayedBadge]; +} + +- (void)setFullScreenBadge:(BadgeButton*)fullScreenBadge { + [self.stackView removeArrangedSubview:_fullScreenBadge]; + [_fullScreenBadge removeFromSuperview]; + if (!fullScreenBadge) { + _fullScreenBadge = nil; + return; + } + _fullScreenBadge = fullScreenBadge; + [self.stackView insertArrangedSubview:_fullScreenBadge atIndex:0]; } #pragma mark - Helpers
diff --git a/ios/chrome/browser/ui/badges/resources/BUILD.gn b/ios/chrome/browser/ui/badges/resources/BUILD.gn new file mode 100644 index 0000000..4f30b86 --- /dev/null +++ b/ios/chrome/browser/ui/badges/resources/BUILD.gn
@@ -0,0 +1,13 @@ +# 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/config/ios/asset_catalog.gni") + +imageset("incognito_badge") { + sources = [ + "incognito_badge.imageset/Contents.json", + "incognito_badge.imageset/incognito_badge@2x.png", + "incognito_badge.imageset/incognito_badge@3x.png", + ] +}
diff --git a/ios/chrome/browser/ui/badges/resources/incognito_badge.imageset/Contents.json b/ios/chrome/browser/ui/badges/resources/incognito_badge.imageset/Contents.json new file mode 100644 index 0000000..1b85dab --- /dev/null +++ b/ios/chrome/browser/ui/badges/resources/incognito_badge.imageset/Contents.json
@@ -0,0 +1,18 @@ +{ + "images": [ + { + "idiom": "universal", + "scale": "2x", + "filename": "incognito_badge@2x.png" + }, + { + "idiom": "universal", + "scale": "3x", + "filename": "incognito_badge@3x.png" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +}
diff --git a/ios/chrome/browser/ui/badges/resources/incognito_badge.imageset/incognito_badge@2x.png b/ios/chrome/browser/ui/badges/resources/incognito_badge.imageset/incognito_badge@2x.png new file mode 100644 index 0000000..2f5062d --- /dev/null +++ b/ios/chrome/browser/ui/badges/resources/incognito_badge.imageset/incognito_badge@2x.png Binary files differ
diff --git a/ios/chrome/browser/ui/badges/resources/incognito_badge.imageset/incognito_badge@3x.png b/ios/chrome/browser/ui/badges/resources/incognito_badge.imageset/incognito_badge@3x.png new file mode 100644 index 0000000..3750f4a --- /dev/null +++ b/ios/chrome/browser/ui/badges/resources/incognito_badge.imageset/incognito_badge@3x.png Binary files differ
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm index 6ac804b..2c2e6a1 100644 --- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm +++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
@@ -8,7 +8,7 @@ #import "ios/chrome/browser/ui/util/i18n_string.h" #include "ios/chrome/browser/ui/util/ui_util.h" #import "ios/chrome/browser/ui/util/uikit_ui_util.h" -#import "ios/chrome/common/colors/UIColor+cr_semantic_colors.h" +#import "ios/chrome/common/colors/semantic_color_names.h" #import "ios/chrome/common/favicon/favicon_view.h" #import "ios/chrome/common/ui_util/constraints_ui_util.h" #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" @@ -30,10 +30,6 @@ // Name of the icon displayed when there is not image but one should be // displayed. NSString* const kNoImageIconName = @"content_suggestions_no_image"; -// No image icon percentage of white. -const CGFloat kNoImageIconWhite = 0.38; -// No image background percentage of white. -const CGFloat kNoImageBackgroundWhite = 0.95; // Duration of the animation to display the image. const CGFloat kAnimationDuration = 0.3; } @@ -104,17 +100,16 @@ [_imageContainer addSubview:_noImageIcon]; [_imageContainer addSubview:_contentImageView]; - _imageContainer.backgroundColor = - [UIColor colorWithWhite:kNoImageBackgroundWhite alpha:1]; + _imageContainer.backgroundColor = [UIColor colorNamed:kGrey100Color]; _noImageIcon.image = [[UIImage imageNamed:kNoImageIconName] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - [_noImageIcon - setTintColor:[UIColor colorWithWhite:kNoImageIconWhite alpha:1]]; + _noImageIcon.tintColor = [UIColor colorNamed:kGrey400Color]; [[self class] configureTitleLabel:_titleLabel]; _additionalInformationLabel.font = [[self class] additionalInformationFont]; _faviconView.font = [[MDCTypography fontLoader] mediumFontOfSize:10]; - _additionalInformationLabel.textColor = UIColor.cr_secondaryLabelColor; + _additionalInformationLabel.textColor = + [UIColor colorNamed:kTextSecondaryColor]; [self applyConstraints]; } @@ -124,7 +119,7 @@ - (void)setHighlighted:(BOOL)highlighted { [super setHighlighted:highlighted]; self.contentView.backgroundColor = - highlighted ? [UIColor colorWithWhite:0 alpha:0.05] : nil; + highlighted ? [UIColor colorNamed:kTableViewRowHighlightColor] : nil; } - (void)setContentImage:(UIImage*)image animated:(BOOL)animated { @@ -376,7 +371,7 @@ // Configures the |titleLabel|. + (void)configureTitleLabel:(UILabel*)titleLabel { - titleLabel.textColor = UIColor.cr_labelColor; + titleLabel.textColor = [UIColor colorNamed:kTextPrimaryColor]; UIFontDescriptor* descriptor = [[UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleSubheadline] fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
diff --git a/ios/chrome/browser/ui/download/download_manager_egtest.mm b/ios/chrome/browser/ui/download/download_manager_egtest.mm index fe5a101..fbd01bc 100644 --- a/ios/chrome/browser/ui/download/download_manager_egtest.mm +++ b/ios/chrome/browser/ui/download/download_manager_egtest.mm
@@ -174,9 +174,7 @@ - (void)testDownloadInNewTab { // TODO(crbug.com/989550) Disable broken context menu tests on Xc11b5. if (@available(iOS 13, *)) { - if ([ChromeEarlGrey isIPadIdiom]) { - EARL_GREY_TEST_DISABLED(@"Test disabled on iPad."); - } + EARL_GREY_TEST_DISABLED(@"Test disabled on iOS13."); } [ChromeEarlGrey loadURL:self.testServer->GetURL("/")];
diff --git a/ios/chrome/browser/ui/history/BUILD.gn b/ios/chrome/browser/ui/history/BUILD.gn index 7d28dd5a..bdab756 100644 --- a/ios/chrome/browser/ui/history/BUILD.gn +++ b/ios/chrome/browser/ui/history/BUILD.gn
@@ -27,6 +27,7 @@ "//ios/chrome/browser/ui/context_menu", "//ios/chrome/browser/ui/coordinators:chrome_coordinators", "//ios/chrome/browser/ui/table_view", + "//ios/chrome/browser/ui/table_view:feature_flags", "//ios/chrome/browser/ui/util", ] libs = [
diff --git a/ios/chrome/browser/ui/history/history_coordinator.mm b/ios/chrome/browser/ui/history/history_coordinator.mm index 7f5b167b..37b6a74 100644 --- a/ios/chrome/browser/ui/history/history_coordinator.mm +++ b/ios/chrome/browser/ui/history/history_coordinator.mm
@@ -16,6 +16,7 @@ #include "ios/chrome/browser/ui/history/history_table_view_controller.h" #import "ios/chrome/browser/ui/history/history_transitioning_delegate.h" #include "ios/chrome/browser/ui/history/ios_browsing_history_driver.h" +#import "ios/chrome/browser/ui/table_view/feature_flags.h" #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller.h" #include "ios/chrome/browser/ui/util/ui_util.h" @@ -86,12 +87,28 @@ self.historyTableViewController.localDispatcher = self; self.historyTableViewController.presentationDelegate = self.presentationDelegate; - self.historyTransitioningDelegate = - [[HistoryTransitioningDelegate alloc] init]; - self.historyNavigationController.transitioningDelegate = - self.historyTransitioningDelegate; - [self.historyNavigationController - setModalPresentationStyle:UIModalPresentationCustom]; + + BOOL useCustomPresentation = YES; + if (IsCollectionsCardPresentationStyleEnabled()) { + if (@available(iOS 13, *)) { +#if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) + [self.historyNavigationController + setModalPresentationStyle:UIModalPresentationFormSheet]; + self.historyNavigationController.presentationController.delegate = + self.historyTableViewController; + useCustomPresentation = NO; +#endif + } + } + + if (useCustomPresentation) { + self.historyTransitioningDelegate = + [[HistoryTransitioningDelegate alloc] init]; + self.historyNavigationController.transitioningDelegate = + self.historyTransitioningDelegate; + [self.historyNavigationController + setModalPresentationStyle:UIModalPresentationCustom]; + } [self.baseViewController presentViewController:self.historyNavigationController animated:YES
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.h b/ios/chrome/browser/ui/history/history_table_view_controller.h index 4ce8eef..7847b787 100644 --- a/ios/chrome/browser/ui/history/history_table_view_controller.h +++ b/ios/chrome/browser/ui/history/history_table_view_controller.h
@@ -22,7 +22,8 @@ // ChromeTableViewController for displaying history items. @interface HistoryTableViewController - : ChromeTableViewController<HistoryConsumer> + : ChromeTableViewController <HistoryConsumer, + UIAdaptivePresentationControllerDelegate> // The ViewController's BrowserState. @property(nonatomic, assign) ios::ChromeBrowserState* browserState; // Abstraction to communicate with HistoryService and WebHistoryService.
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.mm b/ios/chrome/browser/ui/history/history_table_view_controller.mm index 0d238cb6..5993b2d 100644 --- a/ios/chrome/browser/ui/history/history_table_view_controller.mm +++ b/ios/chrome/browser/ui/history/history_table_view_controller.mm
@@ -167,6 +167,7 @@ // Add a tableFooterView in order to disable separators at the bottom of the // tableView. self.tableView.tableFooterView = [[UIView alloc] init]; + self.tableView.accessibilityIdentifier = kHistoryTableViewIdentifier; // ContextMenu gesture recognizer. UILongPressGestureRecognizer* longPressRecognizer = [ @@ -475,6 +476,24 @@ [self updateEntriesStatusMessage]; } +#pragma mark UIAdaptivePresentationControllerDelegate + +- (void)presentationControllerWillDismiss: + (UIPresentationController*)presentationController { + if (self.searchInProgress) { + // Dismiss the keyboard if trying to dismiss the VC so the keyboard doesn't + // linger until the VC dismissal has completed. + [self.searchController.searchBar endEditing:YES]; + } +} + +- (void)presentationControllerDidDismiss: + (UIPresentationController*)presentationController { + // Call the localDispatcher dismissHistoryWithCompletion to clean up state and + // stop the Coordinator. + [self.localDispatcher dismissHistoryWithCompletion:nil]; +} + #pragma mark - History Data Updates // Search history for text |query| and display the results. |query| may be nil.
diff --git a/ios/chrome/browser/ui/history/history_ui_constants.h b/ios/chrome/browser/ui/history/history_ui_constants.h index f155ea5c..3dbe6c4c 100644 --- a/ios/chrome/browser/ui/history/history_ui_constants.h +++ b/ios/chrome/browser/ui/history/history_ui_constants.h
@@ -7,6 +7,8 @@ #import <Foundation/Foundation.h> +// Accessibility identifier for the History TableView. +extern NSString* const kHistoryTableViewIdentifier; // Accessibility identifier of the search controller search bar. extern NSString* const kHistorySearchControllerSearchBarIdentifier; // Accessibility identifier of the navigation controller done button. @@ -19,7 +21,7 @@ extern NSString* const kHistoryToolbarEditButtonIdentifier; // Accessibility identifier of the cancel toolbar button. extern NSString* const kHistoryToolbarCancelButtonIdentifier; -// Accessibility ID for the scrim over TableView. +// Accessibility identifier for the scrim over TableView. extern NSString* const kHistorySearchScrimIdentifier; #endif // IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_UI_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/history/history_ui_constants.mm b/ios/chrome/browser/ui/history/history_ui_constants.mm index 216d36fc..c41fe3c 100644 --- a/ios/chrome/browser/ui/history/history_ui_constants.mm +++ b/ios/chrome/browser/ui/history/history_ui_constants.mm
@@ -8,6 +8,7 @@ #error "This file requires ARC support." #endif +NSString* const kHistoryTableViewIdentifier = @"kHistoryTableViewIdentifier"; NSString* const kHistorySearchControllerSearchBarIdentifier = @"kHistorySearchControllerSearchBarIdentifier"; NSString* const kHistoryNavigationControllerDoneButtonIdentifier =
diff --git a/ios/chrome/browser/ui/history/history_ui_egtest.mm b/ios/chrome/browser/ui/history/history_ui_egtest.mm index 1d5e421..e1d307e 100644 --- a/ios/chrome/browser/ui/history/history_ui_egtest.mm +++ b/ios/chrome/browser/ui/history/history_ui_egtest.mm
@@ -404,6 +404,62 @@ _URL1.spec().c_str()); } +// Tests that the VC can be dismissed by swiping down. +- (void)testSwipeDownDismiss { + if (!base::ios::IsRunningOnOrLater(13, 0, 0)) { + EARL_GREY_TEST_SKIPPED(@"Test disabled on iOS 12 and lower."); + } + [self loadTestURLs]; + [self openHistoryPanel]; + + // Check that the TableView is presented. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kHistoryTableViewIdentifier)] + assertWithMatcher:grey_notNil()]; + + // Swipe TableView down. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kHistoryTableViewIdentifier)] + performAction:grey_swipeFastInDirection(kGREYDirectionDown)]; + + // Check that the TableView has been dismissed. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kHistoryTableViewIdentifier)] + assertWithMatcher:grey_nil()]; +} + +// Tests that the VC can be dismissed by swiping down while its searching. +- (void)testSwipeDownDismissWhileSearching { + if (!base::ios::IsRunningOnOrLater(13, 0, 0)) { + EARL_GREY_TEST_SKIPPED(@"Test disabled on iOS 12 and lower."); + } + [self loadTestURLs]; + [self openHistoryPanel]; + + // Check that the TableView is presented. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kHistoryTableViewIdentifier)] + assertWithMatcher:grey_notNil()]; + + // Search for the first URL. + [[EarlGrey selectElementWithMatcher:SearchIconButton()] + performAction:grey_tap()]; + NSString* searchString = + [NSString stringWithFormat:@"%s", _URL1.path().c_str()]; + [[EarlGrey selectElementWithMatcher:SearchIconButton()] + performAction:grey_typeText(searchString)]; + + // Swipe TableView down. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kHistoryTableViewIdentifier)] + performAction:grey_swipeFastInDirection(kGREYDirectionDown)]; + + // Check that the TableView has been dismissed. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kHistoryTableViewIdentifier)] + assertWithMatcher:grey_nil()]; +} + // Navigates to history and checks elements for accessibility. - (void)testAccessibilityOnHistory { [self loadTestURLs];
diff --git a/ios/chrome/browser/ui/page_info/BUILD.gn b/ios/chrome/browser/ui/page_info/BUILD.gn index c7548ae1..6003459 100644 --- a/ios/chrome/browser/ui/page_info/BUILD.gn +++ b/ios/chrome/browser/ui/page_info/BUILD.gn
@@ -5,8 +5,6 @@ source_set("page_info") { configs += [ "//build/config/compiler:enable_arc" ] sources = [ - "page_info_constants.h", - "page_info_constants.mm", "page_info_model.cc", "page_info_model.h", "page_info_model_observer.h", @@ -36,9 +34,20 @@ "//ui/gfx", "//url", ] + public_deps = [ + ":constants", + ] libs = [ "UIKit.framework" ] } +source_set("constants") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ + "page_info_constants.h", + "page_info_constants.mm", + ] +} + source_set("coordinator") { configs += [ "//build/config/compiler:enable_arc" ] sources = [ @@ -66,6 +75,7 @@ source_set("eg_tests") { configs += [ "//build/config/compiler:enable_arc" ] + defines = [ "CHROME_EARL_GREY_1" ] testonly = true sources = [ "page_info_egtest.mm", @@ -75,6 +85,31 @@ "//ios/chrome/browser/ui/popup_menu:constants", "//ios/chrome/browser/ui/util", "//ios/chrome/test/earl_grey:test_support", + "//ios/testing/earl_grey:earl_grey_support", + ] + libs = [ + "UIKit.framework", + "XCTest.framework", + ] +} + +source_set("eg2_tests") { + defines = [ "CHROME_EARL_GREY_2" ] + configs += [ + "//build/config/compiler:enable_arc", + "//build/config/ios:xctest_config", + ] + testonly = true + sources = [ + "page_info_egtest.mm", + ] + deps = [ + ":constants", + "//ios/chrome/browser/ui/popup_menu:constants", + "//ios/chrome/test/earl_grey:eg_test_support+eg2", + "//ios/testing/earl_grey:eg_test_support+eg2", + "//ios/third_party/earl_grey2:test_lib", + "//ui/base", ] libs = [ "UIKit.framework",
diff --git a/ios/chrome/browser/ui/page_info/page_info_egtest.mm b/ios/chrome/browser/ui/page_info/page_info_egtest.mm index 1e8c6fc..57ed9c4 100644 --- a/ios/chrome/browser/ui/page_info/page_info_egtest.mm +++ b/ios/chrome/browser/ui/page_info/page_info_egtest.mm
@@ -2,16 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import <EarlGrey/EarlGrey.h> #import <UIKit/UIKit.h> #import <XCTest/XCTest.h> #import "ios/chrome/browser/ui/page_info/page_info_constants.h" #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h" -#include "ios/chrome/browser/ui/util/ui_util.h" #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" #import "ios/chrome/test/earl_grey/chrome_test_case.h" +#import "ios/testing/earl_grey/earl_grey_test.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -31,8 +30,14 @@ } if ([[UIDevice currentDevice] orientation] != UIDeviceOrientationPortrait) { +#if defined(CHROME_EARL_GREY_1) [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait errorOrNil:nil]; +#elif defined(CHROME_EARL_GREY_2) + [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait error:nil]; +#else +#error Neither CHROME_EARL_GREY_1 nor CHROME_EARL_GREY_2 are defined +#endif } [ChromeEarlGrey loadURL:GURL("https://invalid")]; @@ -50,8 +55,15 @@ kPageInfoViewAccessibilityIdentifier)] assertWithMatcher:grey_sufficientlyVisible()]; +#if defined(CHROME_EARL_GREY_1) [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeRight errorOrNil:nil]; +#elif defined(CHROME_EARL_GREY_2) + [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeRight + error:nil]; +#else +#error Neither CHROME_EARL_GREY_1 nor CHROME_EARL_GREY_2 are defined +#endif // Expect that the page info view has disappeared. [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
diff --git a/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm b/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm index d54a210..dff0702 100644 --- a/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm +++ b/ios/chrome/browser/ui/payments/payment_request_picker_view_controller.mm
@@ -96,7 +96,6 @@ self.tableView.delegate = self; - [self updateTableInset]; self.tableView.estimatedRowHeight = MDCCellDefaultOneLineHeight; self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.accessibilityIdentifier = @@ -133,11 +132,6 @@ [self.appBarViewController didMoveToParentViewController:self]; } -- (void)viewSafeAreaInsetsDidChange { - [super viewSafeAreaInsetsDidChange]; - [self updateTableInset]; -} - - (UIViewController*)childViewControllerForStatusBarHidden { return self.appBarViewController; } @@ -274,26 +268,6 @@ #pragma mark - Private -- (void)updateTableInset { - const bool isFullScreen = - !IsIPadIdiom() || ([self navigationController].modalPresentationStyle != - UIModalPresentationFormSheet); - - if (isFullScreen && [self navigationController].navigationBarHidden) { - // TODO(crbug.com/767428): When shown full screen, the UITableViewController - // uses the full screen even when the status bar is present, but insets the - // section headers by the size of the status bar. This will inset the - // content by the same amount, to ensure they line up properly. Also insets - // by one more pixel to hide the one pixel gap left in between the - // navigation bar and the UITableView. - CGFloat topInset = self.view.safeAreaInsets.top; - const UIEdgeInsets statusBarInset = - UIEdgeInsetsMake(-1 - topInset, 0, 0, 0); - self.tableView.contentInset = statusBarInset; - self.tableView.scrollIndicatorInsets = statusBarInset; - } -} - - (void)onBack { [self.delegate paymentRequestPickerViewControllerDidFinish:self]; }
diff --git a/ios/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/recent_tabs/BUILD.gn index c0ecfa9..e4560dc 100644 --- a/ios/chrome/browser/ui/recent_tabs/BUILD.gn +++ b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -31,6 +31,7 @@ "//ios/chrome/browser/ui/coordinators:chrome_coordinators", "//ios/chrome/browser/ui/ntp", "//ios/chrome/browser/ui/table_view", + "//ios/chrome/browser/ui/table_view:feature_flags", "//ios/chrome/browser/ui/util", "//ios/chrome/browser/url_loading", "//ui/base",
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm index 2775ce36..66c2e75 100644 --- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm +++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm
@@ -12,6 +12,7 @@ #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_presentation_delegate.h" #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h" #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_transitioning_delegate.h" +#import "ios/chrome/browser/ui/table_view/feature_flags.h" #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller.h" #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h" #import "ios/chrome/browser/url_loading/url_loading_params.h" @@ -83,12 +84,29 @@ self.recentTabsNavigationController = [[TableViewNavigationController alloc] initWithTable:recentTabsTableViewController]; self.recentTabsNavigationController.toolbarHidden = YES; - self.recentTabsTransitioningDelegate = - [[RecentTabsTransitioningDelegate alloc] init]; - self.recentTabsNavigationController.transitioningDelegate = - self.recentTabsTransitioningDelegate; - [self.recentTabsNavigationController - setModalPresentationStyle:UIModalPresentationCustom]; + + BOOL useCustomPresentation = YES; + if (IsCollectionsCardPresentationStyleEnabled()) { + if (@available(iOS 13, *)) { +#if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) + [self.recentTabsNavigationController + setModalPresentationStyle:UIModalPresentationFormSheet]; + self.recentTabsNavigationController.presentationController.delegate = + recentTabsTableViewController; + useCustomPresentation = NO; +#endif + } + } + + if (useCustomPresentation) { + self.recentTabsTransitioningDelegate = + [[RecentTabsTransitioningDelegate alloc] init]; + self.recentTabsNavigationController.transitioningDelegate = + self.recentTabsTransitioningDelegate; + [self.recentTabsNavigationController + setModalPresentationStyle:UIModalPresentationCustom]; + } + [self.baseViewController presentViewController:self.recentTabsNavigationController animated:YES
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm index d410a829..3570436 100644 --- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm +++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
@@ -8,6 +8,7 @@ #import <map> #import <string> +#include "base/ios/ios_util.h" #include "base/test/scoped_feature_list.h" #include "components/strings/grit/components_strings.h" #import "ios/chrome/app/main_controller.h" @@ -241,4 +242,32 @@ ->RemoveIdentity(identity); } +// Tests that the VC can be dismissed by swiping down. +- (void)testSwipeDownDismiss { + if (!base::ios::IsRunningOnOrLater(13, 0, 0)) { + EARL_GREY_TEST_SKIPPED(@"Test disabled on iOS 12 and lower."); + } + OpenRecentTabsPanel(); + + // Check that the TableView is presented. + [[EarlGrey selectElementWithMatcher: + grey_accessibilityID( + kRecentTabsTableViewControllerAccessibilityIdentifier)] + assertWithMatcher:grey_notNil()]; + + // Swipe TableView down. + [[EarlGrey selectElementWithMatcher: + grey_accessibilityID( + kRecentTabsTableViewControllerAccessibilityIdentifier)] + performAction:grey_swipeFastInDirection(kGREYDirectionDown)]; + + // Check that the TableView has been dismissed. + [[EarlGrey selectElementWithMatcher: + grey_accessibilityID( + kRecentTabsTableViewControllerAccessibilityIdentifier)] + assertWithMatcher:grey_nil()]; + + [ChromeEarlGrey closeCurrentTab]; +} + @end
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h index 0dc66fb0..1d89ab7 100644 --- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h +++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h
@@ -21,7 +21,8 @@ @protocol TableViewFaviconDataSource; @interface RecentTabsTableViewController - : ChromeTableViewController<RecentTabsConsumer> + : ChromeTableViewController <RecentTabsConsumer, + UIAdaptivePresentationControllerDelegate> // The coordinator's BrowserState. @property(nonatomic, assign) ios::ChromeBrowserState* browserState; // The dispatcher used by this ViewController.
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm index 24c3b26..9bb2bc7 100644 --- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm +++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -1148,6 +1148,13 @@ [self.dispatcher showSignin:command baseViewController:self]; } +#pragma mark - UIAdaptivePresentationControllerDelegate +- (void)presentationControllerDidDismiss: + (UIPresentationController*)presentationController { + // Call dismissRecentTabs so the Coordinator cleans up any state it needs to. + [self.presentationDelegate dismissRecentTabs]; +} + #pragma mark - Accessibility - (BOOL)accessibilityPerformEscape {
diff --git a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm index 9e1bdb0..9c431800 100644 --- a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm +++ b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm
@@ -86,8 +86,10 @@ block:^BOOL { return browsing_data_removed; }]; - GREYAssert([condition waitWithTimeout:base::test::ios::kWaitForActionTimeout], - @"Browsing data was not removed."); + GREYAssert( + [condition + waitWithTimeout:base::test::ios::kWaitForClearBrowsingDataTimeout], + @"Browsing data was not removed."); } } // namespace
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm index c9b6354..7f80b0b8 100644 --- a/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm +++ b/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
@@ -158,11 +158,12 @@ [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()] performAction:grey_tap()]; - // Swipe over to Recent Tabs - [[EarlGrey selectElementWithMatcher:grey_accessibilityID( - kTabGridScrollViewIdentifier)] - performAction:[GREYActions - actionForSwipeFastInDirection:kGREYDirectionLeft]]; + + // Switch over to Recent Tabs. + [[EarlGrey + selectElementWithMatcher:grey_accessibilityID( + kTabGridRemoteTabsPageButtonIdentifier)] + performAction:grey_tap()]; // Tap on "Show History" // Undo is available after close all action.
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm index 72ec411..73f5448b 100644 --- a/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm +++ b/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm
@@ -41,6 +41,7 @@ [super configureHeaderFooterView:headerFooter withStyler:styler]; headerFooter.linkURL = self.linkURL; + headerFooter.accessibilityTraits |= UIAccessibilityTraitLink; [headerFooter setText:self.text]; } @@ -141,4 +142,10 @@ return NO; } +#pragma mark - NSObject(Accessibility) + +- (NSString*)accessibilityLabel { + return [self.textView.attributedText string]; +} + @end
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm index 0bb265dc..dd930b0d 100644 --- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm +++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm
@@ -627,9 +627,7 @@ - (void)testNavigationButtons { // TODO(crbug.com/989550) Disable broken context menu tests on Xc11b5. if (@available(iOS 13, *)) { - if ([ChromeEarlGrey isIPadIdiom]) { - EARL_GREY_TEST_DISABLED(@"Test disabled on iPad."); - } + EARL_GREY_TEST_DISABLED(@"Test disabled on iOS13."); } // Setup the server. self.testServer->RegisterRequestHandler(
diff --git a/ios/chrome/browser/web/tab_order_egtest.mm b/ios/chrome/browser/web/tab_order_egtest.mm index 28f296a..6912e47 100644 --- a/ios/chrome/browser/web/tab_order_egtest.mm +++ b/ios/chrome/browser/web/tab_order_egtest.mm
@@ -55,9 +55,7 @@ - (void)testChildTabOrdering { // TODO(crbug.com/989550) Disable broken context menu tests on Xc11b5. if (@available(iOS 13, *)) { - if ([ChromeEarlGrey isIPadIdiom]) { - EARL_GREY_TEST_DISABLED(@"Test disabled on iPad."); - } + EARL_GREY_TEST_DISABLED(@"Test disabled on iOS13."); } GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
diff --git a/ios/chrome/test/app/history_test_util.mm b/ios/chrome/test/app/history_test_util.mm index 3300fbc31..756f5ca 100644 --- a/ios/chrome/test/app/history_test_util.mm +++ b/ios/chrome/test/app/history_test_util.mm
@@ -27,7 +27,7 @@ did_complete = true; }]; return base::test::ios::WaitUntilConditionOrTimeout( - base::test::ios::kWaitForUIElementTimeout, ^{ + base::test::ios::kWaitForClearBrowsingDataTimeout, ^{ return did_complete; }); }
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm index 6b560d8d..3296fe8 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm +++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
@@ -121,6 +121,21 @@ performAction:grey_tap()]; [[EarlGrey selectElementWithMatcher:chrome_test_util::ClearCacheButton()] performAction:grey_tap()]; + + // Set 'Time Range' to 'All Time'. + [[EarlGrey selectElementWithMatcher: + chrome_test_util::ButtonWithAccessibilityLabelId( + IDS_IOS_CLEAR_BROWSING_DATA_TIME_RANGE_SELECTOR_TITLE)] + performAction:grey_tap()]; + [[EarlGrey + selectElementWithMatcher: + chrome_test_util::ButtonWithAccessibilityLabelId( + IDS_IOS_CLEAR_BROWSING_DATA_TIME_RANGE_OPTION_BEGINNING_OF_TIME)] + performAction:grey_tap()]; + [[[EarlGrey + selectElementWithMatcher:chrome_test_util::SettingsMenuBackButton()] + atIndex:0] performAction:grey_tap()]; + [[EarlGrey selectElementWithMatcher:chrome_test_util::ClearBrowsingDataButton()] performAction:grey_tap()]; @@ -131,6 +146,12 @@ // data has been finished. [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; + // Recheck "Cookies, Site Data" and "Cached Images and Files." + [[EarlGrey selectElementWithMatcher:chrome_test_util::ClearCookiesButton()] + performAction:grey_tap()]; + [[EarlGrey selectElementWithMatcher:chrome_test_util::ClearCacheButton()] + performAction:grey_tap()]; + // Include sufficientlyVisible condition for the case of the clear browsing // dialog, which also has a "Done" button and is displayed over the history // panel.
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn index 40dc58a..93e8845 100644 --- a/ios/chrome/test/earl_grey2/BUILD.gn +++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -44,6 +44,7 @@ "//ios/chrome/browser/ui/omnibox/popup:eg2_tests", "//ios/chrome/browser/ui/omnibox/popup/shortcuts:eg2_tests", "//ios/chrome/browser/ui/open_in:eg2_tests", + "//ios/chrome/browser/ui/page_info:eg2_tests", "//ios/chrome/browser/ui/toolbar:eg2_tests", ] }
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index 561ad69..d673484 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -513,6 +513,10 @@ const base::Feature kMediaEngagementHTTPSOnly{ "MediaEngagementHTTPSOnly", base::FEATURE_DISABLED_BY_DEFAULT}; +// Send events to devtools rather than to chrome://media-internals +const base::Feature kMediaInspectorLogging{"MediaInspectorLogging", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables experimental local learning for media. Adds reporting only; does not // change media behavior. const base::Feature kMediaLearningExperiment{"MediaLearningExperiment",
diff --git a/media/base/media_switches.h b/media/base/media_switches.h index b8137f35..9d8de56 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h
@@ -113,6 +113,7 @@ MEDIA_EXPORT extern const base::Feature kMediaCastOverlayButton; MEDIA_EXPORT extern const base::Feature kMediaEngagementBypassAutoplayPolicies; MEDIA_EXPORT extern const base::Feature kMediaEngagementHTTPSOnly; +MEDIA_EXPORT extern const base::Feature kMediaInspectorLogging; MEDIA_EXPORT extern const base::Feature kMediaLearningExperiment; MEDIA_EXPORT extern const base::Feature kMemoryPressureBasedSourceBufferGC; MEDIA_EXPORT extern const base::Feature kChromeosVideoDecoder;
diff --git a/media/base/simple_watch_timer.cc b/media/base/simple_watch_timer.cc index aa3038fa..83668df 100644 --- a/media/base/simple_watch_timer.cc +++ b/media/base/simple_watch_timer.cc
@@ -5,6 +5,7 @@ #include "media/base/simple_watch_timer.h" #include "base/location.h" +#include "media/base/timestamp_constants.h" namespace media { @@ -43,7 +44,11 @@ void SimpleWatchTimer::Tick() { base::TimeDelta current_time = get_current_time_cb_.Run(); - base::TimeDelta duration = current_time - last_current_time_; + base::TimeDelta duration; + if (last_current_time_ != kNoTimestamp && + last_current_time_ != kInfiniteDuration) { + duration = current_time - last_current_time_; + } last_current_time_ = current_time; // Accumulate watch time if the duration is reasonable.
diff --git a/media/filters/fuchsia/fuchsia_video_decoder.cc b/media/filters/fuchsia/fuchsia_video_decoder.cc index de64a4e9..0ab9a43 100644 --- a/media/filters/fuchsia/fuchsia_video_decoder.cc +++ b/media/filters/fuchsia/fuchsia_video_decoder.cc
@@ -338,7 +338,8 @@ private: // Event handlers for |codec_|. - void OnStreamFailed(uint64_t stream_lifetime_ordinal); + void OnStreamFailed(uint64_t stream_lifetime_ordinal, + fuchsia::media::StreamError error); void OnInputConstraints( fuchsia::media::StreamBufferConstraints input_constraints); void OnFreeInputPacket(fuchsia::media::PacketHeader free_input_packet); @@ -506,7 +507,7 @@ OnError(); }); - codec_.events().OnStreamFailed = + codec_.events().OnStreamFailed2 = fit::bind_member(this, &FuchsiaVideoDecoder::OnStreamFailed); codec_.events().OnInputConstraints = fit::bind_member(this, &FuchsiaVideoDecoder::OnInputConstraints); @@ -578,7 +579,8 @@ return input_buffers_.size() + 1; } -void FuchsiaVideoDecoder::OnStreamFailed(uint64_t stream_lifetime_ordinal) { +void FuchsiaVideoDecoder::OnStreamFailed(uint64_t stream_lifetime_ordinal, + fuchsia::media::StreamError error) { if (stream_lifetime_ordinal_ != stream_lifetime_ordinal) { return; }
diff --git a/net/reporting/reporting_uploader.cc b/net/reporting/reporting_uploader.cc index 3f8acaa..930a49f 100644 --- a/net/reporting/reporting_uploader.cc +++ b/net/reporting/reporting_uploader.cc
@@ -169,9 +169,8 @@ upload->request->set_method("OPTIONS"); - upload->request->SetLoadFlags(LOAD_DISABLE_CACHE | - LOAD_DO_NOT_SAVE_COOKIES | - LOAD_DO_NOT_SEND_COOKIES); + upload->request->SetLoadFlags(LOAD_DISABLE_CACHE); + upload->request->set_allow_credentials(false); upload->request->SetExtraRequestHeaderByName( HttpRequestHeaders::kOrigin, upload->report_origin.Serialize(), true); @@ -201,9 +200,8 @@ upload->request->set_method("POST"); - upload->request->SetLoadFlags(LOAD_DISABLE_CACHE | - LOAD_DO_NOT_SAVE_COOKIES | - LOAD_DO_NOT_SEND_COOKIES); + upload->request->SetLoadFlags(LOAD_DISABLE_CACHE); + upload->request->set_allow_credentials(false); upload->request->SetExtraRequestHeaderByName( HttpRequestHeaders::kContentType, kUploadContentType, true);
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc index 1a203fc..0a73a41 100644 --- a/net/socket/ssl_client_socket_impl.cc +++ b/net/socket/ssl_client_socket_impl.cc
@@ -865,8 +865,7 @@ mode.ConfigureFlag(SSL_MODE_RELEASE_BUFFERS, true); mode.ConfigureFlag(SSL_MODE_CBC_RECORD_SPLITTING, true); - mode.ConfigureFlag(SSL_MODE_ENABLE_FALSE_START, - ssl_config_.false_start_enabled); + mode.ConfigureFlag(SSL_MODE_ENABLE_FALSE_START, true); SSL_set_mode(ssl_.get(), mode.set_mask); SSL_clear_mode(ssl_.get(), mode.clear_mask);
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index 9ed7f43..3120803 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc
@@ -1267,6 +1267,32 @@ } }; +// Sends an HTTP request on the socket and reads the response. This may be used +// to ensure some data has been consumed from the server. +int MakeHTTPRequest(StreamSocket* socket) { + base::StringPiece request = "GET / HTTP/1.0\r\n\r\n"; + TestCompletionCallback callback; + while (!request.empty()) { + auto request_buffer = + base::MakeRefCounted<StringIOBuffer>(request.as_string()); + int rv = callback.GetResult( + socket->Write(request_buffer.get(), request_buffer->size(), + callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS)); + if (rv < 0) { + return rv; + } + request = request.substr(rv); + } + + auto response_buffer = base::MakeRefCounted<IOBuffer>(1024); + int rv = callback.GetResult( + socket->Read(response_buffer.get(), 1024, callback.callback())); + if (rv < 0) { + return rv; + } + return OK; +} + // Provides a response to the 0RTT request indicating whether it was received // as early data. class ZeroRTTResponse : public test_server::HttpResponse { @@ -1384,13 +1410,7 @@ // Use the socket for an HTTP request to ensure we've processed the // post-handshake TLS 1.3 ticket. - constexpr base::StringPiece kRequest = "GET / HTTP/1.0\r\n\r\n"; - if (kRequest.size() != WriteAndWait(kRequest)) - return false; - - scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBuffer>(4096); - if (ReadAndWait(buf.get(), 4096) <= 0) - return false; + EXPECT_THAT(MakeHTTPRequest(ssl_socket_.get()), IsOk()); SSLInfo ssl_info; EXPECT_TRUE(GetSSLInfo(&ssl_info)); @@ -1745,14 +1765,10 @@ int rv = callback.GetResult(transport->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); - // Disable TLS False Start to avoid handshake non-determinism. - SSLConfig ssl_config; - ssl_config.false_start_enabled = false; - SynchronousErrorStreamSocket* raw_transport = transport.get(); std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( std::move(transport), spawned_test_server()->host_port_pair(), - ssl_config)); + SSLConfig())); raw_transport->SetNextWriteError(ERR_CONNECTION_RESET); @@ -1776,14 +1792,10 @@ int rv = callback.GetResult(transport->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); - // Disable TLS False Start to avoid handshake non-determinism. - SSLConfig ssl_config; - ssl_config.false_start_enabled = false; - SynchronousErrorStreamSocket* raw_transport = transport.get(); std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( std::move(transport), spawned_test_server()->host_port_pair(), - ssl_config)); + SSLConfig())); rv = callback.GetResult(sock->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); @@ -1834,13 +1846,9 @@ int rv = callback.GetResult(transport->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); - // Disable TLS False Start to avoid handshake non-determinism. - SSLConfig ssl_config; - ssl_config.false_start_enabled = false; - std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( std::move(transport), spawned_test_server()->host_port_pair(), - ssl_config)); + SSLConfig())); rv = callback.GetResult(sock->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); @@ -1903,13 +1911,9 @@ int rv = callback.GetResult(counting_socket->Connect(callback.callback())); ASSERT_THAT(rv, IsOk()); - // Disable TLS False Start to avoid handshake non-determinism. - SSLConfig ssl_config; - ssl_config.false_start_enabled = false; - std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( std::move(counting_socket), spawned_test_server()->host_port_pair(), - ssl_config)); + SSLConfig())); rv = callback.GetResult(sock->Connect(callback.callback())); ASSERT_THAT(rv, IsOk()); @@ -2008,13 +2012,9 @@ int rv = callback.GetResult(transport->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); - // Disable TLS False Start to avoid handshake non-determinism. - SSLConfig ssl_config; - ssl_config.false_start_enabled = false; - std::unique_ptr<SSLClientSocket> sock = CreateSSLClientSocket( std::move(transport), spawned_test_server()->host_port_pair(), - ssl_config); + SSLConfig()); rv = callback.GetResult(sock->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); @@ -2094,13 +2094,9 @@ int rv = callback.GetResult(transport->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); - // Disable TLS False Start to avoid handshake non-determinism. - SSLConfig ssl_config; - ssl_config.false_start_enabled = false; - std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( std::move(transport), spawned_test_server()->host_port_pair(), - ssl_config)); + SSLConfig())); rv = callback.GetResult(sock->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); @@ -2204,14 +2200,10 @@ int rv = callback.GetResult(transport->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); - // Disable TLS False Start to ensure the handshake has completed. - SSLConfig ssl_config; - ssl_config.false_start_enabled = false; - SynchronousErrorStreamSocket* raw_transport = transport.get(); std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( std::move(transport), spawned_test_server()->host_port_pair(), - ssl_config)); + SSLConfig())); rv = callback.GetResult(sock->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); @@ -2241,13 +2233,9 @@ int rv = callback.GetResult(transport->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); - // Disable TLS False Start to ensure the handshake has completed. - SSLConfig ssl_config; - ssl_config.false_start_enabled = false; - std::unique_ptr<SSLClientSocket> sock(CreateSSLClientSocket( std::move(transport), spawned_test_server()->host_port_pair(), - ssl_config)); + SSLConfig())); rv = callback.GetResult(sock->Connect(callback.callback())); EXPECT_THAT(rv, IsOk()); @@ -3223,8 +3211,6 @@ // First, perform a full handshake. SSLConfig ssl_config; - // Disable TLS False Start to ensure the handshake has completed. - ssl_config.false_start_enabled = false; ssl_config.alpn_protos.push_back(kProtoHTTP2); int rv; ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv)); @@ -3234,6 +3220,10 @@ EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type); EXPECT_EQ(kProtoHTTP2, sock_->GetNegotiatedProtocol()); + // TLS 1.2 with False Start and TLS 1.3 cause the ticket to arrive later, so + // use the socket to ensure the session ticket has been picked up. + EXPECT_THAT(MakeHTTPRequest(sock_.get()), IsOk()); + // The next connection should resume; ALPN should be renegotiated. ssl_config.alpn_protos.clear(); ssl_config.alpn_protos.push_back(kProtoHTTP11); @@ -5766,23 +5756,9 @@ histograms.ExpectUniqueSample("Net.SSLHandshakeDetails", GetParam().expected_initial, 1); - // Use the socket to ensure the session ticket has been picked up. - base::StringPiece request = "GET / HTTP/1.0\r\n\r\n"; - TestCompletionCallback callback; - while (!request.empty()) { - auto request_buffer = - base::MakeRefCounted<StringIOBuffer>(request.as_string()); - rv = callback.GetResult( - sock_->Write(request_buffer.get(), request_buffer->size(), - callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS)); - ASSERT_GT(rv, 0); - request = request.substr(rv); - } - - auto response_buffer = base::MakeRefCounted<IOBuffer>(1024); - rv = callback.GetResult( - sock_->Read(response_buffer.get(), 1024, callback.callback())); - ASSERT_GT(rv, 0); + // TLS 1.2 with False Start and TLS 1.3 cause the ticket to arrive later, so + // use the socket to ensure the session ticket has been picked up. + EXPECT_THAT(MakeHTTPRequest(sock_.get()), IsOk()); } // Make a resumption connection.
diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc index 274ae98..adc2664 100644 --- a/net/socket/ssl_server_socket_unittest.cc +++ b/net/socket/ssl_server_socket_unittest.cc
@@ -380,8 +380,6 @@ ASSERT_TRUE(key); server_ssl_private_key_ = WrapOpenSSLPrivateKey(bssl::UpRef(key->key())); - client_ssl_config_.false_start_enabled = false; - // Certificate provided by the host doesn't need authority. client_ssl_config_.allowed_bad_certs.emplace_back( server_cert_, CERT_STATUS_AUTHORITY_INVALID);
diff --git a/net/ssl/ssl_config.cc b/net/ssl/ssl_config.cc index fbd71b01..3178d017 100644 --- a/net/ssl/ssl_config.cc +++ b/net/ssl/ssl_config.cc
@@ -23,7 +23,6 @@ SSLConfig::SSLConfig() : early_data_enabled(false), - false_start_enabled(true), require_ecdhe(false), ignore_certificate_errors(false), disable_cert_verification_network_fetches(false),
diff --git a/net/ssl/ssl_config.h b/net/ssl/ssl_config.h index 4be4323..11f360b 100644 --- a/net/ssl/ssl_config.h +++ b/net/ssl/ssl_config.h
@@ -75,8 +75,6 @@ // If unsure, do not enable this option. bool early_data_enabled; - bool false_start_enabled; // True if we'll use TLS False Start. - // If true, causes only ECDHE cipher suites to be enabled. bool require_ecdhe;
diff --git a/remoting/base/chromium_url_request.cc b/remoting/base/chromium_url_request.cc index 2ae424c..842d267 100644 --- a/remoting/base/chromium_url_request.cc +++ b/remoting/base/chromium_url_request.cc
@@ -33,8 +33,7 @@ resource_request_ = std::make_unique<network::ResourceRequest>(); resource_request_->url = GURL(url); resource_request_->method = request_type; - resource_request_->load_flags = - net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES; + resource_request_->credentials_mode = network::mojom::CredentialsMode::kOmit; resource_request_->referrer = GURL("https://chrome.google.com/remotedesktop"); }
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc index ebda635..7ca16c3 100644 --- a/services/network/cors/cors_url_loader_factory.cc +++ b/services/network/cors/cors_url_loader_factory.cc
@@ -36,6 +36,7 @@ const OriginAccessList* origin_access_list, std::unique_ptr<mojom::URLLoaderFactory> network_loader_factory_for_testing) : context_(context), + is_trusted_(params->is_trusted), disable_web_security_(params->disable_web_security), process_id_(params->process_id), request_initiator_site_lock_(params->request_initiator_site_lock), @@ -142,6 +143,14 @@ return false; } + // Reject request with trusted params if factory is not for a trusted + // consumer. + if (request.trusted_params && !is_trusted_) { + mojo::ReportBadMessage( + "CorsURLLoaderFactory: Untrusted caller making trusted request"); + return false; + } + // Ensure that renderer requests are covered either by CORS or CORB. if (process_id_ != mojom::kBrowserProcessId) { switch (request.mode) {
diff --git a/services/network/cors/cors_url_loader_factory.h b/services/network/cors/cors_url_loader_factory.h index 89616115..4ad1e408 100644 --- a/services/network/cors/cors_url_loader_factory.h +++ b/services/network/cors/cors_url_loader_factory.h
@@ -77,6 +77,9 @@ NetworkContext* const context_ = nullptr; scoped_refptr<ResourceSchedulerClient> resource_scheduler_client_; + // If false, ResourceRequests cannot have their |trusted_params| fields set. + bool is_trusted_; + // Retained from URLLoaderFactoryParams: const bool disable_web_security_; const uint32_t process_id_ = mojom::kInvalidProcessId;
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc index 95962de2..6bc0af7 100644 --- a/services/network/cors/cors_url_loader_unittest.cc +++ b/services/network/cors/cors_url_loader_unittest.cc
@@ -183,10 +183,11 @@ } void CreateLoaderAndStart(const ResourceRequest& request) { + test_cors_loader_client_ = std::make_unique<TestURLLoaderClient>(); cors_url_loader_factory_->CreateLoaderAndStart( mojo::MakeRequest(&url_loader_), 0 /* routing_id */, 0 /* request_id */, mojom::kURLLoadOptionNone, request, - test_cors_loader_client_.CreateInterfacePtr(), + test_cors_loader_client_->CreateInterfacePtr(), net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); } @@ -254,9 +255,11 @@ return test_url_loader_factory_->num_created_loaders(); } - const TestURLLoaderClient& client() const { return test_cors_loader_client_; } + const TestURLLoaderClient& client() const { + return *test_cors_loader_client_; + } void ClearHasReceivedRedirect() { - test_cors_loader_client_.ClearHasReceivedRedirect(); + test_cors_loader_client_->ClearHasReceivedRedirect(); } void RunUntilCreateLoaderAndStartCalled() { @@ -266,9 +269,9 @@ run_loop.Run(); test_url_loader_factory_->SetOnCreateLoaderAndStart({}); } - void RunUntilComplete() { test_cors_loader_client_.RunUntilComplete(); } + void RunUntilComplete() { test_cors_loader_client_->RunUntilComplete(); } void RunUntilRedirectReceived() { - test_cors_loader_client_.RunUntilRedirectReceived(); + test_cors_loader_client_->RunUntilRedirectReceived(); } void AddAllowListEntryForOrigin(const url::Origin& source_origin, @@ -319,11 +322,11 @@ } void ResetFactory(base::Optional<url::Origin> initiator, - uint32_t process_id) { + uint32_t process_id, + bool is_trusted = false) { std::unique_ptr<TestURLLoaderFactory> factory = std::make_unique<TestURLLoaderFactory>(); test_url_loader_factory_ = factory->GetWeakPtr(); - auto factory_params = network::mojom::URLLoaderFactoryParams::New(); if (initiator) { factory_params->request_initiator_site_lock = *initiator; @@ -332,6 +335,7 @@ cloned_patterns.push_back(item.Clone()); factory_params->factory_bound_allow_patterns = std::move(cloned_patterns); } + factory_params->is_trusted = is_trusted; factory_params->process_id = process_id; factory_params->is_corb_enabled = (process_id != mojom::kBrowserProcessId); constexpr int kRouteId = 765; @@ -372,7 +376,7 @@ mojom::URLLoaderPtr url_loader_; // TestURLLoaderClient that records callback activities. - TestURLLoaderClient test_cors_loader_client_; + std::unique_ptr<TestURLLoaderClient> test_cors_loader_client_; // Holds for allowed origin access lists. OriginAccessList origin_access_list_; @@ -1774,6 +1778,54 @@ "CorsURLLoaderFactory: unsupported credentials mode on navigation")); } +// Make sure than when a request is failed due to having |trusted_params| set +// and being sent to an untrusted URLLoaderFactory, no CORS request is made. +TEST_F(CorsURLLoaderTest, TrustedParamsWithUntrustedFactoryFailsBeforeCORS) { + // Run the test with a trusted URLLoaderFactory as well, to make sure a CORS + // request is in fact made when using a trusted factory. + for (bool is_trusted : {false, true}) { + ResetFactory(base::nullopt, kRendererProcessId, is_trusted); + + BadMessageTestHelper bad_message_helper; + + ResourceRequest request; + request.mode = mojom::RequestMode::kCors; + request.credentials_mode = mojom::CredentialsMode::kOmit; + request.method = net::HttpRequestHeaders::kGetMethod; + request.url = GURL("http://other.com/foo.png"); + request.request_initiator = url::Origin::Create(GURL("http://example.com")); + request.trusted_params = ResourceRequest::TrustedParams(); + CreateLoaderAndStart(request); + + if (!is_trusted) { + RunUntilComplete(); + EXPECT_FALSE(IsNetworkLoaderStarted()); + EXPECT_FALSE(client().has_received_redirect()); + EXPECT_FALSE(client().has_received_response()); + EXPECT_TRUE(client().has_received_completion()); + EXPECT_EQ(net::ERR_INVALID_ARGUMENT, + client().completion_status().error_code); + EXPECT_THAT( + bad_message_helper.bad_message_reports(), + ::testing::ElementsAre( + "CorsURLLoaderFactory: Untrusted caller making trusted request")); + } else { + NotifyLoaderClientOnReceiveResponse( + {"Access-Control-Allow-Origin: http://example.com"}); + NotifyLoaderClientOnComplete(net::OK); + + RunUntilComplete(); + + EXPECT_TRUE(IsNetworkLoaderStarted()); + EXPECT_TRUE(client().has_received_response()); + EXPECT_TRUE(client().has_received_completion()); + EXPECT_EQ(net::OK, client().completion_status().error_code); + EXPECT_TRUE( + GetRequest().headers.HasHeader(net::HttpRequestHeaders::kOrigin)); + } + } +} + } // namespace } // namespace cors
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc index 528efc0..f794703 100644 --- a/services/network/network_context_unittest.cc +++ b/services/network/network_context_unittest.cc
@@ -3432,6 +3432,11 @@ std::move(on_done_accepting_connections_).Run(); } + int GetTotalSocketsSeen() const { + base::AutoLock lock(lock_); + return total_sockets_seen_; + } + private: static uint16_t GetPort(const net::StreamSocket& connection) { // Get the remote port of the peer, since the local port will always be the @@ -3756,6 +3761,59 @@ } } +// Test that only trusted URLLoaderFactories accept +// ResourceRequest::trusted_params. +TEST_F(NetworkContextTest, TrustedParams) { + for (bool trusted_factory : {false, true}) { + ConnectionListener connection_listener; + net::EmbeddedTestServer test_server; + test_server.AddDefaultHandlers( + base::FilePath(FILE_PATH_LITERAL("services/test/data"))); + test_server.SetConnectionListener(&connection_listener); + ASSERT_TRUE(test_server.Start()); + + std::unique_ptr<NetworkContext> network_context = + CreateContextWithParams(CreateContextParams()); + + mojom::URLLoaderFactoryPtr loader_factory; + mojom::URLLoaderFactoryParamsPtr params = + mojom::URLLoaderFactoryParams::New(); + params->process_id = mojom::kBrowserProcessId; + params->is_corb_enabled = false; + // URLLoaderFactories should not be trusted by default. + EXPECT_FALSE(params->is_trusted); + params->is_trusted = trusted_factory; + network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory), + std::move(params)); + + ResourceRequest request; + request.url = test_server.GetURL("/echo"); + request.trusted_params = ResourceRequest::TrustedParams(); + mojom::URLLoaderPtr loader; + TestURLLoaderClient client; + loader_factory->CreateLoaderAndStart( + mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */, + 0 /* options */, request, client.CreateInterfacePtr(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + + client.RunUntilComplete(); + + // If the factory was trusted, the request should have succeeded. Otherwise, + // it should have failed. + EXPECT_EQ(trusted_factory, client.has_received_response()); + + if (trusted_factory) { + EXPECT_THAT(client.completion_status().error_code, net::test::IsOk()); + EXPECT_EQ(1, connection_listener.GetTotalSocketsSeen()); + } else { + EXPECT_THAT(client.completion_status().error_code, + net::test::IsError(net::ERR_INVALID_ARGUMENT)); + // No connection should have been made to the test server. + EXPECT_EQ(0, connection_listener.GetTotalSocketsSeen()); + } + } +} + #if BUILDFLAG(IS_CT_SUPPORTED) TEST_F(NetworkContextTest, ExpectCT) { std::unique_ptr<NetworkContext> network_context = @@ -5731,16 +5789,22 @@ base::Optional<GURL> new_url = base::nullopt) { ResourceRequest request = CreateResourceRequest("GET", url); request.load_flags |= net::LOAD_SKIP_CACHE_VALIDATION; - request.update_network_isolation_key_on_redirect = - update_network_isolation_key_on_redirect; mojom::URLLoaderFactoryPtr loader_factory; auto params = mojom::URLLoaderFactoryParams::New(); params->process_id = mojom::kBrowserProcessId; params->is_corb_enabled = false; if (is_navigation) { - request.trusted_network_isolation_key = key; + request.trusted_params = ResourceRequest::TrustedParams(); + request.trusted_params->network_isolation_key = key; + request.trusted_params->update_network_isolation_key_on_redirect = + update_network_isolation_key_on_redirect; + params->is_trusted = true; } else { + // Different |update_network_isolation_key_on_redirect| values may only be + // set for navigations. + DCHECK_EQ(mojom::UpdateNetworkIsolationKeyOnRedirect::kDoNotUpdate, + update_network_isolation_key_on_redirect); params->network_isolation_key = key; } network_context_->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc index 29d598bd..9ce60f78 100644 --- a/services/network/public/cpp/resource_request.cc +++ b/services/network/public/cpp/resource_request.cc
@@ -8,6 +8,16 @@ namespace network { +ResourceRequest::TrustedParams::TrustedParams() = default; +ResourceRequest::TrustedParams::~TrustedParams() = default; + +bool ResourceRequest::TrustedParams::operator==( + const TrustedParams& other) const { + return network_isolation_key == other.network_isolation_key && + update_network_isolation_key_on_redirect == + other.update_network_isolation_key_on_redirect; +} + ResourceRequest::ResourceRequest() {} ResourceRequest::ResourceRequest(const ResourceRequest& request) = default; ResourceRequest::~ResourceRequest() {} @@ -16,10 +26,6 @@ return method == request.method && url == request.url && site_for_cookies == request.site_for_cookies && top_frame_origin == request.top_frame_origin && - trusted_network_isolation_key == - request.trusted_network_isolation_key && - update_network_isolation_key_on_redirect == - request.update_network_isolation_key_on_redirect && attach_same_site_cookies == request.attach_same_site_cookies && update_first_party_url_on_redirect == request.update_first_party_url_on_redirect && @@ -70,7 +76,8 @@ devtools_request_id == request.devtools_request_id && is_signed_exchange_prefetch_cache_enabled == request.is_signed_exchange_prefetch_cache_enabled && - obey_origin_policy == request.obey_origin_policy; + obey_origin_policy == request.obey_origin_policy && + trusted_params == trusted_params; } bool ResourceRequest::SendsCookies() const {
diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h index d3d2a026..0ad6712 100644 --- a/services/network/public/cpp/resource_request.h +++ b/services/network/public/cpp/resource_request.h
@@ -29,6 +29,23 @@ // Note: Please revise EqualsForTesting accordingly on any updates to this // struct. struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { + // Typemapped to network.mojom::TrustedUrlRequestParams, see comments there + // for details of each field. + // + // TODO(mmenke): There are likely other fields that should be moved into this + // class. + struct COMPONENT_EXPORT(NETWORK_CPP_BASE) TrustedParams { + TrustedParams(); + ~TrustedParams(); + + bool operator==(const TrustedParams& other) const; + + net::NetworkIsolationKey network_isolation_key; + mojom::UpdateNetworkIsolationKeyOnRedirect + update_network_isolation_key_on_redirect = + network::mojom::UpdateNetworkIsolationKeyOnRedirect::kDoNotUpdate; + }; + ResourceRequest(); ResourceRequest(const ResourceRequest& request); ~ResourceRequest(); @@ -41,10 +58,6 @@ GURL url; GURL site_for_cookies; base::Optional<url::Origin> top_frame_origin; - net::NetworkIsolationKey trusted_network_isolation_key; - mojom::UpdateNetworkIsolationKeyOnRedirect - update_network_isolation_key_on_redirect = - network::mojom::UpdateNetworkIsolationKeyOnRedirect::kDoNotUpdate; bool attach_same_site_cookies = false; bool update_first_party_url_on_redirect = false; base::Optional<url::Origin> request_initiator; @@ -91,6 +104,8 @@ base::Optional<std::string> devtools_request_id; bool is_signed_exchange_prefetch_cache_enabled = false; bool obey_origin_policy = false; + + base::Optional<TrustedParams> trusted_params; }; } // namespace network
diff --git a/services/network/public/cpp/url_request.typemap b/services/network/public/cpp/url_request.typemap index c31e3eb9..7a1b6ede 100644 --- a/services/network/public/cpp/url_request.typemap +++ b/services/network/public/cpp/url_request.typemap
@@ -21,6 +21,7 @@ "//services/network/public/cpp:cpp_base", ] type_mappings = [ + "network.mojom.TrustedUrlRequestParams=network::ResourceRequest::TrustedParams", "network.mojom.URLRequest=network::ResourceRequest", "network.mojom.URLRequestBody=scoped_refptr<network::ResourceRequestBody>[nullable_is_same_type,copyable_pass_by_value]", "network.mojom.URLRequestReferrerPolicy=net::URLRequest::ReferrerPolicy",
diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc index ff757b8..a4bca43 100644 --- a/services/network/public/cpp/url_request_mojom_traits.cc +++ b/services/network/public/cpp/url_request_mojom_traits.cc
@@ -150,6 +150,17 @@ return false; } +bool StructTraits<network::mojom::TrustedUrlRequestParamsDataView, + network::ResourceRequest::TrustedParams>:: + Read(network::mojom::TrustedUrlRequestParamsDataView data, + network::ResourceRequest::TrustedParams* out) { + if (!data.ReadNetworkIsolationKey(&out->network_isolation_key)) + return false; + out->update_network_isolation_key_on_redirect = + data.update_network_isolation_key_on_redirect(); + return true; +} + bool StructTraits< network::mojom::URLRequestDataView, network::ResourceRequest>::Read(network::mojom::URLRequestDataView data, @@ -157,8 +168,7 @@ if (!data.ReadMethod(&out->method) || !data.ReadUrl(&out->url) || !data.ReadSiteForCookies(&out->site_for_cookies) || !data.ReadTopFrameOrigin(&out->top_frame_origin) || - !data.ReadTrustedNetworkIsolationKey( - &out->trusted_network_isolation_key) || + !data.ReadTrustedParams(&out->trusted_params) || !data.ReadRequestInitiator(&out->request_initiator) || !data.ReadReferrer(&out->referrer) || !data.ReadReferrerPolicy(&out->referrer_policy) || @@ -181,8 +191,6 @@ return false; } - out->update_network_isolation_key_on_redirect = - data.update_network_isolation_key_on_redirect(); out->attach_same_site_cookies = data.attach_same_site_cookies(); out->update_first_party_url_on_redirect = data.update_first_party_url_on_redirect();
diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h index 482291a..15d5031 100644 --- a/services/network/public/cpp/url_request_mojom_traits.h +++ b/services/network/public/cpp/url_request_mojom_traits.h
@@ -46,6 +46,24 @@ template <> struct COMPONENT_EXPORT(NETWORK_CPP_BASE) + StructTraits<network::mojom::TrustedUrlRequestParamsDataView, + network::ResourceRequest::TrustedParams> { + static const net::NetworkIsolationKey& network_isolation_key( + const network::ResourceRequest::TrustedParams& trusted_params) { + return trusted_params.network_isolation_key; + } + static network::mojom::UpdateNetworkIsolationKeyOnRedirect + update_network_isolation_key_on_redirect( + const network::ResourceRequest::TrustedParams& trusted_params) { + return trusted_params.update_network_isolation_key_on_redirect; + } + + static bool Read(network::mojom::TrustedUrlRequestParamsDataView data, + network::ResourceRequest::TrustedParams* out); +}; + +template <> +struct COMPONENT_EXPORT(NETWORK_CPP_BASE) StructTraits<network::mojom::URLRequestDataView, network::ResourceRequest> { static const std::string& method(const network::ResourceRequest& request) { return request.method; @@ -60,15 +78,6 @@ const network::ResourceRequest& request) { return request.top_frame_origin; } - static network::mojom::UpdateNetworkIsolationKeyOnRedirect - update_network_isolation_key_on_redirect( - const network::ResourceRequest& request) { - return request.update_network_isolation_key_on_redirect; - } - static const net::NetworkIsolationKey& trusted_network_isolation_key( - const network::ResourceRequest& request) { - return request.trusted_network_isolation_key; - } static bool attach_same_site_cookies( const network::ResourceRequest& request) { return request.attach_same_site_cookies; @@ -224,6 +233,10 @@ static bool obey_origin_policy(const network::ResourceRequest& request) { return request.obey_origin_policy; } + static const base::Optional<network::ResourceRequest::TrustedParams>& + trusted_params(const network::ResourceRequest& request) { + return request.trusted_params; + } static bool Read(network::mojom::URLRequestDataView data, network::ResourceRequest* out);
diff --git a/services/network/public/cpp/url_request_mojom_traits_unittest.cc b/services/network/public/cpp/url_request_mojom_traits_unittest.cc index cc4b846..32ce01b 100644 --- a/services/network/public/cpp/url_request_mojom_traits_unittest.cc +++ b/services/network/public/cpp/url_request_mojom_traits_unittest.cc
@@ -49,12 +49,7 @@ original.url = GURL("https://example.com/resources/dummy.xml"); original.site_for_cookies = GURL("https://example.com/index.html"); url::Origin origin = url::Origin::Create(original.url); - ; original.top_frame_origin = origin; - original.trusted_network_isolation_key = - net::NetworkIsolationKey(origin, origin); - original.update_network_isolation_key_on_redirect = network::mojom:: - UpdateNetworkIsolationKeyOnRedirect::kUpdateTopFrameAndFrameOrigin; original.attach_same_site_cookies = true; original.update_first_party_url_on_redirect = false; original.request_initiator = url::Origin::Create(original.url); @@ -94,6 +89,12 @@ original.custom_proxy_post_cache_headers.SetHeader("post_y", "y_value"); original.fetch_window_id = base::UnguessableToken::Create(); + original.trusted_params = ResourceRequest::TrustedParams(); + original.trusted_params->network_isolation_key = + net::NetworkIsolationKey(origin, origin); + original.trusted_params->update_network_isolation_key_on_redirect = network:: + mojom::UpdateNetworkIsolationKeyOnRedirect::kUpdateTopFrameAndFrameOrigin; + network::ResourceRequest copied; EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::URLRequest>(&original, &copied));
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom index ecccb54c..afa8aaf 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom
@@ -507,6 +507,11 @@ // Key used to isolate shared network resources like the cache. NetworkIsolationKey? network_isolation_key; + + // True if this is for exclusive use by a trusted consumer. Only trusted + // consumers can issue requests with ResourceRequest::trusted_params + // populated. + bool is_trusted; }; // Callback interface for NetworkContext when routing identifiers aren't
diff --git a/services/network/public/mojom/url_loader.mojom b/services/network/public/mojom/url_loader.mojom index 2152f55..d839a35 100644 --- a/services/network/public/mojom/url_loader.mojom +++ b/services/network/public/mojom/url_loader.mojom
@@ -80,12 +80,24 @@ // the top frame origin and frame origin. kUpdateTopFrameAndFrameOrigin, - // The updated network isolation key will take existing - // |trusted_network_isolation_key|'s top frame origin and redirected url's - // origin as the frame origin. + // The updated network isolation key will take the existing + // NetworkIsolationKey's top frame origin and redirected url's origin as the + // frame origin. kUpdateFrameOrigin }; +// Options that may only be set on URLRequests passed to a URLLoaderFactory +// created with |is_trusted| set to true. +struct TrustedUrlRequestParams { + // This resource request will be keyed using |network_isolation_key| for + // accessing shared network resources like the http cache. + NetworkIsolationKey? network_isolation_key; + + // Whether or not the network isolation key needs to be recomputed on + // redirects. Typically this is only done for navigations. + UpdateNetworkIsolationKeyOnRedirect update_network_isolation_key_on_redirect; +}; + // Typemapped to network::ResourceRequest. struct URLRequest { // The request method: GET, POST, etc. @@ -121,18 +133,6 @@ // removed at some point. url.mojom.Origin? top_frame_origin; - // This resource request will be keyed using |trusted_network_isolation_key| - // for accessing shared network resources like the http cache. This must only - // be populated from a trusted process. Optional since it is not filled for - // all requests e.g. those coming from a less trusted process. This parameter - // may only be set if the URLLoaderFactory was not created with a pre-bound - // network isolation key, and it is an error to do otherwise. - NetworkIsolationKey? trusted_network_isolation_key; - - // Whether or not the network isolation key needs to be recomputed on - // redirects. Typically this is only done for navigations. - UpdateNetworkIsolationKeyOnRedirect update_network_isolation_key_on_redirect; - // Boolean indicating whether SameSite cookies are allowed to be attached // to the request. It should be used as additional input to network side // checks. @@ -345,6 +345,9 @@ // policy, if necessary, and attach it to the ResourceResponseHead. // Spec: https://wicg.github.io/origin-policy/ bool obey_origin_policy; + + // Setting these from an untrusted URLLoader will cause the request to fail. + TrustedUrlRequestParams? trusted_params; }; // URLRequestBody represents body (i.e. upload data) of a HTTP request.
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index 113225b..6efc3537 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc
@@ -382,7 +382,9 @@ request.custom_proxy_use_alternate_proxy_list), fetch_window_id_(request.fetch_window_id), update_network_isolation_key_on_redirect_( - request.update_network_isolation_key_on_redirect), + request.trusted_params + ? request.trusted_params->update_network_isolation_key_on_redirect + : mojom::UpdateNetworkIsolationKeyOnRedirect::kDoNotUpdate), origin_policy_manager_(nullptr) { DCHECK(delete_callback_); DCHECK(factory_params_); @@ -411,17 +413,13 @@ url_request_->set_referrer_policy(request.referrer_policy); url_request_->set_upgrade_if_insecure(request.upgrade_if_insecure); - // Populate network isolation key from the factory params or from the resource - // request for navigation resources. - if (!request.trusted_network_isolation_key.IsEmpty()) { - DCHECK(!factory_params_->network_isolation_key); - url_request_->set_network_isolation_key( - request.trusted_network_isolation_key); - } if (factory_params_->network_isolation_key) { - DCHECK(request.trusted_network_isolation_key.IsEmpty()); url_request_->set_network_isolation_key( factory_params_->network_isolation_key.value()); + } else if (request.trusted_params && + !request.trusted_params->network_isolation_key.IsEmpty()) { + url_request_->set_network_isolation_key( + request.trusted_params->network_isolation_key); } // |cors_excempt_headers| must be merged here to avoid breaking CORS checks.
diff --git a/services/network/url_loader_factory.cc b/services/network/url_loader_factory.cc index d2e68cc..2e9e147d 100644 --- a/services/network/url_loader_factory.cc +++ b/services/network/url_loader_factory.cc
@@ -71,6 +71,10 @@ const ResourceRequest& url_request, mojom::URLLoaderClientPtr client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { + // Requests with |trusted_params| when params_->is_trusted is not set should + // have been rejected at the CorsURLLoader layer. + DCHECK(!url_request.trusted_params || params_->is_trusted); + std::string origin_string; bool has_origin = url_request.headers.GetHeader("Origin", &origin_string) && origin_string != "null"; @@ -132,21 +136,6 @@ return; } - // For navigation and worker script resources, the network isolation key may - // be set in the resource request and for sub-resources, it is set in params_. - // Fail if it is set in both as that could imply a compromised less trusted - // process filling up the resource request's trusted_network_isolation_key (It - // should only be filled up by a trusted process). - if (params_->network_isolation_key && - !url_request.trusted_network_isolation_key.IsEmpty()) { - URLLoaderCompletionStatus status; - status.error_code = net::ERR_INVALID_ARGUMENT; - status.exists_in_cache = false; - status.completion_time = base::TimeTicks::Now(); - client->OnComplete(status); - return; - } - auto loader = std::make_unique<URLLoader>( context_->url_request_context(), network_service_client, context_->client(),
diff --git a/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc b/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc index 9fd9ee8..230a254 100644 --- a/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc +++ b/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc
@@ -11,7 +11,6 @@ #include "base/test/trace_event_analyzer.h" #include "base/threading/platform_thread.h" #include "base/time/time.h" -#include "base/time/time_override.h" #include "base/trace_event/memory_dump_request_args.h" #include "build/build_config.h" #include "mojo/public/cpp/bindings/binding.h" @@ -148,13 +147,12 @@ base::TimeDelta::FromMilliseconds(5)); } - private: + protected: std::unique_ptr<NiceMock<FakeCoordinatorImpl>> coordinator_; - // Do not start a ThreadPool as we are overriding global time with - // ScopedTimeClockOverrides and that might lead to races (as worker threads - // try to access base::TimeTicks::Now()) + base::test::ScopedTaskEnvironment scoped_task_environment_{ - base::test::ScopedTaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY}; + base::test::ScopedTaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, + base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME}; }; class MockClientProcess : public mojom::ClientProcess { @@ -321,16 +319,10 @@ TEST_F(CoordinatorImplTest, QueuedRequest) { base::RunLoop run_loop; - // Override TimeTicks::Now with a timer that has extra_time added. // This variable to be static as the lambda below has to convert to a function // pointer rather than a functor. - static base::TimeDelta extra_time; - base::subtle::ScopedTimeClockOverrides time_override( - nullptr, - []() { - return base::subtle::TimeTicksNowIgnoringOverride() + extra_time; - }, - nullptr); + static base::test::ScopedTaskEnvironment* task_environment = nullptr; + task_environment = &scoped_task_environment_; NiceMock<MockClientProcess> client_process_1(this, 1, mojom::ProcessType::BROWSER); @@ -345,7 +337,8 @@ MockClientProcess::RequestChromeMemoryDumpCallback& callback) { // Skip the wall clock time-ticks forward to make sure start_time // is strictly increasing. - extra_time += base::TimeDelta::FromMilliseconds(10); + task_environment->FastForwardBy( + base::TimeDelta::FromMilliseconds(10)); MemoryDumpArgs dump_args{MemoryDumpLevelOfDetail::DETAILED}; auto pmd = std::make_unique<ProcessMemoryDump>(dump_args); std::move(callback).Run(true, args.dump_guid, std::move(pmd)); @@ -360,7 +353,7 @@ base::TimeTicks first_dump_time; EXPECT_CALL(callback1, OnCall(true, NotNull())) .WillOnce(Invoke([&](bool success, GlobalMemoryDump* global_dump) { - EXPECT_LT(before, global_dump->start_time); + EXPECT_LE(before, global_dump->start_time); first_dump_time = global_dump->start_time; })); EXPECT_CALL(callback2, OnCall(true, NotNull()))
diff --git a/services/service_manager/public/cpp/binder_map.h b/services/service_manager/public/cpp/binder_map.h index eb9c717..28f4315 100644 --- a/services/service_manager/public/cpp/binder_map.h +++ b/services/service_manager/public/cpp/binder_map.h
@@ -51,12 +51,6 @@ BinderMapWithContext() = default; ~BinderMapWithContext() = default; - // Adds a new generic binder to this map. A generic binder takes an interface - // name, a receiver pipe, and (if ContextType is non-void) a context value. - void Add(const std::string& interface_name, GenericBinderType binder) { - binders_[interface_name] = std::move(binder); - } - // Adds a new binder specifically for Interface receivers. This exists for the // convenience of being able to register strongly-typed binding methods like: // @@ -64,8 +58,11 @@ // // more easily. template <typename Interface> - void Add(BinderType<Interface> binder) { - binders_[Interface::Name_] = Traits::MakeGenericBinder(std::move(binder)); + void Add(BinderType<Interface> binder, + scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) { + binders_[Interface::Name_] = std::make_unique< + internal::GenericCallbackBinderWithContext<ContextType>>( + Traits::MakeGenericBinder(std::move(binder)), std::move(task_runner)); } // Passes |*receiver_pipe| to a registered binder for |interface_name| if any @@ -81,7 +78,7 @@ if (it == binders_.end()) return false; - it->second.Run(std::move(*receiver_pipe)); + it->second->BindInterface(std::move(*receiver_pipe)); return true; } @@ -97,7 +94,7 @@ if (it == binders_.end()) return false; - it->second.Run(std::move(context), std::move(*receiver_pipe)); + it->second->BindInterface(std::move(context), std::move(*receiver_pipe)); return true; } @@ -105,7 +102,10 @@ void Clear() { binders_.clear(); } private: - std::map<std::string, GenericBinderType> binders_; + std::map< + std::string, + std::unique_ptr<internal::GenericCallbackBinderWithContext<ContextType>>> + binders_; DISALLOW_COPY_AND_ASSIGN(BinderMapWithContext); };
diff --git a/services/service_manager/public/cpp/binder_map_internal.h b/services/service_manager/public/cpp/binder_map_internal.h index 7a72e0c6..a8265bd 100644 --- a/services/service_manager/public/cpp/binder_map_internal.h +++ b/services/service_manager/public/cpp/binder_map_internal.h
@@ -70,6 +70,62 @@ } }; +template <typename ContextType> +class GenericCallbackBinderWithContext { + public: + using Traits = BinderContextTraits<ContextType>; + using ContextValueType = typename Traits::ValueType; + using GenericBinderType = typename Traits::GenericBinderType; + + GenericCallbackBinderWithContext( + GenericBinderType callback, + scoped_refptr<base::SequencedTaskRunner> task_runner) + : callback_(std::move(callback)), task_runner_(std::move(task_runner)) {} + + ~GenericCallbackBinderWithContext() = default; + + void BindInterface(ContextValueType context, + mojo::ScopedMessagePipeHandle receiver_pipe) { + if (task_runner_) { + task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &GenericCallbackBinderWithContext::RunCallbackWithContext, + callback_, std::move(context), std::move(receiver_pipe))); + return; + } + RunCallbackWithContext(callback_, std::move(context), + std::move(receiver_pipe)); + } + + void BindInterface(mojo::ScopedMessagePipeHandle receiver_pipe) { + if (task_runner_) { + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&GenericCallbackBinderWithContext::RunCallback, + callback_, std::move(receiver_pipe))); + return; + } + RunCallback(callback_, std::move(receiver_pipe)); + } + + private: + static void RunCallbackWithContext(const GenericBinderType& callback, + ContextValueType context, + mojo::ScopedMessagePipeHandle handle) { + callback.Run(std::move(context), std::move(handle)); + } + + static void RunCallback(const GenericBinderType& callback, + mojo::ScopedMessagePipeHandle handle) { + callback.Run(std::move(handle)); + } + + const GenericBinderType callback_; + const scoped_refptr<base::SequencedTaskRunner> task_runner_; + DISALLOW_COPY_AND_ASSIGN(GenericCallbackBinderWithContext); +}; + } // namespace internal } // namespace service_manager
diff --git a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc index f9d583f4..4f46634 100644 --- a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc +++ b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
@@ -12,6 +12,7 @@ #include "build/build_config.h" #include "services/tracing/public/cpp/perfetto/dummy_producer.h" #include "services/tracing/public/cpp/perfetto/producer_client.h" +#include "services/tracing/public/cpp/trace_startup.h" #include "services/tracing/public/cpp/tracing_features.h" #if defined(OS_ANDROID) @@ -79,6 +80,7 @@ PerfettoTracedProcess::PerfettoTracedProcess(const char* system_socket) : producer_client_(std::make_unique<ProducerClient>(GetTaskRunner())) { + CHECK(IsTracingInitialized()); DETACH_FROM_SEQUENCE(sequence_checker_); // All communication with the system Perfetto service should occur on a single // sequence. To ensure we set up the socket correctly we construct the @@ -143,6 +145,7 @@ void PerfettoTracedProcess::ResetTaskRunnerForTesting( scoped_refptr<base::SequencedTaskRunner> task_runner) { GetTaskRunner()->ResetTaskRunnerForTesting(task_runner); + InitTracingPostThreadPoolStartAndFeatureList(); // Detaching the sequence_checker_ must happen after we reset the task runner. // This is because the Get() could call the constructor (if this is the first // call to Get()) which would then PostTask which would create races if we
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc index cd529bef..7440586 100644 --- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc +++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -33,6 +33,7 @@ #include "services/tracing/public/cpp/perfetto/traced_value_proto_writer.h" #include "services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h" #include "services/tracing/public/cpp/trace_event_args_whitelist.h" +#include "services/tracing/public/cpp/trace_startup.h" #include "services/tracing/public/mojom/constants.mojom.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/shared_memory_arbiter.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/startup_trace_writer.h" @@ -287,6 +288,7 @@ } void TraceEventDataSource::OnTaskSchedulerAvailable() { + CHECK(IsTracingInitialized()); { base::AutoLock lock(lock_); if (!startup_writer_registry_)
diff --git a/services/tracing/public/cpp/trace_startup.cc b/services/tracing/public/cpp/trace_startup.cc index 7ccc0789..59812b6 100644 --- a/services/tracing/public/cpp/trace_startup.cc +++ b/services/tracing/public/cpp/trace_startup.cc
@@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/logging.h" +#include "base/task/thread_pool/thread_pool.h" #include "base/trace_event/trace_log.h" #include "components/tracing/common/trace_startup_config.h" #include "components/tracing/common/trace_to_console.h" @@ -15,12 +16,17 @@ #include "services/tracing/public/cpp/tracing_features.h" namespace tracing { - namespace { using base::trace_event::TraceConfig; using base::trace_event::TraceLog; } // namespace +bool g_tracing_initialized_after_threadpool_and_featurelist = false; + +bool IsTracingInitialized() { + return g_tracing_initialized_after_threadpool_and_featurelist; +} + void EnableStartupTracingIfNeeded() { const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); @@ -66,7 +72,17 @@ } } -void InitTracingPostThreadPoolStart() { +void InitTracingPostThreadPoolStartAndFeatureList() { + if (g_tracing_initialized_after_threadpool_and_featurelist) { + return; + } + g_tracing_initialized_after_threadpool_and_featurelist = true; + // TODO(nuskos): We should switch these to DCHECK once we're reasonably + // confident we've ensured this is called properly in all processes. Probably + // after M78 release has been cut (since we'll verify in the rollout of M78). + CHECK(base::ThreadPoolInstance::Get()); + CHECK(base::FeatureList::GetInstance()); + // Below are the things tracing must do once per process. TraceEventDataSource::GetInstance()->OnTaskSchedulerAvailable(); if (base::FeatureList::IsEnabled(features::kEnablePerfettoSystemTracing)) { // To ensure System tracing connects we have to initialize the process wide
diff --git a/services/tracing/public/cpp/trace_startup.h b/services/tracing/public/cpp/trace_startup.h index 0273ef8f..658856a 100644 --- a/services/tracing/public/cpp/trace_startup.h +++ b/services/tracing/public/cpp/trace_startup.h
@@ -9,14 +9,19 @@ namespace tracing { -// If startup tracing command line flags are specified for the process, enables +// Returns true if InitTracingPostThreadPoolStartAndFeatureList has been called +// for this process. +bool COMPONENT_EXPORT(TRACING_CPP) IsTracingInitialized(); + // TraceLog with config based on the command line flags. Also hooks up service // callbacks in TraceLog if necessary. The latter is required when the perfetto // tracing backend is used. void COMPONENT_EXPORT(TRACING_CPP) EnableStartupTracingIfNeeded(); -// Initialize tracing components that require task runners. -void COMPONENT_EXPORT(TRACING_CPP) InitTracingPostThreadPoolStart(); +// Initialize tracing components that require task runners. Will switch +// IsTracingInitialized() to return true. +void COMPONENT_EXPORT(TRACING_CPP) + InitTracingPostThreadPoolStartAndFeatureList(); } // namespace tracing
diff --git a/testing/test_env.py b/testing/test_env.py index 3e6723b..13808a2f 100755 --- a/testing/test_env.py +++ b/testing/test_env.py
@@ -284,6 +284,7 @@ signal.signal(signal.SIGTERM, _sig_handler) signal.signal(signal.SIGINT, _sig_handler) + def run_executable(cmd, env, stdoutfile=None): """Runs an executable with: - CHROME_HEADLESS set to indicate that the test is running on a
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 4eeafed..4e04c96f 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -99,7 +99,7 @@ ], "experiments": [ { - "name": "InstallApp", + "name": "Install app", "params": { "include_no_download_required": "false" }, @@ -108,7 +108,7 @@ ] }, { - "name": "InstallAppNoDownloadRequired", + "name": "Install app (no download required)", "params": { "include_no_download_required": "true" },
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom index 8f32ac2..8583e16 100644 --- a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom +++ b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
@@ -5,6 +5,7 @@ module blink.mojom; import "services/network/public/mojom/url_loader_factory.mojom"; +import "third_party/blink/public/mojom/browser_interface_broker.mojom"; import "services/service_manager/public/mojom/interface_provider.mojom"; import "third_party/blink/public/mojom/cache_storage/cache_storage.mojom"; import "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom"; @@ -48,7 +49,12 @@ // byte-to-byte update check before running. CacheStorage? cache_storage; + // TODO(crbug.com/990845): remove when no longer used. service_manager.mojom.InterfaceProvider interface_provider; + + // Used for accessing services from the worker. + // Should replace the |interface_provider| above. + pending_remote<blink.mojom.BrowserInterfaceBroker> browser_interface_broker; }; // ServiceWorkerWorkerClient represents a service worker client that is a worker
diff --git a/third_party/blink/public/web/web_embedded_worker.h b/third_party/blink/public/web/web_embedded_worker.h index a72755f..8d0f16b4 100644 --- a/third_party/blink/public/web/web_embedded_worker.h +++ b/third_party/blink/public/web/web_embedded_worker.h
@@ -66,7 +66,8 @@ std::unique_ptr<WebServiceWorkerInstalledScriptsManagerParams>, mojo::ScopedMessagePipeHandle content_settings_handle, mojo::ScopedMessagePipeHandle cache_storage, - mojo::ScopedMessagePipeHandle interface_provider); + mojo::ScopedMessagePipeHandle interface_provider, + mojo::ScopedMessagePipeHandle browser_interface_broker); virtual ~WebEmbeddedWorker() = default;
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h index 2c8b6e7..b438a4b 100644 --- a/third_party/blink/public/web/web_local_frame_client.h +++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -69,6 +69,7 @@ #include "third_party/blink/public/web/web_history_commit_type.h" #include "third_party/blink/public/web/web_history_item.h" #include "third_party/blink/public/web/web_icon_url.h" +#include "third_party/blink/public/web/web_media_inspector.h" #include "third_party/blink/public/web/web_navigation_params.h" #include "third_party/blink/public/web/web_navigation_policy.h" #include "third_party/blink/public/web/web_navigation_type.h" @@ -145,6 +146,7 @@ // WebContentDecryptionModule* may be null if one has not yet been set. virtual WebMediaPlayer* CreateMediaPlayer(const WebMediaPlayerSource&, WebMediaPlayerClient*, + blink::MediaInspectorContext*, WebMediaPlayerEncryptedMediaClient*, WebContentDecryptionModule*, const WebString& sink_id,
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py index b3a6088..85979f2 100644 --- a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py +++ b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
@@ -15,6 +15,7 @@ from .dictionary import Dictionary from .dictionary import DictionaryMember from .enumeration import Enumeration +from .extended_attribute import ExtendedAttribute from .extended_attribute import ExtendedAttributes from .idl_type import IdlTypeFactory from .includes import Includes @@ -331,8 +332,42 @@ return DefaultValue() def _build_extended_attributes(self, node): + def build_extended_attribute(node): + key = node.GetName() + values = node.GetProperty('VALUE', default=None) + arguments = None + + # Drop constructors as they do not fit in ExtendedAttribute which + # doesn't support IdlType. + if key in ('Constructor', 'CustomConstructor', 'NamedConstructor'): + return None + + child_nodes = node.GetChildren() + if child_nodes: + assert len(child_nodes) == 1 + assert child_nodes[0].GetClass() == 'Arguments' + arguments = map(build_extattr_argument, + child_nodes[0].GetChildren()) + + return ExtendedAttribute( + key=key, values=values, arguments=arguments) + + def build_extattr_argument(node): + assert node.GetClass() == 'Argument' + + child_nodes = node.GetChildren() + assert len(child_nodes) == 1 + assert child_nodes[0].GetClass() == 'Type' + + type_node = child_nodes[0] + type_children = type_node.GetChildren() + assert len(type_children) == 1 + + return (type_children[0].GetName(), node.GetName()) + assert node.GetClass() == 'ExtAttributes' - return ExtendedAttributes() + return ExtendedAttributes( + filter(None, map(build_extended_attribute, node.GetChildren()))) def _build_inheritance(self, node): assert node.GetClass() == 'Inherit'
diff --git a/third_party/blink/renderer/core/animation/animation.h b/third_party/blink/renderer/core/animation/animation.h index 32e4322..405e8bb2 100644 --- a/third_party/blink/renderer/core/animation/animation.h +++ b/third_party/blink/renderer/core/animation/animation.h
@@ -99,6 +99,9 @@ ~Animation() override; void Dispose(); + virtual bool IsCSSAnimation() const { return false; } + virtual bool IsCSSTransition() const { return false; } + // Returns whether the animation is finished. bool Update(TimingUpdateReason);
diff --git a/third_party/blink/renderer/core/animation/css/css_animation.cc b/third_party/blink/renderer/core/animation/css/css_animation.cc index 07e7b2ca..dee7da2d 100644 --- a/third_party/blink/renderer/core/animation/css/css_animation.cc +++ b/third_party/blink/renderer/core/animation/css/css_animation.cc
@@ -21,12 +21,6 @@ AnimationEffect* content, const String& animation_name) : Animation(execution_context, timeline, content), - animation_name_(animation_name) { - setId(animation_name); -} - -const String& CSSAnimation::animationName() const { - return animation_name_; -} + animation_name_(animation_name) {} } // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css/css_animation.h b/third_party/blink/renderer/core/animation/css/css_animation.h index 7fa0885..1df6961 100644 --- a/third_party/blink/renderer/core/animation/css/css_animation.h +++ b/third_party/blink/renderer/core/animation/css/css_animation.h
@@ -24,12 +24,20 @@ AnimationEffect*, const String& animation_name); - const String& animationName() const; + bool IsCSSAnimation() const final { return true; } + + const String& animationName() const { return animation_name_; } private: String animation_name_; }; +DEFINE_TYPE_CASTS(CSSAnimation, + Animation, + animation, + animation->IsCSSAnimation(), + animation.IsCSSAnimation()); + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_ANIMATION_H_
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc index 14a3788e..fff44838 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations.cc +++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -225,24 +225,6 @@ CSSAnimations::CSSAnimations() = default; -bool CSSAnimations::IsAnimationForInspector(const Animation& animation) { - for (const auto& running_animation : running_animations_) { - if (running_animation->animation->SequenceNumber() == - animation.SequenceNumber()) - return true; - } - return false; -} - -bool CSSAnimations::IsTransitionAnimationForInspector( - const Animation& animation) const { - for (const auto& it : transitions_) { - if (it.value.animation->SequenceNumber() == animation.SequenceNumber()) - return true; - } - return false; -} - namespace { const KeyframeEffectModelBase* GetKeyframeEffectModelBase(
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.h b/third_party/blink/renderer/core/animation/css/css_animations.h index 3d50f5b..38a535c 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations.h +++ b/third_party/blink/renderer/core/animation/css/css_animations.h
@@ -57,9 +57,6 @@ public: CSSAnimations(); - bool IsAnimationForInspector(const Animation&); - bool IsTransitionAnimationForInspector(const Animation&) const; - static const StylePropertyShorthand& PropertiesForTransitionAll(); static bool IsAnimationAffectingProperty(const CSSProperty&); static bool IsAffectedByKeyframesFromScope(const Element&, const TreeScope&);
diff --git a/third_party/blink/renderer/core/animation/css/css_transition.cc b/third_party/blink/renderer/core/animation/css/css_transition.cc index f726ebd..f4fcedf0 100644 --- a/third_party/blink/renderer/core/animation/css/css_transition.cc +++ b/third_party/blink/renderer/core/animation/css/css_transition.cc
@@ -19,9 +19,7 @@ AnimationEffect* content, const PropertyHandle& transition_property) : Animation(execution_context, timeline, content), - transition_property_(transition_property) { - setId(transitionProperty()); -} + transition_property_(transition_property) {} AtomicString CSSTransition::transitionProperty() const { return transition_property_.GetCSSPropertyName().ToAtomicString();
diff --git a/third_party/blink/renderer/core/animation/css/css_transition.h b/third_party/blink/renderer/core/animation/css/css_transition.h index 82dced9d..9752b9ee 100644 --- a/third_party/blink/renderer/core/animation/css/css_transition.h +++ b/third_party/blink/renderer/core/animation/css/css_transition.h
@@ -23,12 +23,23 @@ AnimationEffect*, const PropertyHandle& transition_property); + bool IsCSSTransition() const final { return true; } + AtomicString transitionProperty() const; + const CSSProperty& TransitionCSSProperty() const { + return transition_property_.GetCSSProperty(); + } private: PropertyHandle transition_property_; }; +DEFINE_TYPE_CASTS(CSSTransition, + Animation, + animation, + animation->IsCSSTransition(), + animation.IsCSSTransition()); + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_TRANSITION_H_
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc index 38a03dd..9c1a440 100644 --- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc +++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -461,14 +461,24 @@ namespace { bool IsNonZeroUserUnitsValue(const CSSPrimitiveValue* value) { - // TODO(crbug.com/979895): This is the result of a refactoring, which might - // have revealed an existing bug in handling user units in math functions. Fix - // it if necessary. - const auto* numeric_literal = DynamicTo<CSSNumericLiteralValue>(value); - return numeric_literal && - numeric_literal->GetType() == - CSSPrimitiveValue::UnitType::kUserUnits && - value->GetDoubleValue() != 0; + if (!value) + return false; + if (const auto* numeric_literal = DynamicTo<CSSNumericLiteralValue>(value)) { + return numeric_literal->GetType() == + CSSPrimitiveValue::UnitType::kUserUnits && + value->GetDoubleValue() != 0; + } + const auto& math_value = To<CSSMathFunctionValue>(*value); + switch (math_value.Category()) { + case kCalcNumber: + return math_value.DoubleValue() != 0; + case kCalcPercentNumber: + case kCalcLengthNumber: + case kCalcPercentLengthNumber: + return true; + default: + return false; + } } } // namespace
diff --git a/third_party/blink/renderer/core/css/properties/css_property.h b/third_party/blink/renderer/core/css/properties/css_property.h index 9b6005c..a3d418c 100644 --- a/third_party/blink/renderer/core/css/properties/css_property.h +++ b/third_party/blink/renderer/core/css/properties/css_property.h
@@ -72,10 +72,10 @@ const CSSValue* CSSValueFromComputedStyle(const ComputedStyle&, const LayoutObject*, bool allow_visited_style) const; - virtual std::unique_ptr<CrossThreadStyleValue> - CrossThreadStyleValueFromComputedStyle(const ComputedStyle& computed_style, - const LayoutObject* layout_object, - bool allow_visited_style) const; + std::unique_ptr<CrossThreadStyleValue> CrossThreadStyleValueFromComputedStyle( + const ComputedStyle& computed_style, + const LayoutObject* layout_object, + bool allow_visited_style) const; virtual const CSSProperty& ResolveDirectionAwareProperty(TextDirection, WritingMode) const { return *this;
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h index 6f0cb78..68f4bdc 100644 --- a/third_party/blink/renderer/core/dom/element.h +++ b/third_party/blink/renderer/core/dom/element.h
@@ -321,9 +321,9 @@ int scrollHeight(); void scrollBy(double x, double y); - virtual void scrollBy(const ScrollToOptions*); + void scrollBy(const ScrollToOptions*); void scrollTo(double x, double y); - virtual void scrollTo(const ScrollToOptions*); + void scrollTo(const ScrollToOptions*); // This will return the |GetScrollableArea| of correspond LayoutBox. For // LayoutTextControlSingleLine, it will return its |InnerEditorElement|'s. virtual PaintLayerScrollableArea* GetScrollableArea() const;
diff --git a/third_party/blink/renderer/core/dom/events/event_target.h b/third_party/blink/renderer/core/dom/events/event_target.h index 6b48c93..ca8bd99 100644 --- a/third_party/blink/renderer/core/dom/events/event_target.h +++ b/third_party/blink/renderer/core/dom/events/event_target.h
@@ -194,9 +194,9 @@ virtual bool AddEventListenerInternal(const AtomicString& event_type, EventListener*, const AddEventListenerOptionsResolved*); - virtual bool RemoveEventListenerInternal(const AtomicString& event_type, - const EventListener*, - const EventListenerOptions*); + bool RemoveEventListenerInternal(const AtomicString& event_type, + const EventListener*, + const EventListenerOptions*); // Called when an event listener has been successfully added. virtual void AddedEventListener(const AtomicString& event_type,
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h index cfcd663..3db77e4 100644 --- a/third_party/blink/renderer/core/dom/node.h +++ b/third_party/blink/renderer/core/dom/node.h
@@ -564,7 +564,7 @@ } virtual void SetFocused(bool flag, WebFocusType); - virtual void SetHasFocusWithin(bool flag); + void SetHasFocusWithin(bool flag); virtual void SetDragged(bool flag); virtual int tabIndex() const; @@ -832,7 +832,7 @@ // Perform the default action for an event. virtual void DefaultEventHandler(Event&); - virtual void WillCallDefaultEventHandler(const Event&); + void WillCallDefaultEventHandler(const Event&); // Should return true if this Node has activation behavior. // https://dom.spec.whatwg.org/#eventtarget-activation-behavior virtual bool HasActivationBehavior() const; @@ -983,8 +983,7 @@ Node(TreeScope*, ConstructionType); - virtual void WillMoveToNewDocument(Document& old_document, - Document& new_document); + void WillMoveToNewDocument(Document& old_document, Document& new_document); virtual void DidMoveToNewDocument(Document& old_document); void AddedEventListener(const AtomicString& event_type,
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc index ef25053..72d1df7 100644 --- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc +++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -14,6 +14,7 @@ #include "third_party/blink/public/platform/web_coalesced_input_event.h" #include "third_party/blink/public/platform/web_input_event.h" #include "third_party/blink/public/platform/web_layer_tree_view.h" +#include "third_party/blink/public/platform/web_scroll_into_view_params.h" #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" #include "third_party/blink/public/web/web_ax_context.h" #include "third_party/blink/public/web/web_context_menu_data.h" @@ -44,6 +45,7 @@ #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/scroll/scrollbar_theme_overlay.h" +#include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h" #include "third_party/blink/renderer/core/testing/sim/sim_request.h" #include "third_party/blink/renderer/core/testing/sim/sim_test.h" #include "third_party/blink/renderer/platform/geometry/double_point.h" @@ -2510,7 +2512,7 @@ } }; -// Test that we correcty size the visual viewport's scrolling contents layer +// Test that we correctly size the visual viewport's scrolling contents layer // when the layout viewport is smaller. TEST_F(VisualViewportSimTest, ScrollingContentsSmallerThanContainer) { WebView().MainFrameWidget()->Resize(WebSize(400, 600)); @@ -2562,6 +2564,79 @@ } } +class VisualViewportScrollIntoViewTest : public VisualViewportSimTest { + public: + VisualViewportScrollIntoViewTest() {} + + void SetUp() override { + VisualViewportSimTest::SetUp(); + + // Setup a fixed-position element that's outside of an inset visual + // viewport. + WebView().MainFrameWidget()->Resize(WebSize(400, 600)); + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + #bottom { + position: fixed; + bottom: 0; + width: 100%; + height: 20px; + text-align: center; + } + </style> + <body> + <div id="bottom">Layout bottom</div> + </body> + )HTML"); + Compositor().BeginFrame(); + + // Shrink the height such that the fixed element is now off screen. + WebView().ResizeVisualViewport(IntSize(400, 600 - 100)); + } + + // Scrolls an element by the given name into view in the |visual_viewport| + // using params that optionally apply to a scroll sequence. + void ScrollIntoView(const WebString& element_name, + bool is_for_scroll_sequence) { + WebDocument web_doc = WebView().MainFrameImpl()->GetDocument(); + Element* bottom_element = web_doc.GetElementById(element_name); + WebScrollIntoViewParams scroll_params( + ScrollAlignment::kAlignToEdgeIfNeeded, + ScrollAlignment::kAlignToEdgeIfNeeded, kProgrammaticScroll, + /*make_visible_in_visual_viewport=*/true, kScrollBehaviorInstant, + is_for_scroll_sequence); + WebView().GetPage()->GetVisualViewport().ScrollIntoView( + bottom_element->BoundingBox(), scroll_params); + } +}; + +TEST_F(VisualViewportScrollIntoViewTest, + ScrollingToFixedWithScrollSequenceAnimationShort) { + VisualViewport& visual_viewport = WebView().GetPage()->GetVisualViewport(); + EXPECT_EQ(0.f, visual_viewport.GetScrollOffset().Height()); + ScrollIntoView("bottom", true); + visual_viewport.GetSmoothScrollSequencer()->RunQueuedAnimations(); + EXPECT_EQ(100.f, visual_viewport.GetScrollOffset().Height()); +} + +TEST_F(VisualViewportScrollIntoViewTest, + ScrollingToFixedWithoutScrollSequenceAnimationShort) { + VisualViewport& visual_viewport = WebView().GetPage()->GetVisualViewport(); + EXPECT_EQ(0.f, visual_viewport.GetScrollOffset().Height()); + ScrollIntoView("bottom", false); + EXPECT_EQ(100.f, visual_viewport.GetScrollOffset().Height()); +} + +TEST_F(VisualViewportScrollIntoViewTest, ScrollingToFixedFromJavascript) { + VisualViewport& visual_viewport = WebView().GetPage()->GetVisualViewport(); + EXPECT_EQ(0.f, visual_viewport.GetScrollOffset().Height()); + GetDocument().getElementById("bottom")->scrollIntoView(); + EXPECT_EQ(100.f, visual_viewport.GetScrollOffset().Height()); +} + TEST_P(VisualViewportTest, DeviceEmulationTransformNode) { InitializeWithAndroidSettings();
diff --git a/third_party/blink/renderer/core/html/forms/input_type.h b/third_party/blink/renderer/core/html/forms/input_type.h index 1f2097a7..e427d8e 100644 --- a/third_party/blink/renderer/core/html/forms/input_type.h +++ b/third_party/blink/renderer/core/html/forms/input_type.h
@@ -132,10 +132,10 @@ double Minimum() const; double Maximum() const; bool StepMismatch(const String&) const; - virtual bool GetAllowedValueStep(Decimal*) const; + bool GetAllowedValueStep(Decimal*) const; virtual StepRange CreateStepRange(AnyStepHandling) const; - virtual void StepUp(double, ExceptionState&); - virtual void StepUpFromLayoutObject(int); + void StepUp(double, ExceptionState&); + void StepUpFromLayoutObject(int); virtual String BadInputText() const; virtual String RangeOverflowText(const Decimal& maximum) const; virtual String RangeUnderflowText(const Decimal& minimum) const;
diff --git a/third_party/blink/renderer/core/html/media/video_auto_fullscreen_test.cc b/third_party/blink/renderer/core/html/media/video_auto_fullscreen_test.cc index c8505727..25e4b63 100644 --- a/third_party/blink/renderer/core/html/media/video_auto_fullscreen_test.cc +++ b/third_party/blink/renderer/core/html/media/video_auto_fullscreen_test.cc
@@ -24,6 +24,7 @@ public: WebMediaPlayer* CreateMediaPlayer(const WebMediaPlayerSource&, WebMediaPlayerClient*, + blink::MediaInspectorContext*, WebMediaPlayerEncryptedMediaClient*, WebContentDecryptionModule*, const WebString& sink_id,
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc index 4a3e305..ecdf4eb 100644 --- a/third_party/blink/renderer/core/input/event_handler.cc +++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -1533,6 +1533,13 @@ return scroll_manager_->HandleGestureScrollEvent(gesture_event); } +WebInputEventResult EventHandler::HandleGestureScrollEnd( + const WebGestureEvent& gesture_event) { + if (!frame_->GetPage()) + return WebInputEventResult::kNotHandled; + return scroll_manager_->HandleGestureScrollEnd(gesture_event); +} + void EventHandler::SetMouseDownMayStartAutoscroll() { mouse_event_manager_->SetMouseDownMayStartAutoscroll(); }
diff --git a/third_party/blink/renderer/core/input/event_handler.h b/third_party/blink/renderer/core/input/event_handler.h index 26a8dca..f153627 100644 --- a/third_party/blink/renderer/core/input/event_handler.h +++ b/third_party/blink/renderer/core/input/event_handler.h
@@ -201,6 +201,7 @@ // Handle the provided scroll gesture event, propagating down to child frames // as necessary. WebInputEventResult HandleGestureScrollEvent(const WebGestureEvent&); + WebInputEventResult HandleGestureScrollEnd(const WebGestureEvent&); bool IsScrollbarHandlingGestures() const; bool BestClickableNodeForHitTestResult(const HitTestLocation& location,
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc index eb72bffb..4c6de7e 100644 --- a/third_party/blink/renderer/core/input/scroll_manager.cc +++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -113,15 +113,6 @@ } } -Node* ScrollManager::GetScrollEventTarget() { - // Send the overscroll event to the node that scrolling is latched to which - // is either previously scrolled node or the last node in the scroll chain. - Node* scroll_target = previous_gesture_scrolled_node_; - if (!scroll_target && !current_scroll_chain_.IsEmpty()) - scroll_target = DOMNodeIds::NodeForId(current_scroll_chain_.front()); - return scroll_target; -} - void ScrollManager::StopAutoscroll() { if (AutoscrollController* controller = GetAutoscrollController()) controller->StopAutoscroll(); @@ -326,12 +317,11 @@ } ScrollableArea::ScrollCallback callback; - if (RuntimeEnabledFeatures::UpdateHoverAtBeginFrameEnabled() || - RuntimeEnabledFeatures::OverscrollCustomizationEnabled()) { + if (RuntimeEnabledFeatures::UpdateHoverAtBeginFrameEnabled()) { callback = ScrollableArea::ScrollCallback(WTF::Bind( [](WeakPersistent<ScrollableArea> area) { if (area) - area->OnScrollFinished(); + area->MarkHoverStateDirty(); }, WrapWeakPersistent(scrollable_area))); } @@ -635,7 +625,13 @@ return WebInputEventResult::kHandledSystem; if (RuntimeEnabledFeatures::OverscrollCustomizationEnabled()) { - if (Node* overscroll_target = GetScrollEventTarget()) { + // Send the overscroll event to the node that scrolling is latched to which + // is either previously scrolled node or the last node in the scroll chain. + Node* overscroll_target = previous_gesture_scrolled_node_; + if (!overscroll_target && !current_scroll_chain_.IsEmpty()) + overscroll_target = DOMNodeIds::NodeForId(current_scroll_chain_.front()); + + if (overscroll_target) { overscroll_target->GetDocument().EnqueueOverscrollEventForNode( overscroll_target, delta.Width(), delta.Height()); } @@ -696,9 +692,16 @@ snap_at_gesture_scroll_end = SnapAtGestureScrollEnd(); NotifyScrollPhaseEndForCustomizedScroll(); - if (RuntimeEnabledFeatures::OverscrollCustomizationEnabled() && - !snap_at_gesture_scroll_end) { - if (Node* scroll_end_target = GetScrollEventTarget()) { + if (RuntimeEnabledFeatures::OverscrollCustomizationEnabled()) { + // Send the scrollend event to the node that scrolling is latched to + // which is either previously scrolled node or the last node in the + // scroll chain. + DCHECK(!current_scroll_chain_.IsEmpty()); + if (previous_gesture_scrolled_node_) { + previous_gesture_scrolled_node_->GetDocument() + .EnqueueScrollEndEventForNode(previous_gesture_scrolled_node_); + } else if (Node* scroll_end_target = + DOMNodeIds::NodeForId(current_scroll_chain_.front())) { scroll_end_target->GetDocument().EnqueueScrollEndEventForNode( scroll_end_target); } @@ -792,12 +795,6 @@ } void ScrollManager::ScrollEndForSnapFling() { - if (RuntimeEnabledFeatures::OverscrollCustomizationEnabled()) { - if (Node* scroll_end_target = GetScrollEventTarget()) { - scroll_end_target->GetDocument().EnqueueScrollEndEventForNode( - scroll_end_target); - } - } if (current_scroll_chain_.IsEmpty()) { NotifyScrollPhaseEndForCustomizedScroll(); ClearGestureScrollState();
diff --git a/third_party/blink/renderer/core/input/scroll_manager.h b/third_party/blink/renderer/core/input/scroll_manager.h index 7f2e1e7..5ba2d85 100644 --- a/third_party/blink/renderer/core/input/scroll_manager.h +++ b/third_party/blink/renderer/core/input/scroll_manager.h
@@ -119,8 +119,6 @@ WebInputEventResult PassScrollGestureEvent(const WebGestureEvent&, LayoutObject*); - Node* GetScrollEventTarget(); - void ClearGestureScrollState(); void CustomizedScroll(ScrollState&);
diff --git a/third_party/blink/renderer/core/inspector/inspector_animation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_animation_agent.cc index 888a449..893eb8e 100644 --- a/third_party/blink/renderer/core/inspector/inspector_animation_agent.cc +++ b/third_party/blink/renderer/core/inspector/inspector_animation_agent.cc
@@ -10,7 +10,9 @@ #include "third_party/blink/renderer/core/animation/animation.h" #include "third_party/blink/renderer/core/animation/animation_effect.h" #include "third_party/blink/renderer/core/animation/computed_effect_timing.h" +#include "third_party/blink/renderer/core/animation/css/css_animation.h" #include "third_party/blink/renderer/core/animation/css/css_animations.h" +#include "third_party/blink/renderer/core/animation/css/css_transition.h" #include "third_party/blink/renderer/core/animation/effect_model.h" #include "third_party/blink/renderer/core/animation/element_animations.h" #include "third_party/blink/renderer/core/animation/keyframe_effect.h" @@ -34,6 +36,21 @@ namespace blink { +namespace { + +String AnimationDisplayName(const Animation& animation) { + if (!animation.id().IsEmpty()) + return animation.id(); + else if (animation.IsCSSAnimation()) + return ToCSSAnimation(animation).animationName(); + else if (animation.IsCSSTransition()) + return ToCSSTransition(animation).transitionProperty(); + else + return animation.id(); +} + +} // namespace + using protocol::Response; InspectorAnimationAgent::InspectorAnimationAgent( @@ -65,7 +82,6 @@ enabled_.Clear(); instrumenting_agents_->RemoveInspectorAnimationAgent(this); id_to_animation_.clear(); - id_to_animation_type_.clear(); id_to_animation_clone_.clear(); cleared_animations_.clear(); return Response::OK(); @@ -74,7 +90,6 @@ void InspectorAnimationAgent::DidCommitLoadForLocalFrame(LocalFrame* frame) { if (frame == inspected_frames_->Root()) { id_to_animation_.clear(); - id_to_animation_type_.clear(); id_to_animation_clone_.clear(); cleared_animations_.clear(); } @@ -145,47 +160,32 @@ std::unique_ptr<protocol::Animation::Animation> InspectorAnimationAgent::BuildObjectForAnimation(blink::Animation& animation) { - String animation_type; + String animation_type = AnimationType::WebAnimation; std::unique_ptr<protocol::Animation::AnimationEffect> animation_effect_object; - if (!animation.effect()) { - animation_type = AnimationType::WebAnimation; - } else { - const Element* element = ToKeyframeEffect(animation.effect())->target(); - std::unique_ptr<protocol::Animation::KeyframesRule> keyframe_rule; - - if (!element) { - animation_type = AnimationType::WebAnimation; - } else { - CSSAnimations& css_animations = - element->GetElementAnimations()->CssAnimations(); - - if (css_animations.IsTransitionAnimationForInspector(animation)) { - // CSS Transitions - animation_type = AnimationType::CSSTransition; - } else { - // Keyframe based animations - keyframe_rule = BuildObjectForAnimationKeyframes( - ToKeyframeEffect(animation.effect())); - animation_type = css_animations.IsAnimationForInspector(animation) - ? AnimationType::CSSAnimation - : AnimationType::WebAnimation; - } - } - + if (animation.effect()) { animation_effect_object = BuildObjectForAnimationEffect(ToKeyframeEffect(animation.effect())); - animation_effect_object->setKeyframesRule(std::move(keyframe_rule)); + + if (animation.IsCSSTransition()) { + animation_type = AnimationType::CSSTransition; + } else { + animation_effect_object->setKeyframesRule( + BuildObjectForAnimationKeyframes( + ToKeyframeEffect(animation.effect()))); + + if (animation.IsCSSAnimation()) + animation_type = AnimationType::CSSAnimation; + } } String id = String::Number(animation.SequenceNumber()); id_to_animation_.Set(id, &animation); - id_to_animation_type_.Set(id, animation_type); std::unique_ptr<protocol::Animation::Animation> animation_object = protocol::Animation::Animation::create() .setId(id) - .setName(animation.id()) + .setName(AnimationDisplayName(animation)) .setPausedState(animation.Paused()) .setPlayState(animation.playState()) .setPlaybackRate(animation.playbackRate()) @@ -343,7 +343,6 @@ clone->cancel(); id_to_animation_clone_.erase(animation_id); id_to_animation_.erase(animation_id); - id_to_animation_type_.erase(animation_id); cleared_animations_.insert(animation_id); } return Response::OK(); @@ -417,26 +416,28 @@ &GetCSSPropertyTransitionProperty(), &GetCSSPropertyTransitionTimingFunction(), }; - String type = - id_to_animation_type_.at(String::Number(animation.SequenceNumber())); - DCHECK_NE(type, AnimationType::WebAnimation); KeyframeEffect* effect = ToKeyframeEffect(animation.effect()); Vector<const CSSProperty*> css_properties; - if (type == AnimationType::CSSAnimation) { + if (animation.IsCSSAnimation()) { for (const CSSProperty* property : g_animation_properties) css_properties.push_back(property); - } else { + } else if (animation.IsCSSTransition()) { for (const CSSProperty* property : g_transition_properties) css_properties.push_back(property); - css_properties.push_back(&CSSProperty::Get(cssPropertyID(animation.id()))); + css_properties.push_back( + &ToCSSTransition(animation).TransitionCSSProperty()); + } else { + NOTREACHED(); } Element* element = effect->target(); HeapVector<Member<CSSStyleDeclaration>> styles = css_agent_->MatchingStyles(element); Digestor digestor(kHashAlgorithmSha1); - digestor.UpdateUtf8(type); + digestor.UpdateUtf8(animation.IsCSSTransition() + ? AnimationType::CSSTransition + : AnimationType::CSSAnimation); digestor.UpdateUtf8(animation.id()); for (const CSSProperty* property : css_properties) { CSSStyleDeclaration* style =
diff --git a/third_party/blink/renderer/core/inspector/inspector_animation_agent.h b/third_party/blink/renderer/core/inspector/inspector_animation_agent.h index 623a2462e..b804454 100644 --- a/third_party/blink/renderer/core/inspector/inspector_animation_agent.h +++ b/third_party/blink/renderer/core/inspector/inspector_animation_agent.h
@@ -87,7 +87,6 @@ v8_inspector::V8InspectorSession* v8_session_; HeapHashMap<String, Member<blink::Animation>> id_to_animation_; HeapHashMap<String, Member<blink::Animation>> id_to_animation_clone_; - HashMap<String, String> id_to_animation_type_; bool is_cloning_; HashSet<String> cleared_animations_; InspectorAgentState::Boolean enabled_;
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h index 7e046d0d..7df809e 100644 --- a/third_party/blink/renderer/core/layout/layout_box.h +++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -1603,7 +1603,7 @@ const Length& logical_width_length, LayoutUnit available_logical_width, LayoutUnit border_and_padding) const; - virtual LayoutUnit ComputeIntrinsicLogicalContentHeightUsing( + LayoutUnit ComputeIntrinsicLogicalContentHeightUsing( const Length& logical_height_length, LayoutUnit intrinsic_content_height, LayoutUnit border_and_padding) const;
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc b/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc index 781b9c7e..8a84ff2 100644 --- a/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc +++ b/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc
@@ -4,7 +4,6 @@ #include "third_party/blink/renderer/core/layout/layout_flexible_box.h" -#include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" @@ -463,49 +462,4 @@ ExpectSameAsRowVRL(); } -TEST_P(LayoutFlexibleBoxTest, ResizedFlexChildRequiresVisualOverflowRecalc) { - SetBodyInnerHTML(R"HTML( - <style> - #parent { - display: flex; - flex-direction: column; - width: 100px; - height: 1000px; - } - #child1 { - flex-grow: 1; - width: 100px; - will-change: transform; - } - #overflow-child { - width: 100px; - height: 950px; - box-shadow: 5px 10px; - } - #child2 { - width: 100px; - } - </style> - <div id="parent"> - <div id="child1"> - <div id="overflow-child"></div> - </div> - <div id="child2"></div> - </div> - )HTML"); - auto* child1_element = GetElementById("child1"); - auto* child2_element = GetElementById("child2"); - child2_element->setAttribute(html_names::kStyleAttr, "height: 100px;"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); - - auto* child1_box = ToLayoutBox(child1_element->GetLayoutObject()); - ASSERT_TRUE(child1_box->HasSelfPaintingLayer()); - EXPECT_TRUE(child1_box->Layer()->NeedsVisualOverflowRecalcForTesting()); - - UpdateAllLifecyclePhasesForTest(); - - EXPECT_EQ(child1_box->PhysicalVisualOverflowRect(), - PhysicalRect(0, 0, 105, 960)); -} - } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc index 5fc76c2..15c2d41 100644 --- a/third_party/blink/renderer/core/layout/layout_object.cc +++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -1028,7 +1028,9 @@ DCHECK(!object->IsSetNeedsLayoutForbidden()); #endif - object->MarkSelfPaintingLayerForVisualOverflowRecalc(); + if (object->HasLayer() && + ToLayoutBoxModelObject(object)->Layer()->IsSelfPaintingLayer()) + ToLayoutBoxModelObject(object)->Layer()->SetNeedsVisualOverflowRecalc(); if (layouter) { layouter->RecordObjectMarkedForLayout(object); @@ -1036,6 +1038,7 @@ if (object == layouter->Root()) { if (auto* painting_layer = PaintingLayer()) painting_layer->SetNeedsVisualOverflowRecalc(); + return; } } @@ -1943,7 +1946,12 @@ : object->Container(); if (object) { object->SetChildNeedsLayoutOverflowRecalc(); - object->MarkSelfPaintingLayerForVisualOverflowRecalc(); + + if (object->HasLayer()) { + auto* box_model_object = ToLayoutBoxModelObject(object); + if (box_model_object->HasSelfPaintingLayer()) + box_model_object->Layer()->SetNeedsVisualOverflowRecalc(); + } } } while (object); @@ -1953,8 +1961,12 @@ bool needed_recalc = SelfNeedsLayoutOverflowRecalc(); SetSelfNeedsLayoutOverflowRecalc(); SetShouldCheckForPaintInvalidation(); - MarkSelfPaintingLayerForVisualOverflowRecalc(); + if (HasLayer()) { + auto* box_model_object = ToLayoutBoxModelObject(this); + if (box_model_object->HasSelfPaintingLayer()) + box_model_object->Layer()->SetNeedsVisualOverflowRecalc(); + } if (!needed_recalc) MarkContainerChainForOverflowRecalcIfNeeded(); } @@ -4063,14 +4075,6 @@ ->FlipForWritingMode(position, width); } -void LayoutObject::MarkSelfPaintingLayerForVisualOverflowRecalc() { - if (HasLayer()) { - auto* box_model_object = ToLayoutBoxModelObject(this); - if (box_model_object->HasSelfPaintingLayer()) - box_model_object->Layer()->SetNeedsVisualOverflowRecalc(); - } -} - } // namespace blink #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h index 123edca..b6d7e95 100644 --- a/third_party/blink/renderer/core/layout/layout_object.h +++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1405,7 +1405,6 @@ // when the width of the LayoutObject changes as this impacts children with // 'width' set to auto. virtual void UpdateLayout() = 0; - virtual bool UpdateImageLoadingPriorities() { return false; } void HandleSubtreeModifications(); virtual void SubtreeDidChange() {} @@ -2033,10 +2032,8 @@ // Called when the previous visual rect(s) is no longer valid. virtual void ClearPreviousVisualRects(); - void SetSelfNeedsLayoutForAvailableSpace(bool flag) { - bitfields_.SetSelfNeedsLayoutForAvailableSpace(flag); - if (flag) - MarkSelfPaintingLayerForVisualOverflowRecalc(); + void SetSelfNeedsLayoutForAvailableSpace(bool b) { + bitfields_.SetSelfNeedsLayoutForAvailableSpace(b); } PaintInvalidationReason FullPaintInvalidationReason() const { @@ -2729,8 +2726,6 @@ LayoutUnit width, const LayoutBox* box_for_flipping) const; - void MarkSelfPaintingLayerForVisualOverflowRecalc(); - // This is set by Set[Subtree]ShouldDoFullPaintInvalidation, and cleared // during PrePaint in this object's InvalidatePaint(). It's different from // DisplayItemClient::GetPaintInvalidationReason() which is set during
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h index 6b91331..1b86d63 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h +++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
@@ -71,20 +71,6 @@ USING_FAST_MALLOC(NGConstraintSpace); public: - enum ConstraintSpaceFlags { - kOrthogonalWritingModeRoot = 1 << 0, - kIsFixedBlockSizeIndefinite = 1 << 1, - kIntermediateLayout = 1 << 2, - kSeparateLeadingFragmentainerMargins = 1 << 3, - kNewFormattingContext = 1 << 4, - kAnonymous = 1 << 5, - kUseFirstLineStyle = 1 << 6, - kAncestorHasClearancePastAdjoiningFloats = 1 << 7, - - // Size of bitfield used to store the flags. - kNumberOfConstraintSpaceFlags = 8 - }; - // To ensure that the bfc_offset_, rare_data_ union doesn't get polluted, // always initialize the bfc_offset_. NGConstraintSpace() : bfc_offset_() {} @@ -158,7 +144,7 @@ } bool IsOrthogonalWritingModeRoot() const { - return HasFlag(kOrthogonalWritingModeRoot); + return bitfields_.is_orthogonal_writing_mode_root; } // The available space size. @@ -266,7 +252,9 @@ // Whether the current constraint space is for the newly established // Formatting Context. - bool IsNewFormattingContext() const { return HasFlag(kNewFormattingContext); } + bool IsNewFormattingContext() const { + return bitfields_.is_new_formatting_context; + } // Return true if we are to separate (i.e. honor, rather than collapse) // block-start margins at the beginning of fragmentainers. This only makes a @@ -274,20 +262,20 @@ // block-start margins at the beginning of a fragmentainers are to be // truncated to 0 if they occur after a soft (unforced) break. bool HasSeparateLeadingFragmentainerMargins() const { - return HasFlag(kSeparateLeadingFragmentainerMargins); + return bitfields_.has_separate_leading_fragmentainer_margins; } // Whether the fragment produced from layout should be anonymous, (e.g. it // may be a column in a multi-column layout). In such cases it shouldn't have // any borders or padding. - bool IsAnonymous() const { return HasFlag(kAnonymous); } + bool IsAnonymous() const { return bitfields_.is_anonymous; } // Whether to use the ':first-line' style or not. // Note, this is not about the first line of the content to layout, but // whether the constraint space itself is on the first line, such as when it's // an inline block. // Also note this is true only when the document has ':first-line' rules. - bool UseFirstLineStyle() const { return HasFlag(kUseFirstLineStyle); } + bool UseFirstLineStyle() const { return bitfields_.use_first_line_style; } // Returns true if an ancestor had clearance past adjoining floats. // @@ -296,7 +284,7 @@ // margins are adjoining or not), and without this extra bit of information // can get into a bad state. bool AncestorHasClearancePastAdjoiningFloats() const { - return HasFlag(kAncestorHasClearancePastAdjoiningFloats); + return bitfields_.ancestor_has_clearance_past_adjoining_floats; } // Some layout modes “stretch” their children to a fixed size (e.g. flex, @@ -312,7 +300,7 @@ // Whether a fixed block-size should be considered indefinite. bool IsFixedBlockSizeIndefinite() const { - return HasFlag(kIsFixedBlockSizeIndefinite); + return bitfields_.is_fixed_block_size_indefinite; } // Whether an auto inline-size should be interpreted as shrink-to-fit @@ -322,7 +310,9 @@ // Whether this constraint space is used for an intermediate layout in a // multi-pass layout. In such a case, we should not copy back the resulting // layout data to the legacy tree or create a paint fragment from it. - bool IsIntermediateLayout() const { return HasFlag(kIntermediateLayout); } + bool IsIntermediateLayout() const { + return bitfields_.is_intermediate_layout; + } // If specified a layout should produce a Fragment which fragments at the // blockSize if possible. @@ -595,20 +585,40 @@ adjoining_object_types(static_cast<unsigned>(kAdjoiningNone)), writing_mode(static_cast<unsigned>(writing_mode)), direction(static_cast<unsigned>(TextDirection::kLtr)), + is_anonymous(false), + is_new_formatting_context(false), + is_orthogonal_writing_mode_root(false), + is_intermediate_layout(false), + is_fixed_block_size_indefinite(false), + use_first_line_style(false), + has_separate_leading_fragmentainer_margins(false), + ancestor_has_clearance_past_adjoining_floats(false), is_shrink_to_fit(false), is_fixed_inline_size(false), is_fixed_block_size(false), is_in_restricted_block_size_table_cell(false), table_cell_child_layout_phase(static_cast<unsigned>( NGTableCellChildLayoutPhase::kNotTableCellChild)), - flags(), percentage_inline_storage(kSameAsAvailable), percentage_block_storage(kSameAsAvailable), replaced_percentage_block_storage(kSameAsAvailable) {} bool MaySkipLayout(const Bitfields& other) const { return adjoining_object_types == other.adjoining_object_types && - writing_mode == other.writing_mode && flags == other.flags && + writing_mode == other.writing_mode && + direction == other.direction && + is_anonymous == other.is_anonymous && + is_new_formatting_context == other.is_new_formatting_context && + is_orthogonal_writing_mode_root == + other.is_orthogonal_writing_mode_root && + is_intermediate_layout == other.is_intermediate_layout && + is_fixed_block_size_indefinite == + other.is_fixed_block_size_indefinite && + use_first_line_style == other.use_first_line_style && + has_separate_leading_fragmentainer_margins == + other.has_separate_leading_fragmentainer_margins && + ancestor_has_clearance_past_adjoining_floats == + other.ancestor_has_clearance_past_adjoining_floats && baseline_requests == other.baseline_requests; } @@ -627,6 +637,18 @@ unsigned writing_mode : 3; unsigned direction : 1; + unsigned is_anonymous : 1; + unsigned is_new_formatting_context : 1; + unsigned is_orthogonal_writing_mode_root : 1; + unsigned is_intermediate_layout : 1; + + unsigned is_fixed_block_size_indefinite : 1; + unsigned use_first_line_style : 1; + unsigned has_separate_leading_fragmentainer_margins : 1; + unsigned ancestor_has_clearance_past_adjoining_floats : 1; + + unsigned baseline_requests : NGBaselineRequestList::kSerializedBits; + // Size constraints. unsigned is_shrink_to_fit : 1; unsigned is_fixed_inline_size : 1; @@ -634,18 +656,11 @@ unsigned is_in_restricted_block_size_table_cell : 1; unsigned table_cell_child_layout_phase : 2; // NGTableCellChildLayoutPhase - unsigned flags : kNumberOfConstraintSpaceFlags; // ConstraintSpaceFlags - unsigned baseline_requests : NGBaselineRequestList::kSerializedBits; - unsigned percentage_inline_storage : 2; // NGPercentageStorage unsigned percentage_block_storage : 2; // NGPercentageStorage unsigned replaced_percentage_block_storage : 2; // NGPercentageStorage }; - inline bool HasFlag(ConstraintSpaceFlags mask) const { - return bitfields_.flags & static_cast<unsigned>(mask); - } - inline bool HasRareData() const { return bitfields_.has_rare_data; } RareData* EnsureRareData() {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h index 4f216817..fffe7f1d 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h +++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
@@ -30,8 +30,8 @@ out_writing_mode, is_new_fc) { // Propagate the intermediate layout bit to the child constraint space. - if (parent_space.IsIntermediateLayout()) - space_.bitfields_.flags |= NGConstraintSpace::kIntermediateLayout; + space_.bitfields_.is_intermediate_layout = + parent_space.IsIntermediateLayout(); } // The setters on this builder are in the writing mode of parent_writing_mode. @@ -51,11 +51,9 @@ is_new_fc_(is_new_fc), force_orthogonal_writing_mode_root_( force_orthogonal_writing_mode_root) { - if (is_new_fc_) - space_.bitfields_.flags |= NGConstraintSpace::kNewFormattingContext; - - if (!is_in_parallel_flow_ || force_orthogonal_writing_mode_root_) - space_.bitfields_.flags |= NGConstraintSpace::kOrthogonalWritingModeRoot; + space_.bitfields_.is_new_formatting_context = is_new_fc_; + space_.bitfields_.is_orthogonal_writing_mode_root = + !is_in_parallel_flow_ || force_orthogonal_writing_mode_root_; } // If inline size is indefinite, use the fallback size for available inline @@ -154,7 +152,7 @@ NGConstraintSpaceBuilder& SetIsFixedBlockSizeIndefinite(bool b) { if (LIKELY(is_in_parallel_flow_ || !force_orthogonal_writing_mode_root_)) - SetFlag(NGConstraintSpace::kIsFixedBlockSizeIndefinite, b); + space_.bitfields_.is_fixed_block_size_indefinite = b; return *this; } @@ -165,7 +163,7 @@ } NGConstraintSpaceBuilder& SetIsIntermediateLayout(bool b) { - SetFlag(NGConstraintSpace::kIntermediateLayout, b); + space_.bitfields_.is_intermediate_layout = b; return *this; } @@ -183,22 +181,22 @@ } NGConstraintSpaceBuilder& SetSeparateLeadingFragmentainerMargins(bool b) { - SetFlag(NGConstraintSpace::kSeparateLeadingFragmentainerMargins, b); + space_.bitfields_.has_separate_leading_fragmentainer_margins = b; return *this; } NGConstraintSpaceBuilder& SetIsAnonymous(bool b) { - SetFlag(NGConstraintSpace::kAnonymous, b); + space_.bitfields_.is_anonymous = b; return *this; } NGConstraintSpaceBuilder& SetUseFirstLineStyle(bool b) { - SetFlag(NGConstraintSpace::kUseFirstLineStyle, b); + space_.bitfields_.use_first_line_style = b; return *this; } NGConstraintSpaceBuilder& SetAncestorHasClearancePastAdjoiningFloats() { - SetFlag(NGConstraintSpace::kAncestorHasClearancePastAdjoiningFloats, true); + space_.bitfields_.ancestor_has_clearance_past_adjoining_floats = true; return *this; } @@ -312,7 +310,7 @@ #endif DCHECK(!is_new_fc_ || !space_.bitfields_.adjoining_object_types); - DCHECK_EQ(space_.HasFlag(NGConstraintSpace::kOrthogonalWritingModeRoot), + DCHECK_EQ(space_.bitfields_.is_orthogonal_writing_mode_root, !is_in_parallel_flow_ || force_orthogonal_writing_mode_root_); DCHECK(!force_orthogonal_writing_mode_root_ || is_in_parallel_flow_) @@ -325,12 +323,6 @@ } private: - void SetFlag(NGConstraintSpace::ConstraintSpaceFlags mask, bool value) { - space_.bitfields_.flags = - (space_.bitfields_.flags & ~static_cast<unsigned>(mask)) | - (-(int32_t)value & static_cast<unsigned>(mask)); - } - NGConstraintSpace space_; // Orthogonal writing mode roots may need a fallback, to prevent available
diff --git a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc index ae70642..3ecba77 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
@@ -361,7 +361,6 @@ cross_axis_offset = border_scrollbar_padding_.inline_start; } FlexLine* line; - LayoutUnit max_main_axis_extent; while ( (line = algorithm_->ComputeNextFlexLine(border_box_size_.inline_size))) { line->SetContainerMainInnerSize( @@ -415,21 +414,15 @@ // cross_axis_offset is updated in each iteration of the loop, for passing // in to the next iteration. line->ComputeLineItemsPosition(main_axis_offset, cross_axis_offset); - max_main_axis_extent = - std::max(max_main_axis_extent, line->main_axis_extent); } - LayoutUnit intrinsic_block_content_size = - is_column_ ? max_main_axis_extent - : cross_axis_offset - border_scrollbar_padding_.block_start; - LayoutUnit intrinsic_block_size = - intrinsic_block_content_size + border_scrollbar_padding_.BlockSum(); + + LayoutUnit intrinsic_block_size = algorithm_->IntrinsicContentBlockSize() + + border_scrollbar_padding_.BlockSum(); LayoutUnit block_size = ComputeBlockSizeForFragment( ConstraintSpace(), Node(), border_padding_, intrinsic_block_size); + container_builder_.SetIntrinsicBlockSize(intrinsic_block_size); container_builder_.SetBlockSize(block_size); - container_builder_.SetIntrinsicBlockSize( - algorithm_->IntrinsicContentBlockSize() + - border_scrollbar_padding_.BlockSum()); GiveLinesAndItemsFinalPositionAndSize();
diff --git a/third_party/blink/renderer/core/page/context_menu_controller_test.cc b/third_party/blink/renderer/core/page/context_menu_controller_test.cc index 1c840bb..7746066a 100644 --- a/third_party/blink/renderer/core/page/context_menu_controller_test.cc +++ b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
@@ -45,6 +45,7 @@ WebMediaPlayer* CreateMediaPlayer(const WebMediaPlayerSource&, WebMediaPlayerClient*, + blink::MediaInspectorContext*, WebMediaPlayerEncryptedMediaClient*, WebContentDecryptionModule*, const WebString& sink_id,
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h index d5c21ddc..8ed44402 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.h +++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -1108,10 +1108,6 @@ void DirtyStackingContextZOrderLists(); - bool NeedsVisualOverflowRecalcForTesting() const { - return needs_visual_overflow_recalc_; - } - PhysicalOffset OffsetForInFlowRelPosition() const { return rare_data_ ? rare_data_->offset_for_in_flow_rel_position : PhysicalOffset();
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc index 93340f2a..a876957 100644 --- a/third_party/blink/renderer/core/scroll/scrollable_area.cc +++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -300,13 +300,12 @@ CancelScrollAnimation(); ScrollCallback callback = std::move(on_finish); - if (RuntimeEnabledFeatures::UpdateHoverAtBeginFrameEnabled() || - RuntimeEnabledFeatures::OverscrollCustomizationEnabled()) { + if (RuntimeEnabledFeatures::UpdateHoverAtBeginFrameEnabled()) { callback = ScrollCallback(WTF::Bind( [](ScrollCallback original_callback, WeakPersistent<ScrollableArea> area) { if (area) - area->OnScrollFinished(); + area->MarkHoverStateDirty(); if (original_callback) std::move(original_callback).Run(); }, @@ -818,19 +817,13 @@ scrollable_element_id.GetInternalValue(), element_id_namespace); } -void ScrollableArea::OnScrollFinished() { +void ScrollableArea::MarkHoverStateDirty() { if (GetLayoutBox()) { - if (RuntimeEnabledFeatures::OverscrollCustomizationEnabled()) { - if (Node* node = GetLayoutBox()->GetNode()) - node->GetDocument().EnqueueScrollEndEventForNode(node); - } - if (RuntimeEnabledFeatures::UpdateHoverAtBeginFrameEnabled()) { - GetLayoutBox() - ->GetFrame() - ->LocalFrameRoot() - .GetEventHandler() - .MarkHoverStateDirty(); - } + GetLayoutBox() + ->GetFrame() + ->LocalFrameRoot() + .GetEventHandler() + .MarkHoverStateDirty(); } }
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h index b380848b..0ea6877 100644 --- a/third_party/blink/renderer/core/scroll/scrollable_area.h +++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -416,7 +416,7 @@ virtual ScrollbarTheme& GetPageScrollbarTheme() const = 0; - void OnScrollFinished(); + virtual void MarkHoverStateDirty(); float ScrollStep(ScrollGranularity, ScrollbarOrientation) const;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc index d6a8c88..249d5e4d 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1425,7 +1425,7 @@ } bool AXObject::HasIndirectChildren() const { - return IsTableCol() || RoleValue() == ax::mojom::Role::kTableHeaderContainer; + return RoleValue() == ax::mojom::Role::kTableHeaderContainer; } bool AXObject::CanSetSelectedAttribute() const {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h index efe16d1..129600f6d 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.h +++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -384,7 +384,7 @@ void TokenVectorFromAttribute(Vector<String>&, const QualifiedName&) const; - virtual void GetSparseAXAttributes(AXSparseAttributeClient&) const; + void GetSparseAXAttributes(AXSparseAttributeClient&) const; // Determine subclass type. virtual bool IsAXNodeObject() const { return false; } @@ -398,7 +398,6 @@ // Check object role or purpose. virtual ax::mojom::Role RoleValue() const { return role_; } bool IsARIATextControl() const; - virtual bool IsARIARow() const { return false; } virtual bool IsAnchor() const { return false; } bool IsButton() const; bool IsCanvas() const { return RoleValue() == ax::mojom::Role::kCanvas; } @@ -435,7 +434,7 @@ return false; } // contenteditable or role=textbox virtual bool IsPasswordField() const { return false; } - virtual bool IsPasswordFieldAndShouldHideValue() const; + bool IsPasswordFieldAndShouldHideValue() const; bool IsPresentational() const { return RoleValue() == ax::mojom::Role::kNone || RoleValue() == ax::mojom::Role::kPresentational; @@ -498,7 +497,7 @@ virtual bool IsVisited() const { return false; } // Check whether certain properties can be modified. - virtual bool CanSetFocusAttribute() const; + bool CanSetFocusAttribute() const; bool CanSetValueAttribute() const; // Whether objects are ignored, i.e. hidden from the AT. @@ -553,7 +552,7 @@ typedef HeapVector<NameSource> NameSources; // Retrieves the accessible name of the object and a list of all potential // sources for the name, indicating which were used. - virtual String GetName(NameSources*) const; + String GetName(NameSources*) const; typedef HeapVector<DescriptionSource> DescriptionSources; // Takes the result of nameFrom from calling |name|, above, and retrieves the @@ -888,9 +887,8 @@ virtual bool NeedsToUpdateChildren() const { return false; } virtual void SetNeedsToUpdateChildren() {} virtual void ClearChildren(); - virtual void DetachFromParent() { parent_ = nullptr; } - virtual AXObject* ScrollBar(AccessibilityOrientation) { return nullptr; } - virtual void AddAccessibleNodeChildren(); + void DetachFromParent() { parent_ = nullptr; } + void AddAccessibleNodeChildren(); virtual void SelectedOptions(AXObjectVector&) const {} // Properties of the object's owning document or page. @@ -898,7 +896,7 @@ // DOM and layout tree access. virtual Node* GetNode() const { return nullptr; } - virtual Element* GetElement() const; // Same as GetNode, if it's an Element. + Element* GetElement() const; // Same as GetNode, if it's an Element. virtual LayoutObject* GetLayoutObject() const { return nullptr; } virtual Document* GetDocument() const; virtual LocalFrameView* DocumentFrameView() const; @@ -916,11 +914,10 @@ void SetScrollOffset(const IntPoint&) const; // Tables and grids. - virtual bool IsTableLikeRole() const; - virtual bool IsTableRowLikeRole() const; - virtual bool IsTableCellLikeRole() const; + bool IsTableLikeRole() const; + bool IsTableRowLikeRole() const; + bool IsTableCellLikeRole() const; virtual bool IsDataTable() const { return false; } - virtual bool IsTableCol() const { return false; } // For a table. virtual unsigned ColumnCount() const; @@ -934,10 +931,10 @@ virtual unsigned RowIndex() const; virtual unsigned ColumnSpan() const; virtual unsigned RowSpan() const; - virtual unsigned AriaColumnIndex() const; - virtual unsigned AriaRowIndex() const; - virtual int AriaColumnCount() const; - virtual int AriaRowCount() const; + unsigned AriaColumnIndex() const; + unsigned AriaRowIndex() const; + int AriaColumnCount() const; + int AriaRowCount() const; virtual ax::mojom::SortDirection GetSortDirection() const { return ax::mojom::SortDirection::kNone; } @@ -980,8 +977,8 @@ // to keep track of nodes that gain or lose accessibility focus, but // this isn't exposed to the open web so they're explicitly marked as // internal so it's clear that these should not dispatch DOM events. - virtual bool InternalClearAccessibilityFocusAction(); - virtual bool InternalSetAccessibilityFocusAction(); + bool InternalClearAccessibilityFocusAction(); + bool InternalSetAccessibilityFocusAction(); // Native implementations of actions that aren't handled by AOM // event listeners. These all return true if handled. @@ -989,16 +986,16 @@ virtual bool OnNativeClickAction(); virtual bool OnNativeFocusAction(); virtual bool OnNativeIncrementAction(); - virtual bool OnNativeScrollToGlobalPointAction(const IntPoint&) const; - virtual bool OnNativeScrollToMakeVisibleAction() const; - virtual bool OnNativeScrollToMakeVisibleWithSubFocusAction( + bool OnNativeScrollToGlobalPointAction(const IntPoint&) const; + bool OnNativeScrollToMakeVisibleAction() const; + bool OnNativeScrollToMakeVisibleWithSubFocusAction( const IntRect&, blink::ScrollAlignment horizontal_scroll_alignment, blink::ScrollAlignment vertical_scroll_alignment) const; virtual bool OnNativeSetSelectedAction(bool); virtual bool OnNativeSetSequentialFocusNavigationStartingPointAction(); virtual bool OnNativeSetValueAction(const String&); - virtual bool OnNativeShowContextMenuAction(); + bool OnNativeShowContextMenuAction(); // Notifications that this object may have changed. virtual void ChildrenChanged() {} @@ -1087,7 +1084,7 @@ return nullptr; } - virtual bool CanSetSelectedAttribute() const; + bool CanSetSelectedAttribute() const; const AXObject* InertRoot() const; // Returns true if the event was handled.
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc index 1d442f7d..623bc5c8 100644 --- a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc +++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc
@@ -7,6 +7,7 @@ #include <utility> #include "services/service_manager/public/cpp/interface_provider.h" +#include "third_party/blink/public/common/browser_interface_broker_proxy.h" #include "third_party/blink/renderer/modules/background_fetch/background_fetch_options.h" #include "third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h" #include "third_party/blink/renderer/modules/background_fetch/background_fetch_type_converters.h" @@ -93,14 +94,14 @@ mojom::blink::BackgroundFetchService* BackgroundFetchBridge::GetService() { if (!background_fetch_service_) { - auto request = mojo::MakeRequest( - &background_fetch_service_, + auto receiver = background_fetch_service_.BindNewPipeAndPassReceiver( GetSupplementable()->GetExecutionContext()->GetTaskRunner( TaskType::kBackgroundFetch)); - if (auto* interface_provider = GetSupplementable() - ->GetExecutionContext() - ->GetInterfaceProvider()) { - interface_provider->GetInterface(std::move(request)); + if (auto* browser_interface_broker_proxy = + GetSupplementable() + ->GetExecutionContext() + ->GetBrowserInterfaceBrokerProxy()) { + browser_interface_broker_proxy->GetInterface(std::move(receiver)); } } return background_fetch_service_.get();
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.h b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.h index 0d655c0..f88536db 100644 --- a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.h +++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.h
@@ -8,6 +8,7 @@ #include <memory> #include "base/macros.h" +#include "mojo/public/cpp/bindings/remote.h" #include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom-blink.h" #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h" #include "third_party/blink/renderer/platform/heap/handle.h" @@ -79,7 +80,7 @@ mojom::blink::BackgroundFetchError error, mojom::blink::BackgroundFetchRegistrationPtr registration_ptr); - mojom::blink::BackgroundFetchServicePtr background_fetch_service_; + mojo::Remote<mojom::blink::BackgroundFetchService> background_fetch_service_; DISALLOW_COPY_AND_ASSIGN(BackgroundFetchBridge); };
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc index 6dc2791..2aaecb0 100644 --- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc +++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -80,7 +80,8 @@ installed_scripts_manager_params, mojo::ScopedMessagePipeHandle content_settings_handle, mojo::ScopedMessagePipeHandle cache_storage, - mojo::ScopedMessagePipeHandle interface_provider) { + mojo::ScopedMessagePipeHandle interface_provider, + mojo::ScopedMessagePipeHandle browser_interface_broker) { return std::make_unique<WebEmbeddedWorkerImpl>( std::move(client), std::move(installed_scripts_manager_params), std::make_unique<ServiceWorkerContentSettingsProxy>( @@ -92,7 +93,10 @@ mojom::blink::CacheStorage::Version_), service_manager::mojom::blink::InterfaceProviderPtrInfo( std::move(interface_provider), - service_manager::mojom::blink::InterfaceProvider::Version_)); + service_manager::mojom::blink::InterfaceProvider::Version_), + mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>( + std::move(browser_interface_broker), + mojom::blink::BrowserInterfaceBroker::Version_)); } // static @@ -104,7 +108,8 @@ client, nullptr /* installed_scripts_manager_params */, std::make_unique<ServiceWorkerContentSettingsProxy>( nullptr /* host_info */), - nullptr /* cache_storage_info */, nullptr /* interface_provider_info */); + nullptr /* cache_storage_info */, nullptr /* interface_provider_info */, + mojo::NullRemote() /* browser_interface_broker */); worker_impl->installed_scripts_manager_ = std::move(installed_scripts_manager); return worker_impl; @@ -117,12 +122,15 @@ std::unique_ptr<ServiceWorkerContentSettingsProxy> content_settings_client, mojom::blink::CacheStoragePtrInfo cache_storage_info, service_manager::mojom::blink::InterfaceProviderPtrInfo - interface_provider_info) + interface_provider_info, + mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker> + browser_interface_broker) : worker_context_client_(client), content_settings_client_(std::move(content_settings_client)), pause_after_download_state_(kDontPauseAfterDownload), cache_storage_info_(std::move(cache_storage_info)), - interface_provider_info_(std::move(interface_provider_info)) { + interface_provider_info_(std::move(interface_provider_info)), + browser_interface_broker_(std::move(browser_interface_broker)) { if (installed_scripts_manager_params) { DCHECK(installed_scripts_manager_params->manager_request.is_valid()); DCHECK(installed_scripts_manager_params->manager_host_ptr.is_valid()); @@ -289,8 +297,7 @@ std::move(worker_settings), static_cast<V8CacheOptions>(worker_start_data_.v8_cache_options), nullptr /* worklet_module_respones_map */, - std::move(interface_provider_info_), - mojo::NullRemote() /* TODO(crbug.com/985112) pass a real BIB */, + std::move(interface_provider_info_), std::move(browser_interface_broker_), BeginFrameProviderParams(), nullptr /* parent_feature_policy */, base::UnguessableToken() /* agent_cluster_id */);
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h index 00d53a5..734b269 100644 --- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h +++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h
@@ -68,7 +68,8 @@ std::unique_ptr<WebServiceWorkerInstalledScriptsManagerParams>, std::unique_ptr<ServiceWorkerContentSettingsProxy>, mojom::blink::CacheStoragePtrInfo, - service_manager::mojom::blink::InterfaceProviderPtrInfo); + service_manager::mojom::blink::InterfaceProviderPtrInfo, + mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker>); ~WebEmbeddedWorkerImpl() override; // WebEmbeddedWorker overrides. @@ -132,6 +133,9 @@ service_manager::mojom::blink::InterfaceProviderPtrInfo interface_provider_info_; + mojo::PendingRemote<mojom::blink::BrowserInterfaceBroker> + browser_interface_broker_; + DISALLOW_COPY_AND_ASSIGN(WebEmbeddedWorkerImpl); };
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc index 9703ade..de71c6e07 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc +++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -10,7 +10,6 @@ #include <limits> #include <string> #include <utility> -#include <vector> #include "base/feature_list.h" #include "base/metrics/field_trial.h" @@ -18,8 +17,6 @@ #include "base/metrics/histogram_macros.h" #include "base/optional.h" #include "base/single_thread_task_runner.h" -#include "base/strings/string_number_conversions.h" -#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" @@ -33,6 +30,7 @@ #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" #include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/webrtc/api/audio/echo_canceller3_config.h" #include "third_party/webrtc/api/audio/echo_canceller3_config_json.h" #include "third_party/webrtc/api/audio/echo_canceller3_factory.h" @@ -462,7 +460,7 @@ // supported. int channels = std::min(2, audio_bus->channels()); - std::vector<const float*> channel_ptrs(channels); + Vector<const float*> channel_ptrs(channels); for (int i = 0; i < channels; ++i) channel_ptrs[i] = audio_bus->channel(i);
diff --git a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc index c293359..c328b94 100644 --- a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc +++ b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc
@@ -55,10 +55,9 @@ const scoped_refptr<media::VideoFrame> frame = media::VideoFrame::CreateColorFrame(format_.frame_size, 0, 0, 0, base::TimeDelta()); - // TODO(crbug.com/964947): Remove the rebind of |frame_callback_|. PostCrossThreadTask( *io_task_runner(), FROM_HERE, - CrossThreadBindRepeating(frame_callback_, frame, base::TimeTicks())); + CrossThreadBindOnce(frame_callback_, frame, base::TimeTicks())); } } @@ -97,11 +96,9 @@ scoped_refptr<media::VideoFrame> frame) { DCHECK(!is_stopped_for_restart_); DCHECK(!frame_callback_.is_null()); - // TODO(crbug.com/964947): Remove the rebind of |frame_callback_|. - PostCrossThreadTask( - *io_task_runner(), FROM_HERE, - CrossThreadBindRepeating(frame_callback_, std::move(frame), - base::TimeTicks())); + PostCrossThreadTask(*io_task_runner(), FROM_HERE, + CrossThreadBindOnce(frame_callback_, std::move(frame), + base::TimeTicks())); } void MockMediaStreamVideoSource::StopSourceForRestartImpl() {
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc index 3a19ebf..71d7734b 100644 --- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc +++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -8,6 +8,7 @@ #include <algorithm> #include <utility> +#include <vector> #include "base/location.h" #include "base/logging.h" @@ -28,6 +29,7 @@ #include "third_party/blink/public/platform/web_media_stream_track.h" #include "third_party/blink/public/platform/web_screen_info.h" #include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h" #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h" #include "third_party/blink/public/web/modules/mediastream/processed_local_audio_source.h" @@ -93,11 +95,11 @@ MediaStreamType* stream_type = &track_controls->stream_type; *stream_type = MediaStreamType::NO_SERVICE; - std::string source_constraint = + String source_constraint = constraints.Basic().media_stream_source.Exact().empty() - ? std::string() - : constraints.Basic().media_stream_source.Exact()[0].Utf8(); - if (!source_constraint.empty()) { + ? String() + : String(constraints.Basic().media_stream_source.Exact()[0]); + if (!source_constraint.IsEmpty()) { if (source_constraint == blink::kMediaStreamSourceTab) { *stream_type = MediaStreamType::GUM_TAB_AUDIO_CAPTURE; } else if (source_constraint == blink::kMediaStreamSourceDesktop || @@ -128,11 +130,11 @@ MediaStreamType* stream_type = &track_controls->stream_type; *stream_type = MediaStreamType::NO_SERVICE; - std::string source_constraint = + String source_constraint = constraints.Basic().media_stream_source.Exact().empty() - ? std::string() - : constraints.Basic().media_stream_source.Exact()[0].Utf8(); - if (!source_constraint.empty()) { + ? String() + : String(constraints.Basic().media_stream_source.Exact()[0]); + if (!source_constraint.IsEmpty()) { if (source_constraint == blink::kMediaStreamSourceTab) { *stream_type = MediaStreamType::GUM_TAB_VIDEO_CAPTURE; } else if (source_constraint == blink::kMediaStreamSourceDesktop || @@ -571,7 +573,7 @@ // will contain the same non-reconfigurable settings that limit the // associated capabilities. blink::MediaStreamAudioSource* audio_source = nullptr; - auto it = + auto* it = std::find_if(local_sources_.begin(), local_sources_.end(), [&device](const blink::WebMediaStreamSource& web_source) { DCHECK(!web_source.IsNull()); @@ -643,7 +645,7 @@ // Create a copy of the blink::WebMediaStreamSource objects that are // associated to the same audio device capture based on its device ID. - std::vector<blink::WebMediaStreamSource> matching_sources; + Vector<blink::WebMediaStreamSource> matching_sources; std::copy_if(local_sources_.begin(), local_sources_.end(), std::back_inserter(matching_sources), [&device_id](const blink::WebMediaStreamSource& web_source) { @@ -653,7 +655,7 @@ // Return the session ID associated to the source that has the same settings // that have been previously selected, if one exists. - if (!matching_sources.empty()) { + if (!matching_sources.IsEmpty()) { for (auto& matching_source : matching_sources) { blink::MediaStreamAudioSource* audio_source = static_cast<blink::MediaStreamAudioSource*>( @@ -932,7 +934,7 @@ const String& result_name) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - for (auto it = pending_local_sources_.begin(); + for (auto* it = pending_local_sources_.begin(); it != pending_local_sources_.end(); ++it) { blink::WebPlatformMediaStreamSource* const source_extra_data = it->GetPlatformSource(); @@ -1205,17 +1207,19 @@ ToStdVector(current_request_info_->video_devices()), weak_factory_.GetWeakPtr()); - blink::WebVector<blink::WebMediaStreamTrack> audio_tracks( + Vector<blink::WebMediaStreamTrack> audio_tracks( current_request_info_->audio_devices().size()); CreateAudioTracks(current_request_info_->audio_devices(), &audio_tracks); - blink::WebVector<blink::WebMediaStreamTrack> video_tracks( + Vector<blink::WebMediaStreamTrack> video_tracks( current_request_info_->video_devices().size()); CreateVideoTracks(current_request_info_->video_devices(), &video_tracks); String blink_id = label; - current_request_info_->web_stream()->Initialize(blink_id, audio_tracks, - video_tracks); + current_request_info_->web_stream()->Initialize( + blink_id, + WebVector<WebMediaStreamTrack>(audio_tracks.data(), audio_tracks.size()), + WebVector<WebMediaStreamTrack>(video_tracks.data(), video_tracks.size())); // Wait for the tracks to be started successfully or to fail. current_request_info_->CallbackOnTracksStarted( @@ -1225,7 +1229,7 @@ void UserMediaProcessor::CreateVideoTracks( const Vector<MediaStreamDevice>& devices, - blink::WebVector<blink::WebMediaStreamTrack>* webkit_tracks) { + Vector<blink::WebMediaStreamTrack>* webkit_tracks) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(current_request_info_); DCHECK_EQ(devices.size(), webkit_tracks->size()); @@ -1240,7 +1244,7 @@ void UserMediaProcessor::CreateAudioTracks( const Vector<MediaStreamDevice>& devices, - blink::WebVector<blink::WebMediaStreamTrack>* webkit_tracks) { + Vector<blink::WebMediaStreamTrack>* webkit_tracks) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(current_request_info_); DCHECK_EQ(devices.size(), webkit_tracks->size()); @@ -1479,7 +1483,7 @@ const blink::WebMediaStreamSource& source) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - for (auto device_it = local_sources_.begin(); + for (auto* device_it = local_sources_.begin(); device_it != local_sources_.end(); ++device_it) { if (IsSameSource(*device_it, source)) { local_sources_.erase(device_it); @@ -1488,7 +1492,7 @@ } // Check if the source was pending. - for (auto device_it = pending_local_sources_.begin(); + for (auto* device_it = pending_local_sources_.begin(); device_it != pending_local_sources_.end(); ++device_it) { if (IsSameSource(*device_it, source)) { blink::WebPlatformMediaStreamSource* const source_extra_data = @@ -1560,7 +1564,7 @@ request_completed_cb_.Reset(); // Loop through all current local sources and stop the sources. - auto it = local_sources_.begin(); + auto* it = local_sources_.begin(); while (it != local_sources_.end()) { StopLocalSource(*it, true); it = local_sources_.erase(it); @@ -1603,7 +1607,7 @@ } bool UserMediaProcessor::HasActiveSources() const { - return !local_sources_.empty(); + return !local_sources_.IsEmpty(); } const blink::mojom::blink::MediaStreamDispatcherHostPtr&
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.h b/third_party/blink/renderer/modules/mediastream/user_media_processor.h index 47d04c6..fcb8d5cc 100644 --- a/third_party/blink/renderer/modules/mediastream/user_media_processor.h +++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.h
@@ -6,9 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_USER_MEDIA_PROCESSOR_H_ #include <memory> -#include <string> #include <utility> -#include <vector> #include "base/callback_forward.h" #include "base/macros.h" @@ -19,7 +17,6 @@ #include "third_party/blink/public/mojom/mediastream/media_devices.mojom-blink.h" #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h" #include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h" -#include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/public/web/web_user_media_request.h" #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.h" #include "third_party/blink/renderer/modules/modules_export.h" @@ -140,7 +137,7 @@ private: class RequestInfo; - using LocalStreamSources = std::vector<blink::WebMediaStreamSource>; + using LocalStreamSources = Vector<blink::WebMediaStreamSource>; void OnStreamGenerated(int request_id, blink::mojom::blink::MediaStreamRequestResult result, @@ -185,13 +182,11 @@ void StartTracks(const String& label); - void CreateVideoTracks( - const Vector<blink::MediaStreamDevice>& devices, - blink::WebVector<blink::WebMediaStreamTrack>* webkit_tracks); + void CreateVideoTracks(const Vector<blink::MediaStreamDevice>& devices, + Vector<blink::WebMediaStreamTrack>* webkit_tracks); - void CreateAudioTracks( - const Vector<blink::MediaStreamDevice>& devices, - blink::WebVector<blink::WebMediaStreamTrack>* webkit_tracks); + void CreateAudioTracks(const Vector<blink::MediaStreamDevice>& devices, + Vector<blink::WebMediaStreamTrack>* webkit_tracks); // Callback function triggered when all native versions of the // underlying media sources and tracks have been created and started.
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc index ed6e4041..b3d54123 100644 --- a/third_party/blink/renderer/modules/modules_initializer.cc +++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -273,8 +273,10 @@ HTMLMediaElementEncryptedMedia::From(html_media_element); WebString sink_id( HTMLMediaElementAudioOutputDevice::sinkId(html_media_element)); + MediaInspectorContextImpl* context_impl = + MediaInspectorContextImpl::FromHtmlMediaElement(html_media_element); return base::WrapUnique(web_frame_client->CreateMediaPlayer( - source, media_player_client, &encrypted_media, + source, media_player_client, context_impl, &encrypted_media, encrypted_media.ContentDecryptionModule(), sink_id, view)); }
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc b/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc index dfe6bdf..54c4299e 100644 --- a/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc +++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc
@@ -688,8 +688,18 @@ return false; if (!frame_width || !frame_height) return false; - if (x_offset + frame_width > width_ || y_offset + frame_height > height_) - return false; + { + png_uint_32 frame_right; + if (!base::CheckAdd(x_offset, frame_width).AssignIfValid(&frame_right) || + frame_right > width_) + return false; + } + { + png_uint_32 frame_bottom; + if (!base::CheckAdd(y_offset, frame_height).AssignIfValid(&frame_bottom) || + frame_bottom > height_) + return false; + } new_frame_.frame_rect = IntRect(x_offset, y_offset, frame_width, frame_height);
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations index 13ac6f1..2b13dab 100644 --- a/third_party/blink/web_tests/MSANExpectations +++ b/third_party/blink/web_tests/MSANExpectations
@@ -52,6 +52,12 @@ crbug.com/810963 [ Linux ] external/wpt/dom/interfaces.html [ Timeout ] +crbug.com/993468 [ Linux ] external/wpt/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-zero.html [ Timeout ] +crbug.com/993468 [ Linux ] external/wpt/html/semantics/document-metadata/the-link-element/stylesheet-not-removed-until-next-stylesheet-loads.html [ Timeout ] +crbug.com/993468 [ Linux ] external/wpt/infrastructure/reftest/reftest_ref_timeout.html [ Timeout ] +crbug.com/993468 [ Linux ] external/wpt/vibration/idlharness.window.html [ Timeout ] +crbug.com/993468 [ Linux ] external/wpt/xslt/idlharness.tentative.window.html [ Timeout ] + # Intentionally failed allocations, via partitionAllocGenericFlags() crbug.com/577889 [ Linux ] fast/js/typed-array-allocation-failure.html [ Crash ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index f9956169..5e50d8c 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -241,6 +241,11 @@ crbug.com/857490 virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-fling-with-page-scale.html [ Skip ] crbug.com/857490 virtual/scroll_customization/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html [ Skip ] +# Currently on main thread scrolling path, scrollend gets fired without waiting for scroll/fling snap animation. (The virtual/threaded version of the test passes.) +crbug.com/907601 fast/scrolling/events/scrollend-event-fired-after-snap.html [ Skip ] +crbug.com/907601 virtual/scroll_customization/fast/scrolling/events/scrollend-event-fired-after-snap.html [ Skip ] +crbug.com/907601 external/wpt/dom/events/scrolling/scrollend-event-fired-after-snap.html [ Skip ] + crbug.com/980969 [ Mac ] http/tests/input/discard-events-to-unstable-iframe.html [ Pass Failure ] # Display locking, run highlight-display-locked.js only with display locking. @@ -1879,10 +1884,8 @@ ### virtual/layout_ng_experimental/css3/flexbox/ crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/auto-height-column-with-border-and-padding.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/auto-height-with-flex.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/box-sizing-min-max-sizes.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/columns-height-set-via-top-bottom.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/content-height-with-scrollbars.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flex-align-baseline.html [ Skip ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flex-align-column.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flex-align-end.html [ Failure ] @@ -1900,7 +1903,6 @@ crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flex-order.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flexbox-baseline-margins.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flexbox-baseline.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flexbox-height-with-overflow-auto.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flexbox-overflow-auto.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/flexitem.html [ Crash Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/inline-flexbox-wrap-vertically-width-calculation.html [ Failure ] @@ -1913,13 +1915,11 @@ crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/multiline-reverse-wrap-overflow.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/multiline.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/negative-overflow.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/nested-stretch.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/order-painting.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/overflow-auto-resizes-correctly.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/overflow-keep-scrollpos.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/percentage-height-replaced-element.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/position-absolute-child.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/preferred-widths.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/relayout-align-items.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/relpos-with-percentage-top.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/stretch-input-in-column.html [ Failure ] @@ -1930,7 +1930,6 @@ ### virtual/layout_ng_experimental/css3/flexbox/mozilla/ crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/mozilla/flexbox-items-as-stacking-contexts-2.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/mozilla/flexbox-sizing-vert-1.xhtml [ Failure ] ### virtual/layout_ng_experimental/external/wpt/css/css-flexbox/ crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/Flexible-order.html [ Failure ] @@ -1986,14 +1985,12 @@ crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_align-self-flexstart.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_align-self-stretch.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_direction-column-reverse.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_direction-column.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_flow-column-reverse-wrap-reverse.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_flow-column-reverse-wrap.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_flow-column-wrap-reverse.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_flow-row-wrap-reverse.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_inline.html [ Failure ] crbug.com/467127 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_justifycontent-center-overflow.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_margin-collapse.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_order-box.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_order.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flexbox_quirks_body.html [ Failure ] @@ -2015,7 +2012,6 @@ crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/scrollbars.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/table-as-item-auto-min-width.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/ttwf-reftest-flex-direction-column-reverse.html [ Failure ] -crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/ttwf-reftest-flex-direction-column.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/ttwf-reftest-flex-inline.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/ttwf-reftest-flex-order.html [ Failure ] crbug.com/467127 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/ttwf-reftest-flex-wrap-reverse.html [ Failure ] @@ -3246,7 +3242,6 @@ # Various menulist appearance failures in WPT crbug.com/968164 external/wpt/css/css-ui/appearance-menulist-button-002.html [ Failure ] crbug.com/968164 external/wpt/css/css-ui/webkit-appearance-menulist-001.html [ Failure ] -crbug.com/968164 external/wpt/css/css-ui/webkit-appearance-menulist-button-001.html [ Failure ] # Implement text-decoration-thickness and text-decoration-offset crbug.com/785230 external/wpt/css/css-text-decor/text-underline-offset-002.html [ Failure ] @@ -3262,6 +3257,11 @@ crbug.com/991295 external/wpt/web-animations/timing-model/timelines/document-timelines.html [ Pass Failure ] # ====== New tests from wpt-importer added here ====== +crbug.com/626703 [ Linux ] external/wpt/css/css-ui/appearance-textfield-001.html [ Failure ] +crbug.com/626703 [ Mac ] external/wpt/css/css-ui/appearance-textfield-001.html [ Failure ] +crbug.com/626703 [ Win ] external/wpt/css/css-ui/appearance-textfield-001.html [ Failure ] +crbug.com/626703 [ Linux ] external/wpt/css/css-ui/webkit-appearance-textfield-001.html [ Failure ] +crbug.com/626703 [ Win ] external/wpt/css/css-ui/webkit-appearance-textfield-001.html [ Failure ] crbug.com/626703 [ Win7 ] external/wpt/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/img-tag/keep-origin-redirect/same-origin-insecure.http.html [ Timeout ] crbug.com/626703 [ Win7 ] external/wpt/referrer-policy/unset-referrer-policy/http-rp/same-origin/http-http/img-tag/swap-origin-redirect/insecure-protocol.http.html [ Timeout ] crbug.com/626703 [ Win7 ] external/wpt/referrer-policy/unsafe-url/attr-referrer/cross-origin/http-http/img-tag/no-redirect/generic.http.html [ Timeout ] @@ -3591,26 +3591,16 @@ crbug.com/626703 [ Retina ] external/wpt/preload/onerror-event.html [ Timeout ] crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-digits-001-manual.html [ Skip ] crbug.com/626703 external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement.html [ Timeout ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-radio-001.html [ Failure ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-square-button-001.html [ Failure ] crbug.com/626703 external/wpt/webrtc/RTCRtpTransceiver.https.html [ Crash Timeout ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-push-button-001.html [ Failure ] crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-all-001-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-textarea-001.html [ Failure ] crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-digits-002-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-checkbox-001.html [ Failure ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-progress-bar-001.html [ Failure ] crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-button-bevel-001.html [ Failure ] crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-all-002-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-meter-001.html [ Failure ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-searchfield-001.html [ Failure ] crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.html [ Timeout ] crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-auto-001.html [ Failure ] crbug.com/626703 external/wpt/web-animations/interfaces/Animation/persist.html [ Timeout ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-listbox-001.html [ Failure ] crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-digits-004-manual.html [ Skip ] crbug.com/626703 external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.html [ Timeout ] -crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-slider-horizontal-001.html [ Failure ] crbug.com/626703 [ Mac10.13 ] external/wpt/preload/preload-with-type.html [ Failure Timeout ] crbug.com/626703 [ Retina ] external/wpt/preload/preload-with-type.html [ Timeout ] crbug.com/626703 [ Mac10.13 ] external/wpt/preload/onload-event.html [ Failure Timeout ] @@ -6286,9 +6276,6 @@ crbug.com/974334 [ Mac ] external/wpt/html/cross-origin/null.tentative.html [ Failure ] crbug.com/974334 [ Mac ] external/wpt/html/cross-origin/usecredentials.tentative.html [ Failure ] -# Flaky on Linux, failing on macOS -crbug.com/984926 [ Linux Mac ] http/tests/devtools/elements/highlight/highlight-node-vertical-rl.js [ Pass Failure ] - # Sheriff 2019-06-20 crbug.com/977153 [ Mac ] fast/forms/validation-bubble-appearance-wrap.html [ Pass Failure ] crbug.com/976045 fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json index dee4514..b22b82c 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -87267,6 +87267,18 @@ {} ] ], + "css/css-ui/appearance-textfield-001.html": [ + [ + "css/css-ui/appearance-textfield-001.html", + [ + [ + "/css/css-ui/appearance-textfield-001-ref.html", + "==" + ] + ], + {} + ] + ], "css/css-ui/box-sizing-001.html": [ [ "css/css-ui/box-sizing-001.html", @@ -88383,6 +88395,18 @@ {} ] ], + "css/css-ui/webkit-appearance-textfield-001.html": [ + [ + "css/css-ui/webkit-appearance-textfield-001.html", + [ + [ + "/css/css-ui/appearance-textfield-001-ref.html", + "==" + ] + ], + {} + ] + ], "css/css-values/angle-units-001.html": [ [ "css/css-values/angle-units-001.html", @@ -145532,6 +145556,9 @@ "css/css-ui/appearance-serialization-expected.txt": [ [] ], + "css/css-ui/appearance-textfield-001-ref.html": [ + [] + ], "css/css-ui/inheritance-expected.txt": [ [] ], @@ -393324,6 +393351,14 @@ "5c55ff9fbf6fd993aaf51114c175414a9ec8fe0b", "reftest" ], + "css/css-ui/appearance-textfield-001-ref.html": [ + "5304352e6bafd1d25436babfc2afc0ba1aa26fa3", + "support" + ], + "css/css-ui/appearance-textfield-001.html": [ + "f2ca6fc6ac752085ef9bbf3593b94097a8c47c81", + "reftest" + ], "css/css-ui/box-sizing-001.html": [ "545403f535d2f33993558bde9086e8798c04c11f", "reftest" @@ -395968,6 +396003,10 @@ "cf3a15f4db6c9745de687cb38f593132d5ddf2f8", "reftest" ], + "css/css-ui/webkit-appearance-textfield-001.html": [ + "058e1be83bbf924d949f6b9665e456dbb932d388", + "reftest" + ], "css/css-values/META.yml": [ "a22882a9996b14afa942d3403fa1a873f526073a", "support"
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/CSSAnimation-id.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-animations/CSSAnimation-id.tentative-expected.txt deleted file mode 100644 index ba5b2dd..0000000 --- a/third_party/blink/web_tests/external/wpt/css/css-animations/CSSAnimation-id.tentative-expected.txt +++ /dev/null
@@ -1,4 +0,0 @@ -This is a testharness.js-based test. -FAIL Animation.id for CSS Animations assert_equals: id for CSS Animation is initially empty expected "" but got "abc" -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-textfield-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-textfield-001-ref.html new file mode 100644 index 0000000..5304352e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-textfield-001-ref.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Reference for CSS Basic User Interface Test: appearance: textfield</title> +<style> + #container { width: 500px; } +</style> +<div id="container"> + <a>a</a> + <button>button</button> + <input type="text" value="input-text"> + <input type="text" value="input-search"><!-- intentionally type="text" --> + <textarea>textarea</textarea> + <input type="button" value="input-button"> + <input type="submit" value="input-submit"> + <input type="reset" value="input-reset"> + <input type="range"> + <input type="checkbox"> + <input type="radio"> + <input type="color"> + <select><option>select</option></select> + <select multiple><option>select-multiple</option></select> + <meter value=0.5></meter> + <progress value=0.5></progress> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-textfield-001.html b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-textfield-001.html new file mode 100644 index 0000000..f2ca6fc --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-textfield-001.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Basic User Interface Test: appearance: textfield</title> +<link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching"> +<meta name="assert" content="textfield is an alias to auto except on input type=search."> +<link rel="match" href="appearance-textfield-001-ref.html"> +<style> + #container { width: 500px; } + /* + * If the value being tested is not supported, then 'none' is used instead, + * which is intended to fail the test. + */ + #container > * { appearance: none; appearance: textfield; } +</style> +<div id="container"> + <a>a</a> + <button>button</button> + <input type="text" value="input-text"> + <input type="search" value="input-search"> + <textarea>textarea</textarea> + <input type="button" value="input-button"> + <input type="submit" value="input-submit"> + <input type="reset" value="input-reset"> + <input type="range"> + <input type="checkbox"> + <input type="radio"> + <input type="color"> + <select><option>select</option></select> + <select multiple><option>select-multiple</option></select> + <meter value=0.5></meter> + <progress value=0.5></progress> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-textfield-001.html b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-textfield-001.html new file mode 100644 index 0000000..058e1be --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-textfield-001.html
@@ -0,0 +1,36 @@ +<!-- DO NOT EDIT THIS FILE. +Edit the appearance-* file instead and then run: + ./tools/appearance-build-webkit-reftests.py +--> +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Basic User Interface Test: -webkit-appearance: textfield</title> +<link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching"> +<meta name="assert" content="textfield is an alias to auto except on input type=search."> +<link rel="match" href="appearance-textfield-001-ref.html"> +<style> + #container { width: 500px; } + /* + * If the value being tested is not supported, then 'none' is used instead, + * which is intended to fail the test. + */ + #container > * { -webkit-appearance: none; -webkit-appearance: textfield; } +</style> +<div id="container"> + <a>a</a> + <button>button</button> + <input type="text" value="input-text"> + <input type="search" value="input-search"> + <textarea>textarea</textarea> + <input type="button" value="input-button"> + <input type="submit" value="input-submit"> + <input type="reset" value="input-reset"> + <input type="range"> + <input type="checkbox"> + <input type="radio"> + <input type="color"> + <select><option>select</option></select> + <select multiple><option>select-multiple</option></select> + <meter value=0.5></meter> + <progress value=0.5></progress> +</div>
diff --git a/third_party/blink/web_tests/fast/scrolling/events/scrollend-event-fired-after-snap.html b/third_party/blink/web_tests/fast/scrolling/events/scrollend-event-fired-after-snap.html index f083622..ad739c4 100644 --- a/third_party/blink/web_tests/fast/scrolling/events/scrollend-event-fired-after-snap.html +++ b/third_party/blink/web_tests/fast/scrolling/events/scrollend-event-fired-after-snap.html
@@ -25,14 +25,12 @@ } </style> -<body style="margin:0" onload=runTests()> - <div id="scroller"> - <div id="space"></div> - <div class="target" style="left: 0px; top: 0px;"></div> - <div class="target" style="left: 80px; top: 80px;"></div> - <div class="target" style="left: 200px; top: 200px;"></div> - </div> -</body> +<div id="scroller"> + <div id="space"></div> + <div class="target" style="left: 0px; top: 0px;"></div> + <div class="target" style="left: 80px; top: 80px;"></div> + <div class="target" style="left: 200px; top: 200px;"></div> +</div> <script> if (window.internals) @@ -56,30 +54,28 @@ scroller.addEventListener("scrollend", () => { scroll_end_arrived = true; }); -function runTests() { - promise_test (async () => { - await waitForCompositorCommit(); - await smoothScroll(100, 200, 200, GestureSourceType.TOUCH_INPUT, 'down'); - // Wait for the scroll snap animation to finish. - await waitForAnimationEnd(scrollLeft, MAX_FRAME_COUNT, MAX_UNCHANGED_FRAME); - await waitFor(() => { return scroll_end_arrived; }); - // Verify that scroll snap animation has finished before firing scrollend event. - assert_false(scroll_arrived_after_scroll_end); - }, "Tests that scrollend is fired after scroll snap animation completion."); +promise_test (async () => { + await waitForCompositorCommit(); + await smoothScroll(100, 200, 200, GestureSourceType.TOUCH_INPUT, 'down'); + // Wait for the scroll snap animation to finish. + await waitForAnimationEnd(scrollLeft, MAX_FRAME_COUNT, MAX_UNCHANGED_FRAME); + await waitFor(() => { return scroll_end_arrived; }); + // Verify that scroll snap animation has finished before firing scrollend event. + assert_false(scroll_arrived_after_scroll_end); +}, "Tests that scrollend is fired after scroll snap animation completion."); - promise_test (async () => { - // Reset scroll state. - scroller.scrollTo(0, 0); - await waitForCompositorCommit(); - scroll_end_arrived = false; - scroll_arrived_after_scroll_end = false; +promise_test (async () => { + // Reset scroll state. + scroller.scrollTo(0, 0); + await waitForCompositorCommit(); + scroll_end_arrived = false; + scroll_arrived_after_scroll_end = false; - await swipe(100, 200, 200, 'up'); - // Wait for the scroll snap animation to finish. - await waitForAnimationEnd(scrollLeft, MAX_FRAME_COUNT, MAX_UNCHANGED_FRAME); - await waitFor(() => { return scroll_end_arrived; }); - // Verify that scroll snap animation has finished before firing scrollend event. - assert_false(scroll_arrived_after_scroll_end); - }, "Tests that scrollend is fired after fling snap animation completion."); -} + await swipe(100, 200, 200, 'up'); + // Wait for the scroll snap animation to finish. + await waitForAnimationEnd(scrollLeft, MAX_FRAME_COUNT, MAX_UNCHANGED_FRAME); + await waitFor(() => { return scroll_end_arrived; }); + // Verify that scroll snap animation has finished before firing scrollend event. + assert_false(scroll_arrived_after_scroll_end); +}, "Tests that scrollend is fired after fling snap animation completion."); </script>
diff --git a/third_party/blink/web_tests/platform/win/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt similarity index 100% rename from third_party/blink/web_tests/platform/win/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt rename to third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt
diff --git a/third_party/blink/web_tests/platform/linux/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt b/third_party/blink/web_tests/platform/linux/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt deleted file mode 100644 index 47b4f35..0000000 --- a/third_party/blink/web_tests/platform/linux/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt +++ /dev/null
@@ -1,358 +0,0 @@ - - -container{ - "paths": [ - { - "path": [ - "M", - 80, - 50, - "L", - 380, - 50, - "L", - 380, - 350, - "L", - 80, - 350, - "Z" - ], - "fillColor": "rgba(255, 0, 0, 0)", - "outlineColor": "rgba(128, 0, 0, 0)", - "name": "content" - }, - { - "path": [ - "M", - 80, - 50, - "L", - 380, - 50, - "L", - 380, - 350, - "L", - 80, - 350, - "Z" - ], - "fillColor": "rgba(0, 255, 0, 0)", - "name": "padding" - }, - { - "path": [ - "M", - 80, - 50, - "L", - 380, - 50, - "L", - 380, - 350, - "L", - 80, - 350, - "Z" - ], - "fillColor": "rgba(0, 0, 255, 0)", - "name": "border" - }, - { - "path": [ - "M", - 0, - 0, - "L", - 412, - 0, - "L", - 412, - 420, - "L", - 0, - 420, - "Z" - ], - "fillColor": "rgba(255, 255, 255, 0)", - "name": "margin" - } - ], - "showRulers": true, - "showExtensionLines": true, - "elementInfo": { - "tagName": "div", - "idValue": "container", - "nodeWidth": "300", - "nodeHeight": "300" - } -} -child{ - "paths": [ - { - "path": [ - "M", - 220, - 100, - "L", - 320, - 100, - "L", - 320, - 200, - "L", - 220, - 200, - "Z" - ], - "fillColor": "rgba(255, 0, 0, 0)", - "outlineColor": "rgba(128, 0, 0, 0)", - "name": "content" - }, - { - "path": [ - "M", - 140, - 50, - "L", - 380, - 50, - "L", - 380, - 270, - "L", - 140, - 270, - "Z" - ], - "fillColor": "rgba(0, 255, 0, 0)", - "name": "padding" - }, - { - "path": [ - "M", - 140, - 50, - "L", - 380, - 50, - "L", - 380, - 270, - "L", - 140, - 270, - "Z" - ], - "fillColor": "rgba(0, 0, 255, 0)", - "name": "border" - }, - { - "path": [ - "M", - 140, - 50, - "L", - 380, - 50, - "L", - 380, - 350, - "L", - 140, - 350, - "Z" - ], - "fillColor": "rgba(255, 255, 255, 0)", - "name": "margin" - } - ], - "showRulers": true, - "showExtensionLines": true, - "elementInfo": { - "tagName": "div", - "idValue": "child", - "nodeWidth": "240", - "nodeHeight": "220" - } -} -span{ - "paths": [ - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(255, 0, 0, 0)", - "outlineColor": "rgba(128, 0, 0, 0)", - "name": "content" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(0, 255, 0, 0)", - "name": "padding" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(0, 0, 255, 0)", - "name": "border" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(255, 255, 255, 0)", - "name": "margin" - } - ], - "showRulers": true, - "showExtensionLines": true, - "elementInfo": { - "tagName": "span", - "idValue": "span", - "nodeWidth": "10", - "nodeHeight": "70" - } -} -TEXT{ - "paths": [ - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(255, 0, 0, 0)", - "outlineColor": "rgba(128, 0, 0, 0)", - "name": "content" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(0, 255, 0, 0)", - "name": "padding" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(0, 0, 255, 0)", - "name": "border" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(255, 255, 255, 0)", - "name": "margin" - } - ], - "showRulers": true, - "showExtensionLines": true, - "elementInfo": { - "nodeWidth": "10", - "nodeHeight": "70", - "tagName": "#text" - } -} -
diff --git a/third_party/blink/web_tests/platform/mac/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt b/third_party/blink/web_tests/platform/mac/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt deleted file mode 100644 index 47b4f35..0000000 --- a/third_party/blink/web_tests/platform/mac/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt +++ /dev/null
@@ -1,358 +0,0 @@ - - -container{ - "paths": [ - { - "path": [ - "M", - 80, - 50, - "L", - 380, - 50, - "L", - 380, - 350, - "L", - 80, - 350, - "Z" - ], - "fillColor": "rgba(255, 0, 0, 0)", - "outlineColor": "rgba(128, 0, 0, 0)", - "name": "content" - }, - { - "path": [ - "M", - 80, - 50, - "L", - 380, - 50, - "L", - 380, - 350, - "L", - 80, - 350, - "Z" - ], - "fillColor": "rgba(0, 255, 0, 0)", - "name": "padding" - }, - { - "path": [ - "M", - 80, - 50, - "L", - 380, - 50, - "L", - 380, - 350, - "L", - 80, - 350, - "Z" - ], - "fillColor": "rgba(0, 0, 255, 0)", - "name": "border" - }, - { - "path": [ - "M", - 0, - 0, - "L", - 412, - 0, - "L", - 412, - 420, - "L", - 0, - 420, - "Z" - ], - "fillColor": "rgba(255, 255, 255, 0)", - "name": "margin" - } - ], - "showRulers": true, - "showExtensionLines": true, - "elementInfo": { - "tagName": "div", - "idValue": "container", - "nodeWidth": "300", - "nodeHeight": "300" - } -} -child{ - "paths": [ - { - "path": [ - "M", - 220, - 100, - "L", - 320, - 100, - "L", - 320, - 200, - "L", - 220, - 200, - "Z" - ], - "fillColor": "rgba(255, 0, 0, 0)", - "outlineColor": "rgba(128, 0, 0, 0)", - "name": "content" - }, - { - "path": [ - "M", - 140, - 50, - "L", - 380, - 50, - "L", - 380, - 270, - "L", - 140, - 270, - "Z" - ], - "fillColor": "rgba(0, 255, 0, 0)", - "name": "padding" - }, - { - "path": [ - "M", - 140, - 50, - "L", - 380, - 50, - "L", - 380, - 270, - "L", - 140, - 270, - "Z" - ], - "fillColor": "rgba(0, 0, 255, 0)", - "name": "border" - }, - { - "path": [ - "M", - 140, - 50, - "L", - 380, - 50, - "L", - 380, - 350, - "L", - 140, - 350, - "Z" - ], - "fillColor": "rgba(255, 255, 255, 0)", - "name": "margin" - } - ], - "showRulers": true, - "showExtensionLines": true, - "elementInfo": { - "tagName": "div", - "idValue": "child", - "nodeWidth": "240", - "nodeHeight": "220" - } -} -span{ - "paths": [ - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(255, 0, 0, 0)", - "outlineColor": "rgba(128, 0, 0, 0)", - "name": "content" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(0, 255, 0, 0)", - "name": "padding" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(0, 0, 255, 0)", - "name": "border" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(255, 255, 255, 0)", - "name": "margin" - } - ], - "showRulers": true, - "showExtensionLines": true, - "elementInfo": { - "tagName": "span", - "idValue": "span", - "nodeWidth": "10", - "nodeHeight": "70" - } -} -TEXT{ - "paths": [ - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(255, 0, 0, 0)", - "outlineColor": "rgba(128, 0, 0, 0)", - "name": "content" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(0, 255, 0, 0)", - "name": "padding" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(0, 0, 255, 0)", - "name": "border" - }, - { - "path": [ - "M", - 310, - 100, - "L", - 320, - 100, - "L", - 320, - 170, - "L", - 310, - 170, - "Z" - ], - "fillColor": "rgba(255, 255, 255, 0)", - "name": "margin" - } - ], - "showRulers": true, - "showExtensionLines": true, - "elementInfo": { - "nodeWidth": "10", - "nodeHeight": "70", - "tagName": "#text" - } -} -
diff --git a/third_party/libaddressinput/chromium/chrome_metadata_source.cc b/third_party/libaddressinput/chromium/chrome_metadata_source.cc index 02f74d5..e4633c9e 100644 --- a/third_party/libaddressinput/chromium/chrome_metadata_source.cc +++ b/third_party/libaddressinput/chromium/chrome_metadata_source.cc
@@ -90,8 +90,7 @@ })"); auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = resource; - resource_request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; std::unique_ptr<network::SimpleURLLoader> loader = network::SimpleURLLoader::Create(std::move(resource_request), traffic_annotation);
diff --git a/third_party/lottie/README.chromium b/third_party/lottie/README.chromium index 5ef3a69..3e46edc 100644 --- a/third_party/lottie/README.chromium +++ b/third_party/lottie/README.chromium
@@ -16,4 +16,4 @@ Generating Minified Version: To generate the minified version use node's uglify-js- -$ node uglifyjs --compress --mangle reserved=['onmessage'] lottie_worker.js -o lottie_worker.min.js -b beautify=false,ascii_only=true +$ node uglifyjs --compress --mangle reserved=['onmessage', 'postMessage'] lottie_worker.js -o lottie_worker.min.js -b beautify=false,ascii_only=true
diff --git a/third_party/lottie/README.md b/third_party/lottie/README.md index 6d86f99..b92e3ad 100644 --- a/third_party/lottie/README.md +++ b/third_party/lottie/README.md
@@ -54,3 +54,30 @@ } }, ``` + +## Events fired back to the parent thread +The web worker running the lottie player will send the following events back to +its parent thread: +1. **'initialized'** +```javascript +{ + name: 'initialized', + success: true/false // True if the animation was successfully initialized. +} +``` +2. **'playing'** +```javascript +{ + name: 'playing' +} +``` +3. **'resized'** +```javascript +{ + name: 'resized', + size: { + height: HEIGHT, // Current height of canvas in pixels. + width: WIDTH // Current width of canvas in pixels. + } +} +```
diff --git a/third_party/lottie/lottie_worker.js b/third_party/lottie/lottie_worker.js index fdf9a1a..755de87 100644 --- a/third_party/lottie/lottie_worker.js +++ b/third_party/lottie/lottie_worker.js
@@ -12150,47 +12150,117 @@ * a wrapper that manages animation control for each animation. */ +/** + * An instance of the currently running lottie animation. + * @type {?AnimationItem} + */ var currentAnimation = null; +/** + * Events sent back to the parent thread. + */ +var events = { + INITIALIZED: 'initialized', // Send when the animation was successfully + // initialized. + RESIZED: 'resized', // Sent when the animation has been resized. + PLAYING: 'playing' // Send when the animation started playing. +}; + +/** + * Returns the size of the canvas on which the current animation is running on. + * @return {Object<string, number>} Returns the current size of canvas + */ +getCurrentCanvasSize = function() { + var canvas = currentAnimation.renderer.canvasContext.canvas; + return { + height: canvas.height, + width: canvas.width + }; +} + +/** + * Informs the parent thread that the canvas has been resized and also sends the + * new size. + */ +sendResizeEvent = function() { + var canvas = currentAnimation.renderer.canvasContext.canvas; + postMessage({ + name: events.RESIZED, + size: getCurrentCanvasSize() + }); +}; + +/** + * Informs the parent thread that the animation has started playing. + */ +sendPlayEvent = function() { + postMessage({ + name: events.PLAYING + }); +} + +/** + * Informs the parent thread that the animation has finished initializing. + */ +sendInitializedEvent = function() { + var canvas = currentAnimation.renderer.canvasContext.canvas; + postMessage({ + name: events.INITIALIZED, + success: currentAnimation.isLoaded + }) +} + onmessage = function(evt) { - if (!evt || !evt.data) return; + if (!evt || !evt.data) return; - var canvas = null; + var canvas = null; + if (currentAnimation) { + canvas = currentAnimation.renderer.canvasContext.canvas; + } else if (evt.data.canvas) { + canvas = evt.data.canvas; + } else { + return; + } + + // Set the draw size of the canvas. This is the pixel size at which the + // lottie renderer will render the frames. + if (evt.data.drawSize && evt.data.drawSize.height > 0 && + evt.data.drawSize.width > 0) { + canvas.height = evt.data.drawSize.height; + canvas.width = evt.data.drawSize.width; + + // Update lottie player to use the new canvas size. if (currentAnimation) { - canvas = currentAnimation.renderer.canvasContext.canvas; - } else if (evt.data.canvas) { - canvas = evt.data.canvas; - } else { - return; + currentAnimation.resize(); + sendResizeEvent(); + } + } + + if (!currentAnimation) { + if (!evt.data.animationData || !evt.data.params) + return; + var params = evt.data.params; + var ctx = canvas.getContext("2d"); + currentAnimation = lottiejs.loadAnimation({ + renderer: 'canvas', + loop: params.loop, + autoplay: params.autoplay, + animationData: evt.data.animationData, + rendererSettings: { + context: ctx, + scaleMode: 'noScale', + clearCanvas: true + } + }); + + sendInitializedEvent(); + + if (params.autoplay) { + currentAnimation.play(); } - // Set the draw size of the canvas. This is the pixel size at which the - // lottie renderer will render the frames. - if (evt.data.drawSize) { - canvas.height = evt.data.drawSize.height; - canvas.width = evt.data.drawSize.width; - - // Update lottie player to use the new canvas size. - if (currentAnimation) - currentAnimation.resize(); + if (currentAnimation.isLoaded && !currentAnimation.isPaused) { + sendPlayEvent(); } - - if (!currentAnimation) { - if (!evt.data.animationData || !evt.data.params) - return; - var params = evt.data.params; - var ctx = canvas.getContext("2d"); - currentAnimation = lottiejs.loadAnimation({ - renderer: 'canvas', - loop: params.loop, - autoplay: params.autoplay, - animationData: evt.data.animationData, - rendererSettings: { - context: ctx, - scaleMode: 'noScale', - clearCanvas: true - } - }); - currentAnimation.play(); - } + } };
diff --git a/third_party/lottie/lottie_worker.min.js b/third_party/lottie/lottie_worker.min.js index 7626d80..3e2495ee 100644 --- a/third_party/lottie/lottie_worker.min.js +++ b/third_party/lottie/lottie_worker.min.js
@@ -1 +1 @@ -var lottiejs=function(window){"use strict";var svgNS="http://www.w3.org/2000/svg",locationHref="",initialDefaultFrame=-999999,subframeEnabled=!0,expressionsPlugin,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent),cachedColors={},bm_rounder=Math.round,bm_rnd,bm_pow=Math.pow,bm_sqrt=Math.sqrt,bm_abs=Math.abs,bm_floor=Math.floor,bm_max=Math.max,bm_min=Math.min,blitter=10,BMMath={};function ProjectInterface(){return{}}!function(){var t,e=["abs","acos","acosh","asin","asinh","atan","atanh","atan2","ceil","cbrt","expm1","clz32","cos","cosh","exp","floor","fround","hypot","imul","log","log1p","log2","log10","max","min","pow","random","round","sign","sin","sinh","sqrt","tan","tanh","trunc","E","LN10","LN2","LOG10E","LOG2E","PI","SQRT1_2","SQRT2"],r=e.length;for(t=0;t<r;t+=1)BMMath[e[t]]=Math[e[t]]}(),BMMath.random=Math.random,BMMath.abs=function(t){if("object"==typeof t&&t.length){var e,r=createSizedArray(t.length),i=t.length;for(e=0;e<i;e+=1)r[e]=Math.abs(t[e]);return r}return Math.abs(t)};var defaultCurveSegments=150,degToRads=Math.PI/180,roundCorner=.5519;function roundValues(t){bm_rnd=t?Math.round:function(t){return t}}function styleDiv(t){t.style.position="absolute",t.style.top=0,t.style.left=0,t.style.display="block",t.style.transformOrigin=t.style.webkitTransformOrigin="0 0",t.style.backfaceVisibility=t.style.webkitBackfaceVisibility="visible",t.style.transformStyle=t.style.webkitTransformStyle=t.style.mozTransformStyle="preserve-3d"}function BMEnterFrameEvent(t,e,r,i){this.type=t,this.currentTime=e,this.totalTime=r,this.direction=i<0?-1:1}function BMCompleteEvent(t,e){this.type=t,this.direction=e<0?-1:1}function BMCompleteLoopEvent(t,e,r,i){this.type=t,this.currentLoop=r,this.totalLoops=e,this.direction=i<0?-1:1}function BMSegmentStartEvent(t,e,r){this.type=t,this.firstFrame=e,this.totalFrames=r}function BMDestroyEvent(t,e){this.type=t,this.target=e}roundValues(!1);var createElementID=(B=0,function(){return"__lottie_element_"+ ++B}),B;function HSVtoRGB(t,e,r){var i,s,a,n,o,h,p,l;switch(h=r*(1-e),p=r*(1-(o=6*t-(n=Math.floor(6*t)))*e),l=r*(1-(1-o)*e),n%6){case 0:i=r,s=l,a=h;break;case 1:i=p,s=r,a=h;break;case 2:i=h,s=r,a=l;break;case 3:i=h,s=p,a=r;break;case 4:i=l,s=h,a=r;break;case 5:i=r,s=h,a=p}return[i,s,a]}function RGBtoHSV(t,e,r){var i,s=Math.max(t,e,r),a=Math.min(t,e,r),n=s-a,o=0===s?0:n/s,h=s/255;switch(s){case a:i=0;break;case t:i=e-r+n*(e<r?6:0),i/=6*n;break;case e:i=r-t+2*n,i/=6*n;break;case r:i=t-e+4*n,i/=6*n}return[i,o,h]}function addSaturationToRGB(t,e){var r=RGBtoHSV(255*t[0],255*t[1],255*t[2]);return r[1]+=e,1<r[1]?r[1]=1:r[1]<=0&&(r[1]=0),HSVtoRGB(r[0],r[1],r[2])}function addBrightnessToRGB(t,e){var r=RGBtoHSV(255*t[0],255*t[1],255*t[2]);return r[2]+=e,1<r[2]?r[2]=1:r[2]<0&&(r[2]=0),HSVtoRGB(r[0],r[1],r[2])}function addHueToRGB(t,e){var r=RGBtoHSV(255*t[0],255*t[1],255*t[2]);return r[0]+=e/360,1<r[0]?r[0]-=1:r[0]<0&&(r[0]+=1),HSVtoRGB(r[0],r[1],r[2])}var rgbToHex=function(){var t,e,i=[];for(t=0;t<256;t+=1)e=t.toString(16),i[t]=1==e.length?"0"+e:e;return function(t,e,r){return t<0&&(t=0),e<0&&(e=0),r<0&&(r=0),"#"+i[t]+i[e]+i[r]}}();function BaseEvent(){}BaseEvent.prototype={triggerEvent:function(t,e){if(this._cbs[t])for(var r=this._cbs[t].length,i=0;i<r;i++)this._cbs[t][i](e)},addEventListener:function(t,e){return this._cbs[t]||(this._cbs[t]=[]),this._cbs[t].push(e),function(){this.removeEventListener(t,e)}.bind(this)},removeEventListener:function(t,e){if(e){if(this._cbs[t]){for(var r=0,i=this._cbs[t].length;r<i;)this._cbs[t][r]===e&&(this._cbs[t].splice(r,1),r-=1,i-=1),r+=1;this._cbs[t].length||(this._cbs[t]=null)}}else this._cbs[t]=null}};var createTypedArray="function"==typeof Uint8ClampedArray&&"function"==typeof Float32Array?function(t,e){return"float32"===t?new Float32Array(e):"int16"===t?new Int16Array(e):"uint8c"===t?new Uint8ClampedArray(e):void 0}:function(t,e){var r,i=0,s=[];switch(t){case"int16":case"uint8c":r=1;break;default:r=1.1}for(i=0;i<e;i+=1)s.push(r);return s};function createSizedArray(t){return Array.apply(null,{length:t})}function createTag(t){return document.createElement(t)}function DynamicPropertyContainer(){}DynamicPropertyContainer.prototype={addDynamicProperty:function(t){-1===this.dynamicProperties.indexOf(t)&&(this.dynamicProperties.push(t),this.container.addDynamicProperty(this),this._isAnimated=!0)},iterateDynamicProperties:function(){this._mdf=!1;var t,e=this.dynamicProperties.length;for(t=0;t<e;t+=1)this.dynamicProperties[t].getValue(),this.dynamicProperties[t]._mdf&&(this._mdf=!0)},initDynamicPropertyContainer:function(t){this.container=t,this.dynamicProperties=[],this._mdf=!1,this._isAnimated=!1}};var getBlendMode=(Ja={0:"source-over",1:"multiply",2:"screen",3:"overlay",4:"darken",5:"lighten",6:"color-dodge",7:"color-burn",8:"hard-light",9:"soft-light",10:"difference",11:"exclusion",12:"hue",13:"saturation",14:"color",15:"luminosity"},function(t){return Ja[t]||""}),Ja,Matrix=(La=Math.cos,Ma=Math.sin,Na=Math.tan,Oa=Math.round,function(){this.reset=Pa,this.rotate=Qa,this.rotateX=Ra,this.rotateY=Sa,this.rotateZ=Ta,this.skew=Va,this.skewFromAxis=Wa,this.shear=Ua,this.scale=Xa,this.setTransform=Ya,this.translate=Za,this.transform=$a,this.applyToPoint=db,this.applyToX=eb,this.applyToY=fb,this.applyToZ=gb,this.applyToPointArray=kb,this.applyToTriplePoints=jb,this.applyToPointStringified=lb,this.toCSS=mb,this.to2dCSS=pb,this.clone=bb,this.cloneFromProps=cb,this.equals=ab,this.inversePoints=ib,this.inversePoint=hb,this._t=this.transform,this.isIdentity=_a,this._identity=!0,this._identityCalculated=!1,this.props=createTypedArray("float32",16),this.reset()}),La,Ma,Na,Oa;function Pa(){return this.props[0]=1,this.props[1]=0,this.props[2]=0,this.props[3]=0,this.props[4]=0,this.props[5]=1,this.props[6]=0,this.props[7]=0,this.props[8]=0,this.props[9]=0,this.props[10]=1,this.props[11]=0,this.props[12]=0,this.props[13]=0,this.props[14]=0,this.props[15]=1,this}function Qa(t){if(0===t)return this;var e=La(t),r=Ma(t);return this._t(e,-r,0,0,r,e,0,0,0,0,1,0,0,0,0,1)}function Ra(t){if(0===t)return this;var e=La(t),r=Ma(t);return this._t(1,0,0,0,0,e,-r,0,0,r,e,0,0,0,0,1)}function Sa(t){if(0===t)return this;var e=La(t),r=Ma(t);return this._t(e,0,r,0,0,1,0,0,-r,0,e,0,0,0,0,1)}function Ta(t){if(0===t)return this;var e=La(t),r=Ma(t);return this._t(e,-r,0,0,r,e,0,0,0,0,1,0,0,0,0,1)}function Ua(t,e){return this._t(1,e,t,1,0,0)}function Va(t,e){return this.shear(Na(t),Na(e))}function Wa(t,e){var r=La(e),i=Ma(e);return this._t(r,i,0,0,-i,r,0,0,0,0,1,0,0,0,0,1)._t(1,0,0,0,Na(t),1,0,0,0,0,1,0,0,0,0,1)._t(r,-i,0,0,i,r,0,0,0,0,1,0,0,0,0,1)}function Xa(t,e,r){return r||0===r||(r=1),1===t&&1===e&&1===r?this:this._t(t,0,0,0,0,e,0,0,0,0,r,0,0,0,0,1)}function Ya(t,e,r,i,s,a,n,o,h,p,l,m,f,c,d,u){return this.props[0]=t,this.props[1]=e,this.props[2]=r,this.props[3]=i,this.props[4]=s,this.props[5]=a,this.props[6]=n,this.props[7]=o,this.props[8]=h,this.props[9]=p,this.props[10]=l,this.props[11]=m,this.props[12]=f,this.props[13]=c,this.props[14]=d,this.props[15]=u,this}function Za(t,e,r){return r=r||0,0!==t||0!==e||0!==r?this._t(1,0,0,0,0,1,0,0,0,0,1,0,t,e,r,1):this}function $a(t,e,r,i,s,a,n,o,h,p,l,m,f,c,d,u){var y=this.props;if(1===t&&0===e&&0===r&&0===i&&0===s&&1===a&&0===n&&0===o&&0===h&&0===p&&1===l&&0===m)return y[12]=y[12]*t+y[15]*f,y[13]=y[13]*a+y[15]*c,y[14]=y[14]*l+y[15]*d,y[15]=y[15]*u,this._identityCalculated=!1,this;var g=y[0],v=y[1],P=y[2],b=y[3],x=y[4],_=y[5],S=y[6],T=y[7],A=y[8],C=y[9],E=y[10],k=y[11],D=y[12],M=y[13],I=y[14],w=y[15];return y[0]=g*t+v*s+P*h+b*f,y[1]=g*e+v*a+P*p+b*c,y[2]=g*r+v*n+P*l+b*d,y[3]=g*i+v*o+P*m+b*u,y[4]=x*t+_*s+S*h+T*f,y[5]=x*e+_*a+S*p+T*c,y[6]=x*r+_*n+S*l+T*d,y[7]=x*i+_*o+S*m+T*u,y[8]=A*t+C*s+E*h+k*f,y[9]=A*e+C*a+E*p+k*c,y[10]=A*r+C*n+E*l+k*d,y[11]=A*i+C*o+E*m+k*u,y[12]=D*t+M*s+I*h+w*f,y[13]=D*e+M*a+I*p+w*c,y[14]=D*r+M*n+I*l+w*d,y[15]=D*i+M*o+I*m+w*u,this._identityCalculated=!1,this}function _a(){return this._identityCalculated||(this._identity=!(1!==this.props[0]||0!==this.props[1]||0!==this.props[2]||0!==this.props[3]||0!==this.props[4]||1!==this.props[5]||0!==this.props[6]||0!==this.props[7]||0!==this.props[8]||0!==this.props[9]||1!==this.props[10]||0!==this.props[11]||0!==this.props[12]||0!==this.props[13]||0!==this.props[14]||1!==this.props[15]),this._identityCalculated=!0),this._identity}function ab(t){for(var e=0;e<16;){if(t.props[e]!==this.props[e])return!1;e+=1}return!0}function bb(t){var e;for(e=0;e<16;e+=1)t.props[e]=this.props[e]}function cb(t){var e;for(e=0;e<16;e+=1)this.props[e]=t[e]}function db(t,e,r){return{x:t*this.props[0]+e*this.props[4]+r*this.props[8]+this.props[12],y:t*this.props[1]+e*this.props[5]+r*this.props[9]+this.props[13],z:t*this.props[2]+e*this.props[6]+r*this.props[10]+this.props[14]}}function eb(t,e,r){return t*this.props[0]+e*this.props[4]+r*this.props[8]+this.props[12]}function fb(t,e,r){return t*this.props[1]+e*this.props[5]+r*this.props[9]+this.props[13]}function gb(t,e,r){return t*this.props[2]+e*this.props[6]+r*this.props[10]+this.props[14]}function hb(t){var e=this.props[0]*this.props[5]-this.props[1]*this.props[4],r=this.props[5]/e,i=-this.props[1]/e,s=-this.props[4]/e,a=this.props[0]/e,n=(this.props[4]*this.props[13]-this.props[5]*this.props[12])/e,o=-(this.props[0]*this.props[13]-this.props[1]*this.props[12])/e;return[t[0]*r+t[1]*s+n,t[0]*i+t[1]*a+o,0]}function ib(t){var e,r=t.length,i=[];for(e=0;e<r;e+=1)i[e]=hb(t[e]);return i}function jb(t,e,r){var i=createTypedArray("float32",6);if(this.isIdentity())i[0]=t[0],i[1]=t[1],i[2]=e[0],i[3]=e[1],i[4]=r[0],i[5]=r[1];else{var s=this.props[0],a=this.props[1],n=this.props[4],o=this.props[5],h=this.props[12],p=this.props[13];i[0]=t[0]*s+t[1]*n+h,i[1]=t[0]*a+t[1]*o+p,i[2]=e[0]*s+e[1]*n+h,i[3]=e[0]*a+e[1]*o+p,i[4]=r[0]*s+r[1]*n+h,i[5]=r[0]*a+r[1]*o+p}return i}function kb(t,e,r){return this.isIdentity()?[t,e,r]:[t*this.props[0]+e*this.props[4]+r*this.props[8]+this.props[12],t*this.props[1]+e*this.props[5]+r*this.props[9]+this.props[13],t*this.props[2]+e*this.props[6]+r*this.props[10]+this.props[14]]}function lb(t,e){if(this.isIdentity())return t+","+e;var r=this.props;return Math.round(100*(t*r[0]+e*r[4]+r[12]))/100+","+Math.round(100*(t*r[1]+e*r[5]+r[13]))/100}function mb(){for(var t=0,e=this.props,r="matrix3d(";t<16;)r+=Oa(1e4*e[t])/1e4,r+=15===t?")":",",t+=1;return r}function nb(t){return t<1e-6&&0<t||-1e-6<t&&t<0?Oa(1e4*t)/1e4:t}function pb(){var t=this.props;return"matrix("+nb(t[0])+","+nb(t[1])+","+nb(t[4])+","+nb(t[5])+","+nb(t[12])+","+nb(t[13])+")"}!function(o,h){var p,l=this,m=256,f=6,c="random",d=h.pow(m,f),u=h.pow(2,52),y=2*u,g=m-1;function v(t){var e,r=t.length,n=this,i=0,s=n.i=n.j=0,a=n.S=[];for(r||(t=[r++]);i<m;)a[i]=i++;for(i=0;i<m;i++)a[i]=a[s=g&s+t[i%r]+(e=a[i])],a[s]=e;n.g=function(t){for(var e,r=0,i=n.i,s=n.j,a=n.S;t--;)e=a[i=g&i+1],r=r*m+a[g&(a[i]=a[s=g&s+e])+(a[s]=e)];return n.i=i,n.j=s,r}}function P(t,e){return e.i=t.i,e.j=t.j,e.S=t.S.slice(),e}function b(t,e){for(var r,i=t+"",s=0;s<i.length;)e[g&s]=g&(r^=19*e[g&s])+i.charCodeAt(s++);return x(e)}function x(t){return String.fromCharCode.apply(0,t)}h["seed"+c]=function(t,e,r){function i(){for(var t=n.g(f),e=d,r=0;t<u;)t=(t+r)*m,e*=m,r=n.g(1);for(;y<=t;)t/=2,e/=2,r>>>=1;return(t+r)/e}var s=[],a=b(function t(e,r){var i,s=[],a=typeof e;if(r&&"object"==a)for(i in e)try{s.push(t(e[i],r-1))}catch(t){}return s.length?s:"string"==a?e:e+"\0"}((e=!0===e?{entropy:!0}:e||{}).entropy?[t,x(o)]:null===t?function(){try{if(p)return x(p.randomBytes(m));var t=new Uint8Array(m);return(l.crypto||l.msCrypto).getRandomValues(t),x(t)}catch(t){var e=l.navigator,r=e&&e.plugins;return[+new Date,l,r,l.screen,x(o)]}}():t,3),s),n=new v(s);return i.int32=function(){return 0|n.g(4)},i.quick=function(){return n.g(4)/4294967296},i.double=i,b(x(n.S),o),(e.pass||r||function(t,e,r,i){return i&&(i.S&&P(i,n),t.state=function(){return P(n,{})}),r?(h[c]=t,e):t})(i,a,"global"in e?e.global:this==h,e.state)},b(h.random(),o)}([],BMMath);var BezierFactory=(_e={getBezierEasing:function(t,e,r,i,s){var a=s||("bez_"+t+"_"+e+"_"+r+"_"+i).replace(/\./g,"p");if(af[a])return af[a];var n=new rf([t,e,r,i]);return af[a]=n}},af={},gf=11,hf=1/(gf-1),jf="function"==typeof Float32Array,rf.prototype={get:function(t){var e=this._p[0],r=this._p[1],i=this._p[2],s=this._p[3];return this._precomputed||this._precompute(),e===r&&i===s?t:0===t?0:1===t?1:nf(this._getTForX(t),r,s)},_precompute:function(){var t=this._p[0],e=this._p[1],r=this._p[2],i=this._p[3];this._precomputed=!0,t===e&&r===i||this._calcSampleValues()},_calcSampleValues:function(){for(var t=this._p[0],e=this._p[2],r=0;r<gf;++r)this._mSampleValues[r]=nf(r*hf,t,e)},_getTForX:function(t){for(var e=this._p[0],r=this._p[2],i=this._mSampleValues,s=0,a=1,n=gf-1;a!==n&&i[a]<=t;++a)s+=hf;var o=s+(t-i[--a])/(i[a+1]-i[a])*hf,h=of(o,e,r);return.001<=h?function(t,e,r,i){for(var s=0;s<4;++s){var a=of(e,r,i);if(0===a)return e;e-=(nf(e,r,i)-t)/a}return e}(t,o,e,r):0===h?o:function(t,e,r,i,s){for(var a,n,o=0;0<(a=nf(n=e+(r-e)/2,i,s)-t)?r=n:e=n,1e-7<Math.abs(a)&&++o<10;);return n}(t,s,s+hf,e,r)}},_e),_e,af,gf,hf,jf;function kf(t,e){return 1-3*e+3*t}function lf(t,e){return 3*e-6*t}function mf(t){return 3*t}function nf(t,e,r){return((kf(e,r)*t+lf(e,r))*t+mf(e))*t}function of(t,e,r){return 3*kf(e,r)*t*t+2*lf(e,r)*t+mf(e)}function rf(t){this._p=t,this._mSampleValues=jf?new Float32Array(gf):new Array(gf),this._precomputed=!1,this.get=this.get.bind(this)}function extendPrototype(t,e){var r,i,s=t.length;for(r=0;r<s;r+=1)for(var a in i=t[r].prototype)i.hasOwnProperty(a)&&(e.prototype[a]=i[a])}function getDescriptor(t,e){return Object.getOwnPropertyDescriptor(t,e)}function createProxyFunction(t){function e(){}return e.prototype=t,e}function bezFunction(){Math;function y(t,e,r,i,s,a){var n=t*i+e*s+r*a-s*i-a*t-r*e;return-.001<n&&n<.001}var l=function(t,e,r,i){var s,a,n,o,h,p,l=defaultCurveSegments,m=0,f=[],c=[],d=bezier_length_pool.newElement();for(n=r.length,s=0;s<l;s+=1){for(h=s/(l-1),a=p=0;a<n;a+=1)o=bm_pow(1-h,3)*t[a]+3*bm_pow(1-h,2)*h*r[a]+3*(1-h)*bm_pow(h,2)*i[a]+bm_pow(h,3)*e[a],f[a]=o,null!==c[a]&&(p+=bm_pow(f[a]-c[a],2)),c[a]=f[a];p&&(m+=p=bm_sqrt(p)),d.percents[s]=h,d.lengths[s]=m}return d.addedLength=m,d};function g(t){this.segmentLength=0,this.points=new Array(t)}function v(t,e){this.partialLength=t,this.point=e}var P,t=(P={},function(t,e,r,i){var s=(t[0]+"_"+t[1]+"_"+e[0]+"_"+e[1]+"_"+r[0]+"_"+r[1]+"_"+i[0]+"_"+i[1]).replace(/\./g,"p");if(!P[s]){var a,n,o,h,p,l,m,f=defaultCurveSegments,c=0,d=null;2===t.length&&(t[0]!=e[0]||t[1]!=e[1])&&y(t[0],t[1],e[0],e[1],t[0]+r[0],t[1]+r[1])&&y(t[0],t[1],e[0],e[1],e[0]+i[0],e[1]+i[1])&&(f=2);var u=new g(f);for(o=r.length,a=0;a<f;a+=1){for(m=createSizedArray(o),p=a/(f-1),n=l=0;n<o;n+=1)h=bm_pow(1-p,3)*t[n]+3*bm_pow(1-p,2)*p*(t[n]+r[n])+3*(1-p)*bm_pow(p,2)*(e[n]+i[n])+bm_pow(p,3)*e[n],m[n]=h,null!==d&&(l+=bm_pow(m[n]-d[n],2));c+=l=bm_sqrt(l),u.points[a]=new v(l,m),d=m}u.segmentLength=c,P[s]=u}return P[s]});function D(t,e){var r=e.percents,i=e.lengths,s=r.length,a=bm_floor((s-1)*t),n=t*e.addedLength,o=0;if(a===s-1||0===a||n===i[a])return r[a];for(var h=i[a]>n?-1:1,p=!0;p;)if(i[a]<=n&&i[a+1]>n?(o=(n-i[a])/(i[a+1]-i[a]),p=!1):a+=h,a<0||s-1<=a){if(a===s-1)return r[a];p=!1}return r[a]+(r[a+1]-r[a])*o}var M=createTypedArray("float32",8);return{getSegmentsLength:function(t){var e,r=segments_length_pool.newElement(),i=t.c,s=t.v,a=t.o,n=t.i,o=t._length,h=r.lengths,p=0;for(e=0;e<o-1;e+=1)h[e]=l(s[e],s[e+1],a[e],n[e+1]),p+=h[e].addedLength;return i&&o&&(h[e]=l(s[e],s[0],a[e],n[0]),p+=h[e].addedLength),r.totalLength=p,r},getNewSegment:function(t,e,r,i,s,a,n){var o,h=D(s=s<0?0:1<s?1:s,n),p=D(a=1<a?1:a,n),l=t.length,m=1-h,f=1-p,c=m*m*m,d=h*m*m*3,u=h*h*m*3,y=h*h*h,g=m*m*f,v=h*m*f+m*h*f+m*m*p,P=h*h*f+m*h*p+h*m*p,b=h*h*p,x=m*f*f,_=h*f*f+m*p*f+m*f*p,S=h*p*f+m*p*p+h*f*p,T=h*p*p,A=f*f*f,C=p*f*f+f*p*f+f*f*p,E=p*p*f+f*p*p+p*f*p,k=p*p*p;for(o=0;o<l;o+=1)M[4*o]=Math.round(1e3*(c*t[o]+d*r[o]+u*i[o]+y*e[o]))/1e3,M[4*o+1]=Math.round(1e3*(g*t[o]+v*r[o]+P*i[o]+b*e[o]))/1e3,M[4*o+2]=Math.round(1e3*(x*t[o]+_*r[o]+S*i[o]+T*e[o]))/1e3,M[4*o+3]=Math.round(1e3*(A*t[o]+C*r[o]+E*i[o]+k*e[o]))/1e3;return M},getPointInSegment:function(t,e,r,i,s,a){var n=D(s,a),o=1-n;return[Math.round(1e3*(o*o*o*t[0]+(n*o*o+o*n*o+o*o*n)*r[0]+(n*n*o+o*n*n+n*o*n)*i[0]+n*n*n*e[0]))/1e3,Math.round(1e3*(o*o*o*t[1]+(n*o*o+o*n*o+o*o*n)*r[1]+(n*n*o+o*n*n+n*o*n)*i[1]+n*n*n*e[1]))/1e3]},buildBezierData:t,pointOnLine2D:y,pointOnLine3D:function(t,e,r,i,s,a,n,o,h){if(0===r&&0===a&&0===h)return y(t,e,i,s,n,o);var p,l=Math.sqrt(Math.pow(i-t,2)+Math.pow(s-e,2)+Math.pow(a-r,2)),m=Math.sqrt(Math.pow(n-t,2)+Math.pow(o-e,2)+Math.pow(h-r,2)),f=Math.sqrt(Math.pow(n-i,2)+Math.pow(o-s,2)+Math.pow(h-a,2));return-1e-4<(p=m<l?f<l?l-m-f:f-m-l:m<f?f-m-l:m-l-f)&&p<1e-4}}}!function(){for(var a=0,t=["ms","moz","webkit","o"],e=0;e<t.length&&!window.requestAnimationFrame;++e)window.requestAnimationFrame=window[t[e]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[t[e]+"CancelAnimationFrame"]||window[t[e]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(t,e){var r=(new Date).getTime(),i=Math.max(0,16-(r-a)),s=setTimeout(function(){t(r+i)},i);return a=r+i,s}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(t){clearTimeout(t)})}();var bez=bezFunction();function dataFunctionManager(){function m(t,e,r){var i,s,a,n,o,h,p=t.length;for(s=0;s<p;s+=1)if("ks"in(i=t[s])&&!i.completed){if(i.completed=!0,i.tt&&(t[s-1].td=i.tt),[],-1,i.hasMask){var l=i.masksProperties;for(n=l.length,a=0;a<n;a+=1)if(l[a].pt.k.i)d(l[a].pt.k);else for(h=l[a].pt.k.length,o=0;o<h;o+=1)l[a].pt.k[o].s&&d(l[a].pt.k[o].s[0]),l[a].pt.k[o].e&&d(l[a].pt.k[o].e[0])}0===i.ty?(i.layers=f(i.refId,e),m(i.layers,e,r)):4===i.ty?c(i.shapes):5==i.ty&&b(i,r)}}function f(t,e){for(var r=0,i=e.length;r<i;){if(e[r].id===t)return e[r].layers.__used?JSON.parse(JSON.stringify(e[r].layers)):(e[r].layers.__used=!0,e[r].layers);r+=1}}function c(t){var e,r,i;for(e=t.length-1;0<=e;e-=1)if("sh"==t[e].ty){if(t[e].ks.k.i)d(t[e].ks.k);else for(i=t[e].ks.k.length,r=0;r<i;r+=1)t[e].ks.k[r].s&&d(t[e].ks.k[r].s[0]),t[e].ks.k[r].e&&d(t[e].ks.k[r].e[0]);!0}else"gr"==t[e].ty&&c(t[e].it)}function d(t){var e,r=t.i.length;for(e=0;e<r;e+=1)t.i[e][0]+=t.v[e][0],t.i[e][1]+=t.v[e][1],t.o[e][0]+=t.v[e][0],t.o[e][1]+=t.v[e][1]}function o(t,e){var r=e?e.split("."):[100,100,100];return t[0]>r[0]||!(r[0]>t[0])&&(t[1]>r[1]||!(r[1]>t[1])&&(t[2]>r[2]||!(r[2]>t[2])&&void 0))}var i,r=(i=[4,4,14],function(t){if(o(i,t.v)&&(s(t.layers),t.assets)){var e,r=t.assets.length;for(e=0;e<r;e+=1)t.assets[e].layers&&s(t.assets[e].layers)}});function s(t){var e,r,i,s=t.length;for(e=0;e<s;e+=1)5===t[e].ty&&(r=t[e],void 0,i=r.t.d,r.t.d={k:[{s:i,t:0}]})}var h,a,n=(h=[4,7,99],function(t){if(t.chars&&!o(h,t.v)){var e,r,i,s,a,n=t.chars.length;for(e=0;e<n;e+=1)if(t.chars[e].data&&t.chars[e].data.shapes)for(i=(a=t.chars[e].data.shapes[0].it).length,r=0;r<i;r+=1)(s=a[r].ks.k).__converted||(d(a[r].ks.k),s.__converted=!0)}}),p=(a=[4,1,9],function(t){if(o(a,t.v)&&(u(t.layers),t.assets)){var e,r=t.assets.length;for(e=0;e<r;e+=1)t.assets[e].layers&&u(t.assets[e].layers)}});function l(t){var e,r,i,s=t.length;for(e=0;e<s;e+=1)if("gr"===t[e].ty)l(t[e].it);else if("fl"===t[e].ty||"st"===t[e].ty)if(t[e].c.k&&t[e].c.k[0].i)for(i=t[e].c.k.length,r=0;r<i;r+=1)t[e].c.k[r].s&&(t[e].c.k[r].s[0]/=255,t[e].c.k[r].s[1]/=255,t[e].c.k[r].s[2]/=255,t[e].c.k[r].s[3]/=255),t[e].c.k[r].e&&(t[e].c.k[r].e[0]/=255,t[e].c.k[r].e[1]/=255,t[e].c.k[r].e[2]/=255,t[e].c.k[r].e[3]/=255);else t[e].c.k[0]/=255,t[e].c.k[1]/=255,t[e].c.k[2]/=255,t[e].c.k[3]/=255}function u(t){var e,r=t.length;for(e=0;e<r;e+=1)4===t[e].ty&&l(t[e].shapes)}var y,g=(y=[4,4,18],function(t){if(o(y,t.v)&&(P(t.layers),t.assets)){var e,r=t.assets.length;for(e=0;e<r;e+=1)t.assets[e].layers&&P(t.assets[e].layers)}});function v(t){var e,r,i;for(e=t.length-1;0<=e;e-=1)if("sh"==t[e].ty){if(t[e].ks.k.i)t[e].ks.k.c=t[e].closed;else for(i=t[e].ks.k.length,r=0;r<i;r+=1)t[e].ks.k[r].s&&(t[e].ks.k[r].s[0].c=t[e].closed),t[e].ks.k[r].e&&(t[e].ks.k[r].e[0].c=t[e].closed);!0}else"gr"==t[e].ty&&v(t[e].it)}function P(t){var e,r,i,s,a,n,o=t.length;for(r=0;r<o;r+=1){if((e=t[r]).hasMask){var h=e.masksProperties;for(s=h.length,i=0;i<s;i+=1)if(h[i].pt.k.i)h[i].pt.k.c=h[i].cl;else for(n=h[i].pt.k.length,a=0;a<n;a+=1)h[i].pt.k[a].s&&(h[i].pt.k[a].s[0].c=h[i].cl),h[i].pt.k[a].e&&(h[i].pt.k[a].e[0].c=h[i].cl)}4===e.ty&&v(e.shapes)}}function b(t,e){0!==t.t.a.length||"m"in t.t.p||(t.singleShape=!0)}var t={completeData:function(t,e){t.__complete||(p(t),r(t),n(t),g(t),m(t.layers,t.assets,e),t.__complete=!0)}};return t.checkColors=p,t.checkChars=n,t.checkShapes=g,t.completeLayers=m,t}var dataManager=dataFunctionManager();dataManager.completeData=function(t,e){t.__complete||(this.checkColors(t),this.checkChars(t),this.checkShapes(t),this.completeLayers(t.layers,t.assets,e),t.__complete=!0)};var FontManager=function(){var a={w:0,size:0,shapes:[]},t=[];function u(t,e){var r=createTag("span");r.style.fontFamily=e;var i=createTag("span");i.innerHTML="giItT1WQy@!-/#",r.style.position="absolute",r.style.left="-10000px",r.style.top="-10000px",r.style.fontSize="300px",r.style.fontVariant="normal",r.style.fontStyle="normal",r.style.fontWeight="normal",r.style.letterSpacing="0",r.appendChild(i),document.body.appendChild(r);var s=i.offsetWidth;return i.style.fontFamily=t+", "+e,{node:i,w:s,parent:r}}t=t.concat([2304,2305,2306,2307,2362,2363,2364,2364,2366,2367,2368,2369,2370,2371,2372,2373,2374,2375,2376,2377,2378,2379,2380,2381,2382,2383,2387,2388,2389,2390,2391,2402,2403]);function e(){this.fonts=[],this.chars=null,this.typekitLoaded=0,this.isLoaded=!1,this.initTime=Date.now()}return e.getCombinedCharacterCodes=function(){return t},e.prototype.addChars=function(t){if(t){this.chars||(this.chars=[]);var e,r,i,s=t.length,a=this.chars.length;for(e=0;e<s;e+=1){for(r=0,i=!1;r<a;)this.chars[r].style===t[e].style&&this.chars[r].fFamily===t[e].fFamily&&this.chars[r].ch===t[e].ch&&(i=!0),r+=1;i||(this.chars.push(t[e]),a+=1)}}},e.prototype.addFonts=function(t,e){if(t){if(this.chars)return this.isLoaded=!0,void(this.fonts=t.list);var r,i,s,a,n=t.list,o=n.length,h=o;for(r=0;r<o;r+=1){var p,l,m=!0;if(n[r].loaded=!1,n[r].monoCase=u(n[r].fFamily,"monospace"),n[r].sansCase=u(n[r].fFamily,"sans-serif"),n[r].fPath){if("p"===n[r].fOrigin||3===n[r].origin){if(0<(p=document.querySelectorAll('style[f-forigin="p"][f-family="'+n[r].fFamily+'"], style[f-origin="3"][f-family="'+n[r].fFamily+'"]')).length&&(m=!1),m){var f=createTag("style");f.setAttribute("f-forigin",n[r].fOrigin),f.setAttribute("f-origin",n[r].origin),f.setAttribute("f-family",n[r].fFamily),f.type="text/css",f.innerHTML="@font-face {font-family: "+n[r].fFamily+"; font-style: normal; src: url('"+n[r].fPath+"');}",e.appendChild(f)}}else if("g"===n[r].fOrigin||1===n[r].origin){for(p=document.querySelectorAll('link[f-forigin="g"], link[f-origin="1"]'),l=0;l<p.length;l++)-1!==p[l].href.indexOf(n[r].fPath)&&(m=!1);if(m){var c=createTag("link");c.setAttribute("f-forigin",n[r].fOrigin),c.setAttribute("f-origin",n[r].origin),c.type="text/css",c.rel="stylesheet",c.href=n[r].fPath,document.body.appendChild(c)}}else if("t"===n[r].fOrigin||2===n[r].origin){for(p=document.querySelectorAll('script[f-forigin="t"], script[f-origin="2"]'),l=0;l<p.length;l++)n[r].fPath===p[l].src&&(m=!1);if(m){var d=createTag("link");d.setAttribute("f-forigin",n[r].fOrigin),d.setAttribute("f-origin",n[r].origin),d.setAttribute("rel","stylesheet"),d.setAttribute("href",n[r].fPath),e.appendChild(d)}}}else n[r].loaded=!0,h-=1;n[r].helper=(i=e,s=n[r],a=void 0,(a=createNS("text")).style.fontSize="100px",a.setAttribute("font-family",s.fFamily),a.setAttribute("font-style",s.fStyle),a.setAttribute("font-weight",s.fWeight),a.textContent="1",s.fClass?(a.style.fontFamily="inherit",a.setAttribute("class",s.fClass)):a.style.fontFamily=s.fFamily,i.appendChild(a),createTag("canvas").getContext("2d").font=s.fWeight+" "+s.fStyle+" 100px "+s.fFamily,a),n[r].cache={},this.fonts.push(n[r])}0===h?this.isLoaded=!0:setTimeout(this.checkLoadedFonts.bind(this),100)}else this.isLoaded=!0},e.prototype.getCharData=function(t,e,r){for(var i=0,s=this.chars.length;i<s;){if(this.chars[i].ch===t&&this.chars[i].style===e&&this.chars[i].fFamily===r)return this.chars[i];i+=1}return console&&console.warn&&console.warn("Missing character from exported characters list: ",t,e,r),a},e.prototype.getFontByName=function(t){for(var e=0,r=this.fonts.length;e<r;){if(this.fonts[e].fName===t)return this.fonts[e];e+=1}return this.fonts[0]},e.prototype.measureText=function(t,e,r){var i=this.getFontByName(e),s=t.charCodeAt(0);if(!i.cache[s+1]){var a=i.helper;if(" "===t){a.textContent="|"+t+"|";var n=a.getComputedTextLength();a.textContent="||";var o=a.getComputedTextLength();i.cache[s+1]=(n-o)/100}else a.textContent=t,i.cache[s+1]=a.getComputedTextLength()/100}return i.cache[s+1]*r},e.prototype.checkLoadedFonts=function(){var t,e,r,i=this.fonts.length,s=i;for(t=0;t<i;t+=1)this.fonts[t].loaded?s-=1:"n"===this.fonts[t].fOrigin||0===this.fonts[t].origin?this.fonts[t].loaded=!0:(e=this.fonts[t].monoCase.node,r=this.fonts[t].monoCase.w,e.offsetWidth!==r?(s-=1,this.fonts[t].loaded=!0):(e=this.fonts[t].sansCase.node,r=this.fonts[t].sansCase.w,e.offsetWidth!==r&&(s-=1,this.fonts[t].loaded=!0)),this.fonts[t].loaded&&(this.fonts[t].sansCase.parent.parentNode.removeChild(this.fonts[t].sansCase.parent),this.fonts[t].monoCase.parent.parentNode.removeChild(this.fonts[t].monoCase.parent)));0!==s&&Date.now()-this.initTime<5e3?setTimeout(this.checkLoadedFonts.bind(this),20):setTimeout(function(){this.isLoaded=!0}.bind(this),0)},e.prototype.loaded=function(){return this.isLoaded},e}();FontManager=function(){this.fonts=[],this.chars=null,this.typekitLoaded=0,this.isLoaded=!1,this.initTime=Date.now()};var PropertyFactory=(km=initialDefaultFrame,lm=Math.abs,{getProp:function(t,e,r,i,s){var a;if(e.k.length)if("number"==typeof e.k[0])a=new vm(t,e,i,s);else switch(r){case 0:a=new wm(t,e,i,s);break;case 1:a=new xm(t,e,i,s)}else a=new um(t,e,i,s);return a.effectsSequence.length&&s.addDynamicProperty(a),a}}),km,lm;function mm(t,e){var r,i=this.offsetTime;"multidimensional"===this.propType&&(r=createTypedArray("float32",this.pv.length));for(var s,a,n,o,h,p,l,m,f=e.lastIndex,c=f,d=this.keyframes.length-1,u=!0;u;){if(s=this.keyframes[c],a=this.keyframes[c+1],c===d-1&&t>=a.t-i){s.h&&(s=a),f=0;break}if(a.t-i>t){f=c;break}c<d-1?c+=1:(f=0,u=!1)}var y,g=a.t-i,v=s.t-i;if(s.to){s.bezierData||(s.bezierData=bez.buildBezierData(s.s,a.s||s.e,s.to,s.ti));var P=s.bezierData;if(g<=t||t<v){var b=g<=t?P.points.length-1:0;for(o=P.points[b].point.length,n=0;n<o;n+=1)r[n]=P.points[b].point[n]}else{s.__fnct?m=s.__fnct:(m=BezierFactory.getBezierEasing(s.o.x,s.o.y,s.i.x,s.i.y,s.n).get,s.__fnct=m),h=m((t-v)/(g-v));var x,_=P.segmentLength*h,S=e.lastFrame<t&&e._lastKeyframeIndex===c?e._lastAddedLength:0;for(l=e.lastFrame<t&&e._lastKeyframeIndex===c?e._lastPoint:0,u=!0,p=P.points.length;u;){if(S+=P.points[l].partialLength,0==_||0===h||l===P.points.length-1){for(o=P.points[l].point.length,n=0;n<o;n+=1)r[n]=P.points[l].point[n];break}if(S<=_&&_<S+P.points[l+1].partialLength){for(x=(_-S)/P.points[l+1].partialLength,o=P.points[l].point.length,n=0;n<o;n+=1)r[n]=P.points[l].point[n]+(P.points[l+1].point[n]-P.points[l].point[n])*x;break}l<p-1?l+=1:u=!1}e._lastPoint=l,e._lastAddedLength=S-P.points[l].partialLength,e._lastKeyframeIndex=c}}else{var T,A,C,E,k;if(d=s.s.length,y=a.s||s.e,this.sh&&1!==s.h)if(g<=t)r[0]=y[0],r[1]=y[1],r[2]=y[2];else if(t<=v)r[0]=s.s[0],r[1]=s.s[1],r[2]=s.s[2];else{!function(t,e){var r=e[0],i=e[1],s=e[2],a=e[3],n=Math.atan2(2*i*a-2*r*s,1-2*i*i-2*s*s),o=Math.asin(2*r*i+2*s*a),h=Math.atan2(2*r*a-2*i*s,1-2*r*r-2*s*s);t[0]=n/degToRads,t[1]=o/degToRads,t[2]=h/degToRads}(r,function(t,e,r){var i,s,a,n,o,h=[],p=t[0],l=t[1],m=t[2],f=t[3],c=e[0],d=e[1],u=e[2],y=e[3];(s=p*c+l*d+m*u+f*y)<0&&(s=-s,c=-c,d=-d,u=-u,y=-y);o=1e-6<1-s?(i=Math.acos(s),a=Math.sin(i),n=Math.sin((1-r)*i)/a,Math.sin(r*i)/a):(n=1-r,r);return h[0]=n*p+o*c,h[1]=n*l+o*d,h[2]=n*m+o*u,h[3]=n*f+o*y,h}(pm(s.s),pm(y),(t-v)/(g-v)))}else for(c=0;c<d;c+=1)1!==s.h&&(h=g<=t?1:t<v?0:(s.o.x.constructor===Array?(s.__fnct||(s.__fnct=[]),s.__fnct[c]?m=s.__fnct[c]:(T=void 0===s.o.x[c]?s.o.x[0]:s.o.x[c],A=void 0===s.o.y[c]?s.o.y[0]:s.o.y[c],C=void 0===s.i.x[c]?s.i.x[0]:s.i.x[c],E=void 0===s.i.y[c]?s.i.y[0]:s.i.y[c],m=BezierFactory.getBezierEasing(T,A,C,E).get,s.__fnct[c]=m)):s.__fnct?m=s.__fnct:(T=s.o.x,A=s.o.y,C=s.i.x,E=s.i.y,m=BezierFactory.getBezierEasing(T,A,C,E).get,s.__fnct=m),m((t-v)/(g-v)))),y=a.s||s.e,k=1===s.h?s.s[c]:s.s[c]+(y[c]-s.s[c])*h,1===d?r=k:r[c]=k}return e.lastIndex=f,r}function pm(t){var e=t[0]*degToRads,r=t[1]*degToRads,i=t[2]*degToRads,s=Math.cos(e/2),a=Math.cos(r/2),n=Math.cos(i/2),o=Math.sin(e/2),h=Math.sin(r/2),p=Math.sin(i/2);return[o*h*n+s*a*p,o*a*n+s*h*p,s*h*n-o*a*p,s*a*n-o*h*p]}function qm(){var t=this.comp.renderedFrame-this.offsetTime,e=this.keyframes[0].t-this.offsetTime,r=this.keyframes[this.keyframes.length-1].t-this.offsetTime;if(!(t===this._caching.lastFrame||this._caching.lastFrame!==km&&(this._caching.lastFrame>=r&&r<=t||this._caching.lastFrame<e&&t<e))){this._caching.lastFrame>=t&&(this._caching._lastKeyframeIndex=-1,this._caching.lastIndex=0);var i=this.interpolateValue(t,this._caching);this.pv=i}return this._caching.lastFrame=t,this.pv}function rm(t){var e;if("unidimensional"===this.propType)e=t*this.mult,1e-5<lm(this.v-e)&&(this.v=e,this._mdf=!0);else for(var r=0,i=this.v.length;r<i;)e=t[r]*this.mult,1e-5<lm(this.v[r]-e)&&(this.v[r]=e,this._mdf=!0),r+=1}function sm(){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length)if(this.lock)this.setVValue(this.pv);else{this.lock=!0,this._mdf=this._isFirstFrame;var t,e=this.effectsSequence.length,r=this.kf?this.pv:this.data.k;for(t=0;t<e;t+=1)r=this.effectsSequence[t](r);this.setVValue(r),this._isFirstFrame=!1,this.lock=!1,this.frameId=this.elem.globalData.frameId}}function tm(t){this.effectsSequence.push(t),this.container.addDynamicProperty(this)}function um(t,e,r,i){this.propType="unidimensional",this.mult=r||1,this.data=e,this.v=r?e.k*r:e.k,this.pv=e.k,this._mdf=!1,this.elem=t,this.container=i,this.comp=t.comp,this.k=!1,this.kf=!1,this.vel=0,this.effectsSequence=[],this._isFirstFrame=!0,this.getValue=sm,this.setVValue=rm,this.addEffect=tm}function vm(t,e,r,i){this.propType="multidimensional",this.mult=r||1,this.data=e,this._mdf=!1,this.elem=t,this.container=i,this.comp=t.comp,this.k=!1,this.kf=!1,this.frameId=-1;var s,a=e.k.length;this.v=createTypedArray("float32",a),this.pv=createTypedArray("float32",a);createTypedArray("float32",a);for(this.vel=createTypedArray("float32",a),s=0;s<a;s+=1)this.v[s]=e.k[s]*this.mult,this.pv[s]=e.k[s];this._isFirstFrame=!0,this.effectsSequence=[],this.getValue=sm,this.setVValue=rm,this.addEffect=tm}function wm(t,e,r,i){this.propType="unidimensional",this.keyframes=e.k,this.offsetTime=t.data.st,this.frameId=-1,this._caching={lastFrame:km,lastIndex:0,value:0,_lastKeyframeIndex:-1},this.k=!0,this.kf=!0,this.data=e,this.mult=r||1,this.elem=t,this.container=i,this.comp=t.comp,this.v=km,this.pv=km,this._isFirstFrame=!0,this.getValue=sm,this.setVValue=rm,this.interpolateValue=mm,this.effectsSequence=[qm.bind(this)],this.addEffect=tm}function xm(t,e,r,i){this.propType="multidimensional";var s,a,n,o,h,p=e.k.length;for(s=0;s<p-1;s+=1)e.k[s].to&&e.k[s].s&&e.k[s].e&&(a=e.k[s].s,n=e.k[s].e,o=e.k[s].to,h=e.k[s].ti,(2===a.length&&(a[0]!==n[0]||a[1]!==n[1])&&bez.pointOnLine2D(a[0],a[1],n[0],n[1],a[0]+o[0],a[1]+o[1])&&bez.pointOnLine2D(a[0],a[1],n[0],n[1],n[0]+h[0],n[1]+h[1])||3===a.length&&(a[0]!==n[0]||a[1]!==n[1]||a[2]!==n[2])&&bez.pointOnLine3D(a[0],a[1],a[2],n[0],n[1],n[2],a[0]+o[0],a[1]+o[1],a[2]+o[2])&&bez.pointOnLine3D(a[0],a[1],a[2],n[0],n[1],n[2],n[0]+h[0],n[1]+h[1],n[2]+h[2]))&&(e.k[s].to=null,e.k[s].ti=null),a[0]===n[0]&&a[1]===n[1]&&0===o[0]&&0===o[1]&&0===h[0]&&0===h[1]&&(2===a.length||a[2]===n[2]&&0===o[2]&&0===h[2])&&(e.k[s].to=null,e.k[s].ti=null));this.effectsSequence=[qm.bind(this)],this.keyframes=e.k,this.offsetTime=t.data.st,this.k=!0,this.kf=!0,this._isFirstFrame=!0,this.mult=r||1,this.elem=t,this.container=i,this.comp=t.comp,this.getValue=sm,this.setVValue=rm,this.interpolateValue=mm,this.frameId=-1;var l=e.k[0].s.length;for(this.v=createTypedArray("float32",l),this.pv=createTypedArray("float32",l),s=0;s<l;s+=1)this.v[s]=km,this.pv[s]=km;this._caching={lastFrame:km,lastIndex:0,value:createTypedArray("float32",l)},this.addEffect=tm}var TransformPropertyFactory=(Qo.prototype={applyToMatrix:function(t){var e=this._mdf;this.iterateDynamicProperties(),this._mdf=this._mdf||e,this.a&&t.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.s&&t.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.sk&&t.skewFromAxis(-this.sk.v,this.sa.v),this.r?t.rotate(-this.r.v):t.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.data.p.s?this.data.p.z?t.translate(this.px.v,this.py.v,-this.pz.v):t.translate(this.px.v,this.py.v,0):t.translate(this.p.v[0],this.p.v[1],-this.p.v[2])},getValue:function(t){if(this.elem.globalData.frameId!==this.frameId){if(this._isDirty&&(this.precalculateMatrix(),this._isDirty=!1),this.iterateDynamicProperties(),this._mdf||t){if(this.v.cloneFromProps(this.pre.props),this.appliedTransformations<1&&this.v.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.appliedTransformations<2&&this.v.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.sk&&this.appliedTransformations<3&&this.v.skewFromAxis(-this.sk.v,this.sa.v),this.r&&this.appliedTransformations<4?this.v.rotate(-this.r.v):!this.r&&this.appliedTransformations<4&&this.v.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.autoOriented){var e,r,i=this.elem.globalData.frameRate;if(this.p&&this.p.keyframes&&this.p.getValueAtTime)r=this.p._caching.lastFrame+this.p.offsetTime<=this.p.keyframes[0].t?(e=this.p.getValueAtTime((this.p.keyframes[0].t+.01)/i,0),this.p.getValueAtTime(this.p.keyframes[0].t/i,0)):this.p._caching.lastFrame+this.p.offsetTime>=this.p.keyframes[this.p.keyframes.length-1].t?(e=this.p.getValueAtTime(this.p.keyframes[this.p.keyframes.length-1].t/i,0),this.p.getValueAtTime((this.p.keyframes[this.p.keyframes.length-1].t-.01)/i,0)):(e=this.p.pv,this.p.getValueAtTime((this.p._caching.lastFrame+this.p.offsetTime-.01)/i,this.p.offsetTime));else if(this.px&&this.px.keyframes&&this.py.keyframes&&this.px.getValueAtTime&&this.py.getValueAtTime){e=[],r=[];var s=this.px,a=this.py;s._caching.lastFrame+s.offsetTime<=s.keyframes[0].t?(e[0]=s.getValueAtTime((s.keyframes[0].t+.01)/i,0),e[1]=a.getValueAtTime((a.keyframes[0].t+.01)/i,0),r[0]=s.getValueAtTime(s.keyframes[0].t/i,0),r[1]=a.getValueAtTime(a.keyframes[0].t/i,0)):s._caching.lastFrame+s.offsetTime>=s.keyframes[s.keyframes.length-1].t?(e[0]=s.getValueAtTime(s.keyframes[s.keyframes.length-1].t/i,0),e[1]=a.getValueAtTime(a.keyframes[a.keyframes.length-1].t/i,0),r[0]=s.getValueAtTime((s.keyframes[s.keyframes.length-1].t-.01)/i,0),r[1]=a.getValueAtTime((a.keyframes[a.keyframes.length-1].t-.01)/i,0)):(e=[s.pv,a.pv],r[0]=s.getValueAtTime((s._caching.lastFrame+s.offsetTime-.01)/i,s.offsetTime),r[1]=a.getValueAtTime((a._caching.lastFrame+a.offsetTime-.01)/i,a.offsetTime))}this.v.rotate(-Math.atan2(e[1]-r[1],e[0]-r[0]))}this.data.p&&this.data.p.s?this.data.p.z?this.v.translate(this.px.v,this.py.v,-this.pz.v):this.v.translate(this.px.v,this.py.v,0):this.v.translate(this.p.v[0],this.p.v[1],-this.p.v[2])}this.frameId=this.elem.globalData.frameId}},precalculateMatrix:function(){if(!this.a.k&&(this.pre.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.appliedTransformations=1,!this.s.effectsSequence.length)){if(this.pre.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.appliedTransformations=2,this.sk){if(this.sk.effectsSequence.length||this.sa.effectsSequence.length)return;this.pre.skewFromAxis(-this.sk.v,this.sa.v),this.appliedTransformations=3}if(this.r){if(this.r.effectsSequence.length)return;this.pre.rotate(-this.r.v),this.appliedTransformations=4}else this.rz.effectsSequence.length||this.ry.effectsSequence.length||this.rx.effectsSequence.length||this.or.effectsSequence.length||(this.pre.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.appliedTransformations=4)}},autoOrient:function(){}},extendPrototype([DynamicPropertyContainer],Qo),Qo.prototype.addDynamicProperty=function(t){this._addDynamicProperty(t),this.elem.addDynamicProperty(t),this._isDirty=!0},Qo.prototype._addDynamicProperty=DynamicPropertyContainer.prototype.addDynamicProperty,{getTransformProperty:function(t,e,r){return new Qo(t,e,r)}});function Qo(t,e,r){if(this.elem=t,this.frameId=-1,this.propType="transform",this.data=e,this.v=new Matrix,this.pre=new Matrix,this.appliedTransformations=0,this.initDynamicPropertyContainer(r||t),e.p&&e.p.s?(this.px=PropertyFactory.getProp(t,e.p.x,0,0,this),this.py=PropertyFactory.getProp(t,e.p.y,0,0,this),e.p.z&&(this.pz=PropertyFactory.getProp(t,e.p.z,0,0,this))):this.p=PropertyFactory.getProp(t,e.p||{k:[0,0,0]},1,0,this),e.rx){if(this.rx=PropertyFactory.getProp(t,e.rx,0,degToRads,this),this.ry=PropertyFactory.getProp(t,e.ry,0,degToRads,this),this.rz=PropertyFactory.getProp(t,e.rz,0,degToRads,this),e.or.k[0].ti){var i,s=e.or.k.length;for(i=0;i<s;i+=1)e.or.k[i].to=e.or.k[i].ti=null}this.or=PropertyFactory.getProp(t,e.or,1,degToRads,this),this.or.sh=!0}else this.r=PropertyFactory.getProp(t,e.r||{k:0},0,degToRads,this);e.sk&&(this.sk=PropertyFactory.getProp(t,e.sk,0,degToRads,this),this.sa=PropertyFactory.getProp(t,e.sa,0,degToRads,this)),this.a=PropertyFactory.getProp(t,e.a||{k:[0,0,0]},1,0,this),this.s=PropertyFactory.getProp(t,e.s||{k:[100,100,100]},1,.01,this),e.o?this.o=PropertyFactory.getProp(t,e.o,0,.01,t):this.o={_mdf:!1,v:1},this._isDirty=!0,this.dynamicProperties.length||this.getValue(!0)}function ShapePath(){this.c=!1,this._length=0,this._maxLength=8,this.v=createSizedArray(this._maxLength),this.o=createSizedArray(this._maxLength),this.i=createSizedArray(this._maxLength)}ShapePath.prototype.setPathData=function(t,e){this.c=t,this.setLength(e);for(var r=0;r<e;)this.v[r]=point_pool.newElement(),this.o[r]=point_pool.newElement(),this.i[r]=point_pool.newElement(),r+=1},ShapePath.prototype.setLength=function(t){for(;this._maxLength<t;)this.doubleArrayLength();this._length=t},ShapePath.prototype.doubleArrayLength=function(){this.v=this.v.concat(createSizedArray(this._maxLength)),this.i=this.i.concat(createSizedArray(this._maxLength)),this.o=this.o.concat(createSizedArray(this._maxLength)),this._maxLength*=2},ShapePath.prototype.setXYAt=function(t,e,r,i,s){var a;switch(this._length=Math.max(this._length,i+1),this._length>=this._maxLength&&this.doubleArrayLength(),r){case"v":a=this.v;break;case"i":a=this.i;break;case"o":a=this.o}a[i]&&(!a[i]||s)||(a[i]=point_pool.newElement()),a[i][0]=t,a[i][1]=e},ShapePath.prototype.setTripleAt=function(t,e,r,i,s,a,n,o){this.setXYAt(t,e,"v",n,o),this.setXYAt(r,i,"o",n,o),this.setXYAt(s,a,"i",n,o)},ShapePath.prototype.reverse=function(){var t=new ShapePath;t.setPathData(this.c,this._length);var e=this.v,r=this.o,i=this.i,s=0;this.c&&(t.setTripleAt(e[0][0],e[0][1],i[0][0],i[0][1],r[0][0],r[0][1],0,!1),s=1);var a,n=this._length-1,o=this._length;for(a=s;a<o;a+=1)t.setTripleAt(e[n][0],e[n][1],i[n][0],i[n][1],r[n][0],r[n][1],a,!1),n-=1;return t};var ShapePropertyFactory=function(){var s=-999999;function t(t,e,r){var i,s,a,n,o,h,p,l,m,f=r.lastIndex,c=this.keyframes;if(t<c[0].t-this.offsetTime)i=c[0].s[0],a=!0,f=0;else if(t>=c[c.length-1].t-this.offsetTime)i=c[c.length-1].s?c[c.length-1].s[0]:c[c.length-2].e[0],a=!0;else{for(var d,u,y=f,g=c.length-1,v=!0;v&&(d=c[y],!((u=c[y+1]).t-this.offsetTime>t));)y<g-1?y+=1:v=!1;if(f=y,!(a=1===d.h)){if(t>=u.t-this.offsetTime)l=1;else if(t<d.t-this.offsetTime)l=0;else{var P;d.__fnct?P=d.__fnct:(P=BezierFactory.getBezierEasing(d.o.x,d.o.y,d.i.x,d.i.y).get,d.__fnct=P),l=P((t-(d.t-this.offsetTime))/(u.t-this.offsetTime-(d.t-this.offsetTime)))}s=u.s?u.s[0]:d.e[0]}i=d.s[0]}for(h=e._length,p=i.i[0].length,r.lastIndex=f,n=0;n<h;n+=1)for(o=0;o<p;o+=1)m=a?i.i[n][o]:i.i[n][o]+(s.i[n][o]-i.i[n][o])*l,e.i[n][o]=m,m=a?i.o[n][o]:i.o[n][o]+(s.o[n][o]-i.o[n][o])*l,e.o[n][o]=m,m=a?i.v[n][o]:i.v[n][o]+(s.v[n][o]-i.v[n][o])*l,e.v[n][o]=m}function a(){this.paths=this.localShapeCollection}function e(t){!function(t,e){if(t._length!==e._length||t.c!==e.c)return!1;var r,i=t._length;for(r=0;r<i;r+=1)if(t.v[r][0]!==e.v[r][0]||t.v[r][1]!==e.v[r][1]||t.o[r][0]!==e.o[r][0]||t.o[r][1]!==e.o[r][1]||t.i[r][0]!==e.i[r][0]||t.i[r][1]!==e.i[r][1])return!1;return!0}(this.v,t)&&(this.v=shape_pool.clone(t),this.localShapeCollection.releaseShapes(),this.localShapeCollection.addShape(this.v),this._mdf=!0,this.paths=this.localShapeCollection)}function r(){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length)if(this.lock)this.setVValue(this.pv);else{this.lock=!0,this._mdf=!1;var t,e=this.kf?this.pv:this.data.ks?this.data.ks.k:this.data.pt.k,r=this.effectsSequence.length;for(t=0;t<r;t+=1)e=this.effectsSequence[t](e);this.setVValue(e),this.lock=!1,this.frameId=this.elem.globalData.frameId}}function n(t,e,r){this.propType="shape",this.comp=t.comp,this.container=t,this.elem=t,this.data=e,this.k=!1,this.kf=!1,this._mdf=!1;var i=3===r?e.pt.k:e.ks.k;this.v=shape_pool.clone(i),this.pv=shape_pool.clone(this.v),this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.paths=this.localShapeCollection,this.paths.addShape(this.v),this.reset=a,this.effectsSequence=[]}function i(t){this.effectsSequence.push(t),this.container.addDynamicProperty(this)}function o(t,e,r){this.propType="shape",this.comp=t.comp,this.elem=t,this.container=t,this.offsetTime=t.data.st,this.keyframes=3===r?e.pt.k:e.ks.k,this.k=!0,this.kf=!0;var i=this.keyframes[0].s[0].i.length;this.keyframes[0].s[0].i[0].length;this.v=shape_pool.newElement(),this.v.setPathData(this.keyframes[0].s[0].c,i),this.pv=shape_pool.clone(this.v),this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.paths=this.localShapeCollection,this.paths.addShape(this.v),this.lastFrame=s,this.reset=a,this._caching={lastFrame:s,lastIndex:0},this.effectsSequence=[function(){var t=this.comp.renderedFrame-this.offsetTime,e=this.keyframes[0].t-this.offsetTime,r=this.keyframes[this.keyframes.length-1].t-this.offsetTime,i=this._caching.lastFrame;return i!==s&&(i<e&&t<e||r<i&&r<t)||(this._caching.lastIndex=i<t?this._caching.lastIndex:0,this.interpolateShape(t,this.pv,this._caching)),this._caching.lastFrame=t,this.pv}.bind(this)]}n.prototype.interpolateShape=t,n.prototype.getValue=r,n.prototype.setVValue=e,n.prototype.addEffect=i,o.prototype.getValue=r,o.prototype.interpolateShape=t,o.prototype.setVValue=e,o.prototype.addEffect=i;var h,p=(h=roundCorner,l.prototype={reset:a,getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertEllToPath())},convertEllToPath:function(){var t=this.p.v[0],e=this.p.v[1],r=this.s.v[0]/2,i=this.s.v[1]/2,s=3!==this.d,a=this.v;a.v[0][0]=t,a.v[0][1]=e-i,a.v[1][0]=s?t+r:t-r,a.v[1][1]=e,a.v[2][0]=t,a.v[2][1]=e+i,a.v[3][0]=s?t-r:t+r,a.v[3][1]=e,a.i[0][0]=s?t-r*h:t+r*h,a.i[0][1]=e-i,a.i[1][0]=s?t+r:t-r,a.i[1][1]=e-i*h,a.i[2][0]=s?t+r*h:t-r*h,a.i[2][1]=e+i,a.i[3][0]=s?t-r:t+r,a.i[3][1]=e+i*h,a.o[0][0]=s?t+r*h:t-r*h,a.o[0][1]=e-i,a.o[1][0]=s?t+r:t-r,a.o[1][1]=e+i*h,a.o[2][0]=s?t-r*h:t+r*h,a.o[2][1]=e+i,a.o[3][0]=s?t-r:t+r,a.o[3][1]=e-i*h}},extendPrototype([DynamicPropertyContainer],l),l);function l(t,e){this.v=shape_pool.newElement(),this.v.setPathData(!0,4),this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.paths=this.localShapeCollection,this.localShapeCollection.addShape(this.v),this.d=e.d,this.elem=t,this.comp=t.comp,this.frameId=-1,this.initDynamicPropertyContainer(t),this.p=PropertyFactory.getProp(t,e.p,1,0,this),this.s=PropertyFactory.getProp(t,e.s,1,0,this),this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertEllToPath())}var m=(f.prototype={reset:a,getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertToPath())},convertStarToPath:function(){var t,e,r,i,s=2*Math.floor(this.pt.v),a=2*Math.PI/s,n=!0,o=this.or.v,h=this.ir.v,p=this.os.v,l=this.is.v,m=2*Math.PI*o/(2*s),f=2*Math.PI*h/(2*s),c=-Math.PI/2;c+=this.r.v;var d=3===this.data.d?-1:1;for(t=this.v._length=0;t<s;t+=1){r=n?p:l,i=n?m:f;var u=(e=n?o:h)*Math.cos(c),y=e*Math.sin(c),g=0===u&&0===y?0:y/Math.sqrt(u*u+y*y),v=0===u&&0===y?0:-u/Math.sqrt(u*u+y*y);u+=+this.p.v[0],y+=+this.p.v[1],this.v.setTripleAt(u,y,u-g*i*r*d,y-v*i*r*d,u+g*i*r*d,y+v*i*r*d,t,!0),n=!n,c+=a*d}},convertPolygonToPath:function(){var t,e=Math.floor(this.pt.v),r=2*Math.PI/e,i=this.or.v,s=this.os.v,a=2*Math.PI*i/(4*e),n=-Math.PI/2,o=3===this.data.d?-1:1;for(n+=this.r.v,t=this.v._length=0;t<e;t+=1){var h=i*Math.cos(n),p=i*Math.sin(n),l=0===h&&0===p?0:p/Math.sqrt(h*h+p*p),m=0===h&&0===p?0:-h/Math.sqrt(h*h+p*p);h+=+this.p.v[0],p+=+this.p.v[1],this.v.setTripleAt(h,p,h-l*a*s*o,p-m*a*s*o,h+l*a*s*o,p+m*a*s*o,t,!0),n+=r*o}this.paths.length=0,this.paths[0]=this.v}},extendPrototype([DynamicPropertyContainer],f),f);function f(t,e){this.v=shape_pool.newElement(),this.v.setPathData(!0,0),this.elem=t,this.comp=t.comp,this.data=e,this.frameId=-1,this.d=e.d,this.initDynamicPropertyContainer(t),1===e.sy?(this.ir=PropertyFactory.getProp(t,e.ir,0,0,this),this.is=PropertyFactory.getProp(t,e.is,0,.01,this),this.convertToPath=this.convertStarToPath):this.convertToPath=this.convertPolygonToPath,this.pt=PropertyFactory.getProp(t,e.pt,0,0,this),this.p=PropertyFactory.getProp(t,e.p,1,0,this),this.r=PropertyFactory.getProp(t,e.r,0,degToRads,this),this.or=PropertyFactory.getProp(t,e.or,0,0,this),this.os=PropertyFactory.getProp(t,e.os,0,.01,this),this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.localShapeCollection.addShape(this.v),this.paths=this.localShapeCollection,this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertToPath())}var c=(d.prototype={convertRectToPath:function(){var t=this.p.v[0],e=this.p.v[1],r=this.s.v[0]/2,i=this.s.v[1]/2,s=bm_min(r,i,this.r.v),a=s*(1-roundCorner);this.v._length=0,2===this.d||1===this.d?(this.v.setTripleAt(t+r,e-i+s,t+r,e-i+s,t+r,e-i+a,0,!0),this.v.setTripleAt(t+r,e+i-s,t+r,e+i-a,t+r,e+i-s,1,!0),0!==s?(this.v.setTripleAt(t+r-s,e+i,t+r-s,e+i,t+r-a,e+i,2,!0),this.v.setTripleAt(t-r+s,e+i,t-r+a,e+i,t-r+s,e+i,3,!0),this.v.setTripleAt(t-r,e+i-s,t-r,e+i-s,t-r,e+i-a,4,!0),this.v.setTripleAt(t-r,e-i+s,t-r,e-i+a,t-r,e-i+s,5,!0),this.v.setTripleAt(t-r+s,e-i,t-r+s,e-i,t-r+a,e-i,6,!0),this.v.setTripleAt(t+r-s,e-i,t+r-a,e-i,t+r-s,e-i,7,!0)):(this.v.setTripleAt(t-r,e+i,t-r+a,e+i,t-r,e+i,2),this.v.setTripleAt(t-r,e-i,t-r,e-i+a,t-r,e-i,3))):(this.v.setTripleAt(t+r,e-i+s,t+r,e-i+a,t+r,e-i+s,0,!0),0!==s?(this.v.setTripleAt(t+r-s,e-i,t+r-s,e-i,t+r-a,e-i,1,!0),this.v.setTripleAt(t-r+s,e-i,t-r+a,e-i,t-r+s,e-i,2,!0),this.v.setTripleAt(t-r,e-i+s,t-r,e-i+s,t-r,e-i+a,3,!0),this.v.setTripleAt(t-r,e+i-s,t-r,e+i-a,t-r,e+i-s,4,!0),this.v.setTripleAt(t-r+s,e+i,t-r+s,e+i,t-r+a,e+i,5,!0),this.v.setTripleAt(t+r-s,e+i,t+r-a,e+i,t+r-s,e+i,6,!0),this.v.setTripleAt(t+r,e+i-s,t+r,e+i-s,t+r,e+i-a,7,!0)):(this.v.setTripleAt(t-r,e-i,t-r+a,e-i,t-r,e-i,1,!0),this.v.setTripleAt(t-r,e+i,t-r,e+i-a,t-r,e+i,2,!0),this.v.setTripleAt(t+r,e+i,t+r-a,e+i,t+r,e+i,3,!0)))},getValue:function(t){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertRectToPath())},reset:a},extendPrototype([DynamicPropertyContainer],d),d);function d(t,e){this.v=shape_pool.newElement(),this.v.c=!0,this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.localShapeCollection.addShape(this.v),this.paths=this.localShapeCollection,this.elem=t,this.comp=t.comp,this.frameId=-1,this.d=e.d,this.initDynamicPropertyContainer(t),this.p=PropertyFactory.getProp(t,e.p,1,0,this),this.s=PropertyFactory.getProp(t,e.s,1,0,this),this.r=PropertyFactory.getProp(t,e.r,0,0,this),this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertRectToPath())}var u={getShapeProp:function(t,e,r){var i;return 3===r||4===r?i=(3===r?e.pt:e.ks).k.length?new o(t,e,r):new n(t,e,r):5===r?i=new c(t,e):6===r?i=new p(t,e):7===r&&(i=new m(t,e)),i.k&&t.addDynamicProperty(i),i},getConstructorFunction:function(){return n},getKeyframedConstructorFunction:function(){return o}};return u}(),ShapeModifiers=(Tr={},Ur={},Tr.registerModifier=function(t,e){Ur[t]||(Ur[t]=e)},Tr.getModifier=function(t,e,r){return new Ur[t](e,r)},Tr),Tr,Ur;function ShapeModifier(){}function TrimModifier(){}function RoundCornersModifier(){}function RepeaterModifier(){}function ShapeCollection(){this._length=0,this._maxLength=4,this.shapes=createSizedArray(this._maxLength)}function DashProperty(t,e,r,i){this.elem=t,this.frameId=-1,this.dataProps=createSizedArray(e.length),this.renderer=r,this.k=!1,this.dashStr="",this.dashArray=createTypedArray("float32",e.length?e.length-1:0),this.dashoffset=createTypedArray("float32",1),this.initDynamicPropertyContainer(i);var s,a,n=e.length||0;for(s=0;s<n;s+=1)a=PropertyFactory.getProp(t,e[s].v,0,0,this),this.k=a.k||this.k,this.dataProps[s]={n:e[s].n,p:a};this.k||this.getValue(!0),this._isAnimated=this.k}function GradientProperty(t,e,r){this.data=e,this.c=createTypedArray("uint8c",4*e.p);var i=e.k.k[0].s?e.k.k[0].s.length-4*e.p:e.k.k.length-4*e.p;this.o=createTypedArray("float32",i),this._cmdf=!1,this._omdf=!1,this._collapsable=this.checkCollapsable(),this._hasOpacity=i,this.initDynamicPropertyContainer(r),this.prop=PropertyFactory.getProp(t,e.k,1,null,this),this.k=this.prop.k,this.getValue(!0)}ShapeModifier.prototype.initModifierProperties=function(){},ShapeModifier.prototype.addShapeToModifier=function(){},ShapeModifier.prototype.addShape=function(t){if(!this.closed){var e={shape:t.sh,data:t,localShapeCollection:shapeCollection_pool.newShapeCollection()};this.shapes.push(e),this.addShapeToModifier(e),this._isAnimated&&t.setAsAnimated()}},ShapeModifier.prototype.init=function(t,e){this.shapes=[],this.elem=t,this.initDynamicPropertyContainer(t),this.initModifierProperties(t,e),this.frameId=initialDefaultFrame,this.closed=!1,this.k=!1,this.dynamicProperties.length?this.k=!0:this.getValue(!0)},ShapeModifier.prototype.processKeys=function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties())},extendPrototype([DynamicPropertyContainer],ShapeModifier),extendPrototype([ShapeModifier],TrimModifier),TrimModifier.prototype.initModifierProperties=function(t,e){this.s=PropertyFactory.getProp(t,e.s,0,.01,this),this.e=PropertyFactory.getProp(t,e.e,0,.01,this),this.o=PropertyFactory.getProp(t,e.o,0,0,this),this.sValue=0,this.eValue=0,this.getValue=this.processKeys,this.m=e.m,this._isAnimated=!!this.s.effectsSequence.length||!!this.e.effectsSequence.length||!!this.o.effectsSequence.length},TrimModifier.prototype.addShapeToModifier=function(t){t.pathsData=[]},TrimModifier.prototype.calculateShapeEdges=function(t,e,r,i,s){var a=[];e<=1?a.push({s:t,e:e}):1<=t?a.push({s:t-1,e:e-1}):(a.push({s:t,e:1}),a.push({s:0,e:e-1}));var n,o,h=[],p=a.length;for(n=0;n<p;n+=1){var l,m;if((o=a[n]).e*s<i||o.s*s>i+r);else l=o.s*s<=i?0:(o.s*s-i)/r,m=o.e*s>=i+r?1:(o.e*s-i)/r,h.push([l,m])}return h.length||h.push([0,0]),h},TrimModifier.prototype.releasePathsData=function(t){var e,r=t.length;for(e=0;e<r;e+=1)segments_length_pool.release(t[e]);return t.length=0,t},TrimModifier.prototype.processShapes=function(t){var e,r,i;if(this._mdf||t){var s=this.o.v%360/360;if(s<0&&(s+=1),e=(1<this.s.v?1:this.s.v<0?0:this.s.v)+s,(r=(1<this.e.v?1:this.e.v<0?0:this.e.v)+s)<e){var a=e;e=r,r=a}e=1e-4*Math.round(1e4*e),r=1e-4*Math.round(1e4*r),this.sValue=e,this.eValue=r}else e=this.sValue,r=this.eValue;var n,o,h,p,l,m,f=this.shapes.length,c=0;if(r===e)for(n=0;n<f;n+=1)this.shapes[n].localShapeCollection.releaseShapes(),this.shapes[n].shape._mdf=!0,this.shapes[n].shape.paths=this.shapes[n].localShapeCollection;else if(1===r&&0===e||0===r&&1===e){if(this._mdf)for(n=0;n<f;n+=1)this.shapes[n].pathsData.length=0,this.shapes[n].shape._mdf=!0}else{var d,u,y=[];for(n=0;n<f;n+=1)if((d=this.shapes[n]).shape._mdf||this._mdf||t||2===this.m){if(h=(i=d.shape.paths)._length,m=0,!d.shape._mdf&&d.pathsData.length)m=d.totalShapeLength;else{for(p=this.releasePathsData(d.pathsData),o=0;o<h;o+=1)l=bez.getSegmentsLength(i.shapes[o]),p.push(l),m+=l.totalLength;d.totalShapeLength=m,d.pathsData=p}c+=m,d.shape._mdf=!0}else d.shape.paths=d.localShapeCollection;var g,v=e,P=r,b=0;for(n=f-1;0<=n;n-=1)if((d=this.shapes[n]).shape._mdf){for((u=d.localShapeCollection).releaseShapes(),2===this.m&&1<f?(g=this.calculateShapeEdges(e,r,d.totalShapeLength,b,c),b+=d.totalShapeLength):g=[[v,P]],h=g.length,o=0;o<h;o+=1){v=g[o][0],P=g[o][1],y.length=0,P<=1?y.push({s:d.totalShapeLength*v,e:d.totalShapeLength*P}):1<=v?y.push({s:d.totalShapeLength*(v-1),e:d.totalShapeLength*(P-1)}):(y.push({s:d.totalShapeLength*v,e:d.totalShapeLength}),y.push({s:0,e:d.totalShapeLength*(P-1)}));var x=this.addShapes(d,y[0]);if(y[0].s!==y[0].e){if(1<y.length)if(d.shape.paths.shapes[d.shape.paths._length-1].c){var _=x.pop();this.addPaths(x,u),x=this.addShapes(d,y[1],_)}else this.addPaths(x,u),x=this.addShapes(d,y[1]);this.addPaths(x,u)}}d.shape.paths=u}}},TrimModifier.prototype.addPaths=function(t,e){var r,i=t.length;for(r=0;r<i;r+=1)e.addShape(t[r])},TrimModifier.prototype.addSegment=function(t,e,r,i,s,a,n){s.setXYAt(e[0],e[1],"o",a),s.setXYAt(r[0],r[1],"i",a+1),n&&s.setXYAt(t[0],t[1],"v",a),s.setXYAt(i[0],i[1],"v",a+1)},TrimModifier.prototype.addSegmentFromArray=function(t,e,r,i){e.setXYAt(t[1],t[5],"o",r),e.setXYAt(t[2],t[6],"i",r+1),i&&e.setXYAt(t[0],t[4],"v",r),e.setXYAt(t[3],t[7],"v",r+1)},TrimModifier.prototype.addShapes=function(t,e,r){var i,s,a,n,o,h,p,l,m=t.pathsData,f=t.shape.paths.shapes,c=t.shape.paths._length,d=0,u=[],y=!0;for(l=r?(o=r._length,r._length):(r=shape_pool.newElement(),o=0),u.push(r),i=0;i<c;i+=1){for(h=m[i].lengths,r.c=f[i].c,a=f[i].c?h.length:h.length+1,s=1;s<a;s+=1)if(d+(n=h[s-1]).addedLength<e.s)d+=n.addedLength,r.c=!1;else{if(d>e.e){r.c=!1;break}e.s<=d&&e.e>=d+n.addedLength?(this.addSegment(f[i].v[s-1],f[i].o[s-1],f[i].i[s],f[i].v[s],r,o,y),y=!1):(p=bez.getNewSegment(f[i].v[s-1],f[i].v[s],f[i].o[s-1],f[i].i[s],(e.s-d)/n.addedLength,(e.e-d)/n.addedLength,h[s-1]),this.addSegmentFromArray(p,r,o,y),y=!1,r.c=!1),d+=n.addedLength,o+=1}if(f[i].c&&h.length){if(n=h[s-1],d<=e.e){var g=h[s-1].addedLength;e.s<=d&&e.e>=d+g?(this.addSegment(f[i].v[s-1],f[i].o[s-1],f[i].i[0],f[i].v[0],r,o,y),y=!1):(p=bez.getNewSegment(f[i].v[s-1],f[i].v[0],f[i].o[s-1],f[i].i[0],(e.s-d)/g,(e.e-d)/g,h[s-1]),this.addSegmentFromArray(p,r,o,y),y=!1,r.c=!1)}else r.c=!1;d+=n.addedLength,o+=1}if(r._length&&(r.setXYAt(r.v[l][0],r.v[l][1],"i",l),r.setXYAt(r.v[r._length-1][0],r.v[r._length-1][1],"o",r._length-1)),d>e.e)break;i<c-1&&(r=shape_pool.newElement(),y=!0,u.push(r),o=0)}return u},ShapeModifiers.registerModifier("tm",TrimModifier),extendPrototype([ShapeModifier],RoundCornersModifier),RoundCornersModifier.prototype.initModifierProperties=function(t,e){this.getValue=this.processKeys,this.rd=PropertyFactory.getProp(t,e.r,0,null,this),this._isAnimated=!!this.rd.effectsSequence.length},RoundCornersModifier.prototype.processPath=function(t,e){var r=shape_pool.newElement();r.c=t.c;var i,s,a,n,o,h,p,l,m,f,c,d,u,y=t._length,g=0;for(i=0;i<y;i+=1)s=t.v[i],n=t.o[i],a=t.i[i],s[0]===n[0]&&s[1]===n[1]&&s[0]===a[0]&&s[1]===a[1]?0!==i&&i!==y-1||t.c?(o=0===i?t.v[y-1]:t.v[i-1],p=(h=Math.sqrt(Math.pow(s[0]-o[0],2)+Math.pow(s[1]-o[1],2)))?Math.min(h/2,e)/h:0,l=d=s[0]+(o[0]-s[0])*p,m=u=s[1]-(s[1]-o[1])*p,f=l-(l-s[0])*roundCorner,c=m-(m-s[1])*roundCorner,r.setTripleAt(l,m,f,c,d,u,g),g+=1,o=i===y-1?t.v[0]:t.v[i+1],p=(h=Math.sqrt(Math.pow(s[0]-o[0],2)+Math.pow(s[1]-o[1],2)))?Math.min(h/2,e)/h:0,l=f=s[0]+(o[0]-s[0])*p,m=c=s[1]+(o[1]-s[1])*p,d=l-(l-s[0])*roundCorner,u=m-(m-s[1])*roundCorner,r.setTripleAt(l,m,f,c,d,u,g)):r.setTripleAt(s[0],s[1],n[0],n[1],a[0],a[1],g):r.setTripleAt(t.v[i][0],t.v[i][1],t.o[i][0],t.o[i][1],t.i[i][0],t.i[i][1],g),g+=1;return r},RoundCornersModifier.prototype.processShapes=function(t){var e,r,i,s,a,n,o=this.shapes.length,h=this.rd.v;if(0!==h)for(r=0;r<o;r+=1){if((a=this.shapes[r]).shape.paths,n=a.localShapeCollection,a.shape._mdf||this._mdf||t)for(n.releaseShapes(),a.shape._mdf=!0,e=a.shape.paths.shapes,s=a.shape.paths._length,i=0;i<s;i+=1)n.addShape(this.processPath(e[i],h));a.shape.paths=a.localShapeCollection}this.dynamicProperties.length||(this._mdf=!1)},ShapeModifiers.registerModifier("rd",RoundCornersModifier),extendPrototype([ShapeModifier],RepeaterModifier),RepeaterModifier.prototype.initModifierProperties=function(t,e){this.getValue=this.processKeys,this.c=PropertyFactory.getProp(t,e.c,0,null,this),this.o=PropertyFactory.getProp(t,e.o,0,null,this),this.tr=TransformPropertyFactory.getTransformProperty(t,e.tr,this),this.so=PropertyFactory.getProp(t,e.tr.so,0,.01,this),this.eo=PropertyFactory.getProp(t,e.tr.eo,0,.01,this),this.data=e,this.dynamicProperties.length||this.getValue(!0),this._isAnimated=!!this.dynamicProperties.length,this.pMatrix=new Matrix,this.rMatrix=new Matrix,this.sMatrix=new Matrix,this.tMatrix=new Matrix,this.matrix=new Matrix},RepeaterModifier.prototype.applyTransforms=function(t,e,r,i,s,a){var n=a?-1:1,o=i.s.v[0]+(1-i.s.v[0])*(1-s),h=i.s.v[1]+(1-i.s.v[1])*(1-s);t.translate(i.p.v[0]*n*s,i.p.v[1]*n*s,i.p.v[2]),e.translate(-i.a.v[0],-i.a.v[1],i.a.v[2]),e.rotate(-i.r.v*n*s),e.translate(i.a.v[0],i.a.v[1],i.a.v[2]),r.translate(-i.a.v[0],-i.a.v[1],i.a.v[2]),r.scale(a?1/o:o,a?1/h:h),r.translate(i.a.v[0],i.a.v[1],i.a.v[2])},RepeaterModifier.prototype.init=function(t,e,r,i){this.elem=t,this.arr=e,this.pos=r,this.elemsData=i,this._currentCopies=0,this._elements=[],this._groups=[],this.frameId=-1,this.initDynamicPropertyContainer(t),this.initModifierProperties(t,e[r]);for(;0<r;)r-=1,this._elements.unshift(e[r]),1;this.dynamicProperties.length?this.k=!0:this.getValue(!0)},RepeaterModifier.prototype.resetElements=function(t){var e,r=t.length;for(e=0;e<r;e+=1)t[e]._processed=!1,"gr"===t[e].ty&&this.resetElements(t[e].it)},RepeaterModifier.prototype.cloneElements=function(t){t.length;var e=JSON.parse(JSON.stringify(t));return this.resetElements(e),e},RepeaterModifier.prototype.changeGroupRender=function(t,e){var r,i=t.length;for(r=0;r<i;r+=1)t[r]._render=e,"gr"===t[r].ty&&this.changeGroupRender(t[r].it,e)},RepeaterModifier.prototype.processShapes=function(t){var e,r,i,s,a;if(this._mdf||t){var n,o=Math.ceil(this.c.v);if(this._groups.length<o){for(;this._groups.length<o;){var h={it:this.cloneElements(this._elements),ty:"gr"};h.it.push({a:{a:0,ix:1,k:[0,0]},nm:"Transform",o:{a:0,ix:7,k:100},p:{a:0,ix:2,k:[0,0]},r:{a:1,ix:6,k:[{s:0,e:0,t:0},{s:0,e:0,t:1}]},s:{a:0,ix:3,k:[100,100]},sa:{a:0,ix:5,k:0},sk:{a:0,ix:4,k:0},ty:"tr"}),this.arr.splice(0,0,h),this._groups.splice(0,0,h),this._currentCopies+=1}this.elem.reloadShapes()}for(i=a=0;i<=this._groups.length-1;i+=1)n=a<o,this._groups[i]._render=n,this.changeGroupRender(this._groups[i].it,n),a+=1;this._currentCopies=o;var p=this.o.v,l=p%1,m=0<p?Math.floor(p):Math.ceil(p),f=(this.tr.v.props,this.pMatrix.props),c=this.rMatrix.props,d=this.sMatrix.props;this.pMatrix.reset(),this.rMatrix.reset(),this.sMatrix.reset(),this.tMatrix.reset(),this.matrix.reset();var u,y,g=0;if(0<p){for(;g<m;)this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!1),g+=1;l&&(this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,l,!1),g+=l)}else if(p<0){for(;m<g;)this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!0),g-=1;l&&(this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,-l,!0),g-=l)}for(i=1===this.data.m?0:this._currentCopies-1,s=1===this.data.m?1:-1,a=this._currentCopies;a;){if(y=(r=(e=this.elemsData[i].it)[e.length-1].transform.mProps.v.props).length,e[e.length-1].transform.mProps._mdf=!0,e[e.length-1].transform.op._mdf=!0,e[e.length-1].transform.op.v=this.so.v+(this.eo.v-this.so.v)*(i/(this._currentCopies-1)),0!==g){for((0!==i&&1===s||i!==this._currentCopies-1&&-1===s)&&this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!1),this.matrix.transform(c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8],c[9],c[10],c[11],c[12],c[13],c[14],c[15]),this.matrix.transform(d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8],d[9],d[10],d[11],d[12],d[13],d[14],d[15]),this.matrix.transform(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8],f[9],f[10],f[11],f[12],f[13],f[14],f[15]),u=0;u<y;u+=1)r[u]=this.matrix.props[u];this.matrix.reset()}else for(this.matrix.reset(),u=0;u<y;u+=1)r[u]=this.matrix.props[u];g+=1,a-=1,i+=s}}else for(a=this._currentCopies,i=0,s=1;a;)r=(e=this.elemsData[i].it)[e.length-1].transform.mProps.v.props,e[e.length-1].transform.mProps._mdf=!1,e[e.length-1].transform.op._mdf=!1,a-=1,i+=s},RepeaterModifier.prototype.addShape=function(){},ShapeModifiers.registerModifier("rp",RepeaterModifier),ShapeCollection.prototype.addShape=function(t){this._length===this._maxLength&&(this.shapes=this.shapes.concat(createSizedArray(this._maxLength)),this._maxLength*=2),this.shapes[this._length]=t,this._length+=1},ShapeCollection.prototype.releaseShapes=function(){var t;for(t=0;t<this._length;t+=1)shape_pool.release(this.shapes[t]);this._length=0},DashProperty.prototype.getValue=function(t){if((this.elem.globalData.frameId!==this.frameId||t)&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf=this._mdf||t,this._mdf)){var e=0,r=this.dataProps.length;for("svg"===this.renderer&&(this.dashStr=""),e=0;e<r;e+=1)"o"!=this.dataProps[e].n?"svg"===this.renderer?this.dashStr+=" "+this.dataProps[e].p.v:this.dashArray[e]=this.dataProps[e].p.v:this.dashoffset[0]=this.dataProps[e].p.v}},extendPrototype([DynamicPropertyContainer],DashProperty),GradientProperty.prototype.comparePoints=function(t,e){for(var r=0,i=this.o.length/2;r<i;){if(.01<Math.abs(t[4*r]-t[4*e+2*r]))return!1;r+=1}return!0},GradientProperty.prototype.checkCollapsable=function(){if(this.o.length/2!=this.c.length/4)return!1;if(this.data.k.k[0].s)for(var t=0,e=this.data.k.k.length;t<e;){if(!this.comparePoints(this.data.k.k[t].s,this.data.p))return!1;t+=1}else if(!this.comparePoints(this.data.k.k,this.data.p))return!1;return!0},GradientProperty.prototype.getValue=function(t){if(this.prop.getValue(),this._mdf=!1,this._cmdf=!1,this._omdf=!1,this.prop._mdf||t){var e,r,i,s=4*this.data.p;for(e=0;e<s;e+=1)r=e%4==0?100:255,i=Math.round(this.prop.v[e]*r),this.c[e]!==i&&(this.c[e]=i,this._cmdf=!t);if(this.o.length)for(s=this.prop.v.length,e=4*this.data.p;e<s;e+=1)r=e%2==0?100:1,i=e%2==0?Math.round(100*this.prop.v[e]):this.prop.v[e],this.o[e-4*this.data.p]!==i&&(this.o[e-4*this.data.p]=i,this._omdf=!t);this._mdf=!t}},extendPrototype([DynamicPropertyContainer],GradientProperty);var buildShapeString=function(t,e,r,i){if(0===e)return"";var s,a=t.o,n=t.i,o=t.v,h=" M"+i.applyToPointStringified(o[0][0],o[0][1]);for(s=1;s<e;s+=1)h+=" C"+i.applyToPointStringified(a[s-1][0],a[s-1][1])+" "+i.applyToPointStringified(n[s][0],n[s][1])+" "+i.applyToPointStringified(o[s][0],o[s][1]);return r&&e&&(h+=" C"+i.applyToPointStringified(a[s-1][0],a[s-1][1])+" "+i.applyToPointStringified(n[0][0],n[0][1])+" "+i.applyToPointStringified(o[0][0],o[0][1]),h+="z"),h},ImagePreloader=function(){},featureSupport=(Iv={maskType:!0},(/MSIE 10/i.test(navigator.userAgent)||/MSIE 9/i.test(navigator.userAgent)||/rv:11.0/i.test(navigator.userAgent)||/Edge\/\d./i.test(navigator.userAgent))&&(Iv.maskType=!1),Iv),Iv,filtersFactory=(Jv={},Jv.createFilter=function(t){var e=createNS("filter");return e.setAttribute("id",t),e.setAttribute("filterUnits","objectBoundingBox"),e.setAttribute("x","0%"),e.setAttribute("y","0%"),e.setAttribute("width","100%"),e.setAttribute("height","100%"),e},Jv.createAlphaToLuminanceFilter=function(){var t=createNS("feColorMatrix");return t.setAttribute("type","matrix"),t.setAttribute("color-interpolation-filters","sRGB"),t.setAttribute("values","0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1"),t},Jv),Jv,assetLoader={load:function(t,e,r){var i,s=new XMLHttpRequest;s.open("GET",t,!0);try{s.responseType="json"}catch(t){}s.send(),s.onreadystatechange=function(){if(4==s.readyState)if(200==s.status)i=Pv(s),e(i);else try{i=Pv(s),e(i)}catch(t){r&&r(t)}}}};function Pv(t){return t.response&&"object"==typeof t.response?t.response:t.response&&"string"==typeof t.response?JSON.parse(t.response):t.responseText?JSON.parse(t.responseText):void 0}var assetLoader=null;function TextAnimatorProperty(t,e,r){this._isFirstFrame=!0,this._hasMaskedPath=!1,this._frameId=-1,this._textData=t,this._renderType=e,this._elem=r,this._animatorsData=createSizedArray(this._textData.a.length),this._pathData={},this._moreOptions={alignment:{}},this.renderedLetters=[],this.lettersChangedFlag=!1,this.initDynamicPropertyContainer(r)}function TextAnimatorDataProperty(t,e,r){var i={propType:!1},s=PropertyFactory.getProp,a=e.a;this.a={r:a.r?s(t,a.r,0,degToRads,r):i,rx:a.rx?s(t,a.rx,0,degToRads,r):i,ry:a.ry?s(t,a.ry,0,degToRads,r):i,sk:a.sk?s(t,a.sk,0,degToRads,r):i,sa:a.sa?s(t,a.sa,0,degToRads,r):i,s:a.s?s(t,a.s,1,.01,r):i,a:a.a?s(t,a.a,1,0,r):i,o:a.o?s(t,a.o,0,.01,r):i,p:a.p?s(t,a.p,1,0,r):i,sw:a.sw?s(t,a.sw,0,0,r):i,sc:a.sc?s(t,a.sc,1,0,r):i,fc:a.fc?s(t,a.fc,1,0,r):i,fh:a.fh?s(t,a.fh,0,0,r):i,fs:a.fs?s(t,a.fs,0,.01,r):i,fb:a.fb?s(t,a.fb,0,.01,r):i,t:a.t?s(t,a.t,0,0,r):i},this.s=TextSelectorProp.getTextSelectorProp(t,e.s,r),this.s.t=e.s.t}function LetterProps(t,e,r,i,s,a){this.o=t,this.sw=e,this.sc=r,this.fc=i,this.m=s,this.p=a,this._mdf={o:!0,sw:!!e,sc:!!r,fc:!!i,m:!0,p:!0}}function TextProperty(t,e){this._frameId=initialDefaultFrame,this.pv="",this.v="",this.kf=!1,this._isFirstFrame=!0,this._mdf=!1,this.data=e,this.elem=t,this.comp=this.elem.comp,this.keysIndex=0,this.canResize=!1,this.minimumFontSize=1,this.effectsSequence=[],this.currentData={ascent:0,boxWidth:this.defaultBoxWidth,f:"",fStyle:"",fWeight:"",fc:"",j:"",justifyOffset:"",l:[],lh:0,lineWidths:[],ls:"",of:"",s:"",sc:"",sw:0,t:0,tr:0,sz:0,ps:null,fillColorAnim:!1,strokeColorAnim:!1,strokeWidthAnim:!1,yOffset:0,finalSize:0,finalText:[],finalLineHeight:0,__complete:!1},this.copyData(this.currentData,this.data.d.k[0].s),this.searchProperty()||this.completeTextData(this.currentData)}TextAnimatorProperty.prototype.searchProperties=function(){var t,e,r=this._textData.a.length,i=PropertyFactory.getProp;for(t=0;t<r;t+=1)e=this._textData.a[t],this._animatorsData[t]=new TextAnimatorDataProperty(this._elem,e,this);this._textData.p&&"m"in this._textData.p?(this._pathData={f:i(this._elem,this._textData.p.f,0,0,this),l:i(this._elem,this._textData.p.l,0,0,this),r:this._textData.p.r,m:this._elem.maskManager.getMaskProperty(this._textData.p.m)},this._hasMaskedPath=!0):this._hasMaskedPath=!1,this._moreOptions.alignment=i(this._elem,this._textData.m.a,1,0,this)},TextAnimatorProperty.prototype.getMeasures=function(t,e){if(this.lettersChangedFlag=e,this._mdf||this._isFirstFrame||e||this._hasMaskedPath&&this._pathData.m._mdf){this._isFirstFrame=!1;var r,i,s,a,n,o,h,p,l,m,f,c,d,u,y,g,v,P,b,x=this._moreOptions.alignment.v,_=this._animatorsData,S=this._textData,T=this.mHelper,A=this._renderType,C=this.renderedLetters.length,E=(this.data,t.l);if(this._hasMaskedPath){if(b=this._pathData.m,!this._pathData.n||this._pathData._mdf){var k,D=b.v;for(this._pathData.r&&(D=D.reverse()),n={tLength:0,segments:[]},a=D._length-1,s=g=0;s<a;s+=1)k=bez.buildBezierData(D.v[s],D.v[s+1],[D.o[s][0]-D.v[s][0],D.o[s][1]-D.v[s][1]],[D.i[s+1][0]-D.v[s+1][0],D.i[s+1][1]-D.v[s+1][1]]),n.tLength+=k.segmentLength,n.segments.push(k),g+=k.segmentLength;s=a,b.v.c&&(k=bez.buildBezierData(D.v[s],D.v[0],[D.o[s][0]-D.v[s][0],D.o[s][1]-D.v[s][1]],[D.i[0][0]-D.v[0][0],D.i[0][1]-D.v[0][1]]),n.tLength+=k.segmentLength,n.segments.push(k),g+=k.segmentLength),this._pathData.pi=n}if(n=this._pathData.pi,o=this._pathData.f.v,m=1,l=!(p=f=0),u=n.segments,o<0&&b.v.c)for(n.tLength<Math.abs(o)&&(o=-Math.abs(o)%n.tLength),m=(d=u[f=u.length-1].points).length-1;o<0;)o+=d[m].partialLength,(m-=1)<0&&(m=(d=u[f-=1].points).length-1);c=(d=u[f].points)[m-1],y=(h=d[m]).partialLength}a=E.length,i=r=0;var M,I,w,F,V=1.2*t.finalSize*.714,R=!0;w=_.length;var L,z,O,B,N,G,j,J,K,q,H,W,X,Y=-1,Q=o,$=f,U=m,Z=-1,tt="",et=this.defaultPropsArray;if(2===t.j||1===t.j){var rt=0,it=0,st=2===t.j?-.5:-1,at=0,nt=!0;for(s=0;s<a;s+=1)if(E[s].n){for(rt&&(rt+=it);at<s;)E[at].animatorJustifyOffset=rt,at+=1;nt=!(rt=0)}else{for(I=0;I<w;I+=1)(M=_[I].a).t.propType&&(nt&&2===t.j&&(it+=M.t.v*st),(L=_[I].s.getMult(E[s].anIndexes[I],S.a[I].s.totalChars)).length?rt+=M.t.v*L[0]*st:rt+=M.t.v*L*st);nt=!1}for(rt&&(rt+=it);at<s;)E[at].animatorJustifyOffset=rt,at+=1}for(s=0;s<a;s+=1){if(T.reset(),N=1,E[s].n)r=0,i+=t.yOffset,i+=R?1:0,o=Q,R=!1,0,this._hasMaskedPath&&(m=U,c=(d=u[f=$].points)[m-1],y=(h=d[m]).partialLength,p=0),X=q=W=tt="",et=this.defaultPropsArray;else{if(this._hasMaskedPath){if(Z!==E[s].line){switch(t.j){case 1:o+=g-t.lineWidths[E[s].line];break;case 2:o+=(g-t.lineWidths[E[s].line])/2}Z=E[s].line}Y!==E[s].ind&&(E[Y]&&(o+=E[Y].extra),o+=E[s].an/2,Y=E[s].ind),o+=x[0]*E[s].an/200;var ot=0;for(I=0;I<w;I+=1)(M=_[I].a).p.propType&&((L=_[I].s.getMult(E[s].anIndexes[I],S.a[I].s.totalChars)).length?ot+=M.p.v[0]*L[0]:ot+=M.p.v[0]*L),M.a.propType&&((L=_[I].s.getMult(E[s].anIndexes[I],S.a[I].s.totalChars)).length?ot+=M.a.v[0]*L[0]:ot+=M.a.v[0]*L);for(l=!0;l;)o+ot<=p+y||!d?(v=(o+ot-p)/h.partialLength,O=c.point[0]+(h.point[0]-c.point[0])*v,B=c.point[1]+(h.point[1]-c.point[1])*v,T.translate(-x[0]*E[s].an/200,-x[1]*V/100),l=!1):d&&(p+=h.partialLength,(m+=1)>=d.length&&(m=0,d=u[f+=1]?u[f].points:b.v.c?u[f=m=0].points:(p-=h.partialLength,null)),d&&(c=h,y=(h=d[m]).partialLength));z=E[s].an/2-E[s].add,T.translate(-z,0,0)}else z=E[s].an/2-E[s].add,T.translate(-z,0,0),T.translate(-x[0]*E[s].an/200,-x[1]*V/100,0);for(E[s].l/2,I=0;I<w;I+=1)(M=_[I].a).t.propType&&(L=_[I].s.getMult(E[s].anIndexes[I],S.a[I].s.totalChars),0===r&&0===t.j||(this._hasMaskedPath?L.length?o+=M.t.v*L[0]:o+=M.t.v*L:L.length?r+=M.t.v*L[0]:r+=M.t.v*L));for(E[s].l/2,t.strokeWidthAnim&&(j=t.sw||0),t.strokeColorAnim&&(G=t.sc?[t.sc[0],t.sc[1],t.sc[2]]:[0,0,0]),t.fillColorAnim&&t.fc&&(J=[t.fc[0],t.fc[1],t.fc[2]]),I=0;I<w;I+=1)(M=_[I].a).a.propType&&((L=_[I].s.getMult(E[s].anIndexes[I],S.a[I].s.totalChars)).length?T.translate(-M.a.v[0]*L[0],-M.a.v[1]*L[1],M.a.v[2]*L[2]):T.translate(-M.a.v[0]*L,-M.a.v[1]*L,M.a.v[2]*L));for(I=0;I<w;I+=1)(M=_[I].a).s.propType&&((L=_[I].s.getMult(E[s].anIndexes[I],S.a[I].s.totalChars)).length?T.scale(1+(M.s.v[0]-1)*L[0],1+(M.s.v[1]-1)*L[1],1):T.scale(1+(M.s.v[0]-1)*L,1+(M.s.v[1]-1)*L,1));for(I=0;I<w;I+=1){if(M=_[I].a,L=_[I].s.getMult(E[s].anIndexes[I],S.a[I].s.totalChars),M.sk.propType&&(L.length?T.skewFromAxis(-M.sk.v*L[0],M.sa.v*L[1]):T.skewFromAxis(-M.sk.v*L,M.sa.v*L)),M.r.propType&&(L.length?T.rotateZ(-M.r.v*L[2]):T.rotateZ(-M.r.v*L)),M.ry.propType&&(L.length?T.rotateY(M.ry.v*L[1]):T.rotateY(M.ry.v*L)),M.rx.propType&&(L.length?T.rotateX(M.rx.v*L[0]):T.rotateX(M.rx.v*L)),M.o.propType&&(L.length?N+=(M.o.v*L[0]-N)*L[0]:N+=(M.o.v*L-N)*L),t.strokeWidthAnim&&M.sw.propType&&(L.length?j+=M.sw.v*L[0]:j+=M.sw.v*L),t.strokeColorAnim&&M.sc.propType)for(K=0;K<3;K+=1)L.length?G[K]=G[K]+(M.sc.v[K]-G[K])*L[0]:G[K]=G[K]+(M.sc.v[K]-G[K])*L;if(t.fillColorAnim&&t.fc){if(M.fc.propType)for(K=0;K<3;K+=1)L.length?J[K]=J[K]+(M.fc.v[K]-J[K])*L[0]:J[K]=J[K]+(M.fc.v[K]-J[K])*L;M.fh.propType&&(J=L.length?addHueToRGB(J,M.fh.v*L[0]):addHueToRGB(J,M.fh.v*L)),M.fs.propType&&(J=L.length?addSaturationToRGB(J,M.fs.v*L[0]):addSaturationToRGB(J,M.fs.v*L)),M.fb.propType&&(J=L.length?addBrightnessToRGB(J,M.fb.v*L[0]):addBrightnessToRGB(J,M.fb.v*L))}}for(I=0;I<w;I+=1)(M=_[I].a).p.propType&&(L=_[I].s.getMult(E[s].anIndexes[I],S.a[I].s.totalChars),this._hasMaskedPath?L.length?T.translate(0,M.p.v[1]*L[0],-M.p.v[2]*L[1]):T.translate(0,M.p.v[1]*L,-M.p.v[2]*L):L.length?T.translate(M.p.v[0]*L[0],M.p.v[1]*L[1],-M.p.v[2]*L[2]):T.translate(M.p.v[0]*L,M.p.v[1]*L,-M.p.v[2]*L));if(t.strokeWidthAnim&&(q=j<0?0:j),t.strokeColorAnim&&(H="rgb("+Math.round(255*G[0])+","+Math.round(255*G[1])+","+Math.round(255*G[2])+")"),t.fillColorAnim&&t.fc&&(W="rgb("+Math.round(255*J[0])+","+Math.round(255*J[1])+","+Math.round(255*J[2])+")"),this._hasMaskedPath){if(T.translate(0,-t.ls),T.translate(0,x[1]*V/100+i,0),S.p.p){P=(h.point[1]-c.point[1])/(h.point[0]-c.point[0]);var ht=180*Math.atan(P)/Math.PI;h.point[0]<c.point[0]&&(ht+=180),T.rotate(-ht*Math.PI/180)}T.translate(O,B,0),o-=x[0]*E[s].an/200,E[s+1]&&Y!==E[s+1].ind&&(o+=E[s].an/2,o+=t.tr/1e3*t.finalSize)}else{switch(T.translate(r,i,0),t.ps&&T.translate(t.ps[0],t.ps[1]+t.ascent,0),t.j){case 1:T.translate(E[s].animatorJustifyOffset+t.justifyOffset+(t.boxWidth-t.lineWidths[E[s].line]),0,0);break;case 2:T.translate(E[s].animatorJustifyOffset+t.justifyOffset+(t.boxWidth-t.lineWidths[E[s].line])/2,0,0)}T.translate(0,-t.ls),T.translate(z,0,0),T.translate(x[0]*E[s].an/200,x[1]*V/100,0),r+=E[s].l+t.tr/1e3*t.finalSize}"html"===A?tt=T.toCSS():"svg"===A?tt=T.to2dCSS():et=[T.props[0],T.props[1],T.props[2],T.props[3],T.props[4],T.props[5],T.props[6],T.props[7],T.props[8],T.props[9],T.props[10],T.props[11],T.props[12],T.props[13],T.props[14],T.props[15]],X=N}C<=s?(F=new LetterProps(X,q,H,W,tt,et),this.renderedLetters.push(F),C+=1,this.lettersChangedFlag=!0):(F=this.renderedLetters[s],this.lettersChangedFlag=F.update(X,q,H,W,tt,et)||this.lettersChangedFlag)}}},TextAnimatorProperty.prototype.getValue=function(){this._elem.globalData.frameId!==this._frameId&&(this._frameId=this._elem.globalData.frameId,this.iterateDynamicProperties())},TextAnimatorProperty.prototype.mHelper=new Matrix,TextAnimatorProperty.prototype.defaultPropsArray=[],extendPrototype([DynamicPropertyContainer],TextAnimatorProperty),LetterProps.prototype.update=function(t,e,r,i,s,a){this._mdf.o=!1,this._mdf.sw=!1,this._mdf.sc=!1,this._mdf.fc=!1,this._mdf.m=!1;var n=this._mdf.p=!1;return this.o!==t&&(this.o=t,n=this._mdf.o=!0),this.sw!==e&&(this.sw=e,n=this._mdf.sw=!0),this.sc!==r&&(this.sc=r,n=this._mdf.sc=!0),this.fc!==i&&(this.fc=i,n=this._mdf.fc=!0),this.m!==s&&(this.m=s,n=this._mdf.m=!0),!a.length||this.p[0]===a[0]&&this.p[1]===a[1]&&this.p[4]===a[4]&&this.p[5]===a[5]&&this.p[12]===a[12]&&this.p[13]===a[13]||(this.p=a,n=this._mdf.p=!0),n},TextProperty.prototype.defaultBoxWidth=[0,0],TextProperty.prototype.copyData=function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r]);return t},TextProperty.prototype.setCurrentData=function(t){t.__complete||this.completeTextData(t),this.currentData=t,this.currentData.boxWidth=this.currentData.boxWidth||this.defaultBoxWidth,this._mdf=!0},TextProperty.prototype.searchProperty=function(){return this.searchKeyframes()},TextProperty.prototype.searchKeyframes=function(){return this.kf=1<this.data.d.k.length,this.kf&&this.addEffect(this.getKeyframeValue.bind(this)),this.kf},TextProperty.prototype.addEffect=function(t){this.effectsSequence.push(t),this.elem.addDynamicProperty(this)},TextProperty.prototype.getValue=function(t){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length||t){this.currentData.t=this.data.d.k[this.keysIndex].s.t;var e=this.currentData,r=this.keysIndex;if(this.lock)this.setCurrentData(this.currentData);else{this.lock=!0,this._mdf=!1;var i,s=this.effectsSequence.length,a=t||this.data.d.k[this.keysIndex].s;for(i=0;i<s;i+=1)a=r!==this.keysIndex?this.effectsSequence[i](a,a.t):this.effectsSequence[i](this.currentData,a.t);e!==a&&this.setCurrentData(a),this.pv=this.v=this.currentData,this.lock=!1,this.frameId=this.elem.globalData.frameId}}},TextProperty.prototype.getKeyframeValue=function(){for(var t=this.data.d.k,e=this.elem.comp.renderedFrame,r=0,i=t.length;r<=i-1&&(t[r].s,!(r===i-1||t[r+1].t>e));)r+=1;return this.keysIndex!==r&&(this.keysIndex=r),this.data.d.k[this.keysIndex].s},TextProperty.prototype.buildFinalText=function(t){for(var e=FontManager.getCombinedCharacterCodes(),r=[],i=0,s=t.length;i<s;)-1!==e.indexOf(t.charCodeAt(i))?r[r.length-1]+=t.charAt(i):r.push(t.charAt(i)),i+=1;return r},TextProperty.prototype.completeTextData=function(t){t.__complete=!0;var e,r,i,s,a,n,o,h=this.elem.globalData.fontManager,p=this.data,l=[],m=0,f=p.m.g,c=0,d=0,u=0,y=[],g=0,v=0,P=h.getFontByName(t.f),b=0,x=P.fStyle?P.fStyle.split(" "):[],_="normal",S="normal";for(r=x.length,e=0;e<r;e+=1)switch(x[e].toLowerCase()){case"italic":S="italic";break;case"bold":_="700";break;case"black":_="900";break;case"medium":_="500";break;case"regular":case"normal":_="400";break;case"light":case"thin":_="200"}t.fWeight=P.fWeight||_,t.fStyle=S,r=t.t.length,t.finalSize=t.s,t.finalText=this.buildFinalText(t.t),t.finalLineHeight=t.lh;var T,A=t.tr/1e3*t.finalSize;if(t.sz)for(var C,E,k=!0,D=t.sz[0],M=t.sz[1];k;){g=C=0,r=(E=this.buildFinalText(t.t)).length,A=t.tr/1e3*t.finalSize;var I=-1;for(e=0;e<r;e+=1)T=E[e].charCodeAt(0),i=!1," "===E[e]?I=e:13!==T&&3!==T||(i=!(g=0),C+=t.finalLineHeight||1.2*t.finalSize),D<g+(b=h.chars?(o=h.getCharData(E[e],P.fStyle,P.fFamily),i?0:o.w*t.finalSize/100):h.measureText(E[e],t.f,t.finalSize))&&" "!==E[e]?(-1===I?r+=1:e=I,C+=t.finalLineHeight||1.2*t.finalSize,E.splice(e,I===e?1:0,"\r"),I=-1,g=0):(g+=b,g+=A);C+=P.ascent*t.finalSize/100,this.canResize&&t.finalSize>this.minimumFontSize&&M<C?(t.finalSize-=1,t.finalLineHeight=t.finalSize*t.lh/t.s):(t.finalText=E,r=t.finalText.length,k=!1)}g=-A;var w,F=b=0;for(e=0;e<r;e+=1)if(i=!1,T=(w=t.finalText[e]).charCodeAt(0)," "===w?s="\xa0":13===T||3===T?(F=0,y.push(g),v=v<g?g:v,g=-2*A,i=!(s=""),u+=1):s=t.finalText[e],b=h.chars?(o=h.getCharData(w,P.fStyle,h.getFontByName(t.f).fFamily),i?0:o.w*t.finalSize/100):h.measureText(s,t.f,t.finalSize)," "===w?F+=b+A:(g+=b+A+F,F=0),l.push({l:b,an:b,add:c,n:i,anIndexes:[],val:s,line:u,animatorJustifyOffset:0}),2==f){if(c+=b,""===s||"\xa0"===s||e===r-1){for(""!==s&&"\xa0"!==s||(c-=b);d<=e;)l[d].an=c,l[d].ind=m,l[d].extra=b,d+=1;m+=1,c=0}}else if(3==f){if(c+=b,""===s||e===r-1){for(""===s&&(c-=b);d<=e;)l[d].an=c,l[d].ind=m,l[d].extra=b,d+=1;c=0,m+=1}}else l[m].ind=m,l[m].extra=0,m+=1;if(t.l=l,v=v<g?g:v,y.push(g),t.sz)t.boxWidth=t.sz[0],t.justifyOffset=0;else switch(t.boxWidth=v,t.j){case 1:t.justifyOffset=-t.boxWidth;break;case 2:t.justifyOffset=-t.boxWidth/2;break;default:t.justifyOffset=0}t.lineWidths=y;var V,R,L=p.a;n=L.length;var z,O,B=[];for(a=0;a<n;a+=1){for((V=L[a]).a.sc&&(t.strokeColorAnim=!0),V.a.sw&&(t.strokeWidthAnim=!0),(V.a.fc||V.a.fh||V.a.fs||V.a.fb)&&(t.fillColorAnim=!0),O=0,z=V.s.b,e=0;e<r;e+=1)(R=l[e]).anIndexes[a]=O,(1==z&&""!==R.val||2==z&&""!==R.val&&"\xa0"!==R.val||3==z&&(R.n||"\xa0"==R.val||e==r-1)||4==z&&(R.n||e==r-1))&&(1===V.s.rn&&B.push(O),O+=1);p.a[a].s.totalChars=O;var N,G=-1;if(1===V.s.rn)for(e=0;e<r;e+=1)G!=(R=l[e]).anIndexes[a]&&(G=R.anIndexes[a],N=B.splice(Math.floor(Math.random()*B.length),1)[0]),R.anIndexes[a]=N}t.yOffset=t.finalLineHeight||1.2*t.finalSize,t.ls=t.ls||0,t.ascent=P.ascent*t.finalSize/100},TextProperty.prototype.updateDocumentData=function(t,e){e=void 0===e?this.keysIndex:e;var r=this.copyData({},this.data.d.k[e].s);r=this.copyData(r,t),this.data.d.k[e].s=r,this.recalculate(e),this.elem.addDynamicProperty(this)},TextProperty.prototype.recalculate=function(t){var e=this.data.d.k[t].s;e.__complete=!1,this.keysIndex=0,this._isFirstFrame=!0,this.getValue(e)},TextProperty.prototype.canResizeFont=function(t){this.canResize=t,this.recalculate(this.keysIndex),this.elem.addDynamicProperty(this)},TextProperty.prototype.setMinimumFontSize=function(t){this.minimumFontSize=Math.floor(t)||1,this.recalculate(this.keysIndex),this.elem.addDynamicProperty(this)};var TextSelectorProp=(cz=Math.max,dz=Math.min,ez=Math.floor,fz.prototype={getMult:function(t){this._currentTextLength!==this.elem.textProperty.currentData.l.length&&this.getValue();var e=BezierFactory.getBezierEasing(this.ne.v/100,0,1-this.xe.v/100,1).get,r=0,i=this.finalS,s=this.finalE,a=this.data.sh;if(2==a)r=e(r=s===i?s<=t?1:0:cz(0,dz(.5/(s-i)+(t-i)/(s-i),1)));else if(3==a)r=e(r=s===i?s<=t?0:1:1-cz(0,dz(.5/(s-i)+(t-i)/(s-i),1)));else if(4==a)s===i?r=0:(r=cz(0,dz(.5/(s-i)+(t-i)/(s-i),1)))<.5?r*=2:r=1-2*(r-.5),r=e(r);else if(5==a){if(s===i)r=0;else{var n=s-i,o=-n/2+(t=dz(cz(0,t+.5-i),s-i)),h=n/2;r=Math.sqrt(1-o*o/(h*h))}r=e(r)}else r=6==a?e(r=s===i?0:(t=dz(cz(0,t+.5-i),s-i),(1+Math.cos(Math.PI+2*Math.PI*t/(s-i)))/2)):(t>=ez(i)&&(r=t-i<0?1-(i-t):cz(0,dz(s-t,1))),e(r));return r*this.a.v},getValue:function(t){this.iterateDynamicProperties(),this._mdf=t||this._mdf,this._currentTextLength=this.elem.textProperty.currentData.l.length||0,t&&2===this.data.r&&(this.e.v=this._currentTextLength);var e=2===this.data.r?1:100/this.data.totalChars,r=this.o.v/e,i=this.s.v/e+r,s=this.e.v/e+r;if(s<i){var a=i;i=s,s=a}this.finalS=i,this.finalE=s}},extendPrototype([DynamicPropertyContainer],fz),{getTextSelectorProp:function(t,e,r){return new fz(t,e,r)}}),cz,dz,ez;function fz(t,e){this._currentTextLength=-1,this.k=!1,this.data=e,this.elem=t,this.comp=t.comp,this.finalS=0,this.finalE=0,this.initDynamicPropertyContainer(t),this.s=PropertyFactory.getProp(t,e.s||{k:0},0,0,this),this.e="e"in e?PropertyFactory.getProp(t,e.e,0,0,this):{v:100},this.o=PropertyFactory.getProp(t,e.o||{k:0},0,0,this),this.xe=PropertyFactory.getProp(t,e.xe||{k:0},0,0,this),this.ne=PropertyFactory.getProp(t,e.ne||{k:0},0,0,this),this.a=PropertyFactory.getProp(t,e.a,0,.01,this),this.dynamicProperties.length||this.getValue()}var pool_factory=function(t,e,r,i){var s=0,a=t,n=createSizedArray(a);function o(){return s?n[s-=1]:e()}return{newElement:o,release:function(t){s===a&&(n=pooling.double(n),a*=2),r&&r(t),n[s]=t,s+=1}}},pooling={double:function(t){return t.concat(createSizedArray(t.length))}},point_pool=pool_factory(8,function(){return createTypedArray("float32",2)}),shape_pool=(Vz=pool_factory(4,function(){return new ShapePath},function(t){var e,r=t._length;for(e=0;e<r;e+=1)point_pool.release(t.v[e]),point_pool.release(t.i[e]),point_pool.release(t.o[e]),t.v[e]=null,t.i[e]=null,t.o[e]=null;t._length=0,t.c=!1}),Vz.clone=function(t){var e,r=Vz.newElement(),i=void 0===t._length?t.v.length:t._length;for(r.setLength(i),r.c=t.c,e=0;e<i;e+=1)r.setTripleAt(t.v[e][0],t.v[e][1],t.o[e][0],t.o[e][1],t.i[e][0],t.i[e][1],e);return r},Vz),Vz,shapeCollection_pool=(cA={newShapeCollection:function(){var t;t=dA?fA[dA-=1]:new ShapeCollection;return t},release:function(t){var e,r=t._length;for(e=0;e<r;e+=1)shape_pool.release(t.shapes[e]);t._length=0,dA===eA&&(fA=pooling.double(fA),eA*=2);fA[dA]=t,dA+=1}},dA=0,eA=4,fA=createSizedArray(eA),cA),cA,dA,eA,fA,segments_length_pool=pool_factory(8,function(){return{lengths:[],totalLength:0}},function(t){var e,r=t.lengths.length;for(e=0;e<r;e+=1)bezier_length_pool.release(t.lengths[e]);t.lengths.length=0}),bezier_length_pool=pool_factory(8,function(){return{addedLength:0,percents:createTypedArray("float32",defaultCurveSegments),lengths:createTypedArray("float32",defaultCurveSegments)}});function BaseRenderer(){}function SVGRenderer(t,e){this.animationItem=t,this.layers=null,this.renderedFrame=-1,this.svgElement=createNS("svg");var r="";if(e&&e.title){var i=createNS("title"),s=createElementID();i.setAttribute("id",s),i.textContent=e.title,this.svgElement.appendChild(i),r+=s}if(e&&e.description){var a=createNS("desc"),n=createElementID();a.setAttribute("id",n),a.textContent=e.description,this.svgElement.appendChild(a),r+=" "+n}r&&this.svgElement.setAttribute("aria-labelledby",r);var o=createNS("defs");this.svgElement.appendChild(o);var h=createNS("g");this.svgElement.appendChild(h),this.layerElement=h,this.renderConfig={preserveAspectRatio:e&&e.preserveAspectRatio||"xMidYMid meet",imagePreserveAspectRatio:e&&e.imagePreserveAspectRatio||"xMidYMid slice",progressiveLoad:e&&e.progressiveLoad||!1,hideOnTransparent:!e||!1!==e.hideOnTransparent,viewBoxOnly:e&&e.viewBoxOnly||!1,viewBoxSize:e&&e.viewBoxSize||!1,className:e&&e.className||""},this.globalData={_mdf:!1,frameNum:-1,defs:o,renderConfig:this.renderConfig},this.elements=[],this.pendingElements=[],this.destroyed=!1,this.rendererType="svg"}function CanvasRenderer(t,e){this.animationItem=t,this.renderConfig={clearCanvas:!e||void 0===e.clearCanvas||e.clearCanvas,context:e&&e.context||null,progressiveLoad:e&&e.progressiveLoad||!1,preserveAspectRatio:e&&e.preserveAspectRatio||"xMidYMid meet",imagePreserveAspectRatio:e&&e.imagePreserveAspectRatio||"xMidYMid slice",className:e&&e.className||""},this.renderConfig.dpr=e&&e.dpr||1,this.animationItem.wrapper&&(this.renderConfig.dpr=e&&e.dpr||window.devicePixelRatio||1),this.renderedFrame=-1,this.globalData={frameNum:-1,_mdf:!1,renderConfig:this.renderConfig,currentGlobalAlpha:-1},this.contextData=new CVContextData,this.elements=[],this.pendingElements=[],this.transformMat=new Matrix,this.completeLayers=!1,this.rendererType="canvas"}function MaskElement(t,e,r){this.data=t,this.element=e,this.globalData=r,this.storedData=[],this.masksProperties=this.data.masksProperties||[],this.maskElement=null;var i,s=this.globalData.defs,a=this.masksProperties?this.masksProperties.length:0;this.viewData=createSizedArray(a),this.solidPath="";var n,o,h,p,l,m,f,c=this.masksProperties,d=0,u=[],y=createElementID(),g="clipPath",v="clip-path";for(i=0;i<a;i++)if(("a"!==c[i].mode&&"n"!==c[i].mode||c[i].inv||100!==c[i].o.k)&&(v=g="mask"),"s"!=c[i].mode&&"i"!=c[i].mode||0!==d?p=null:((p=createNS("rect")).setAttribute("fill","#ffffff"),p.setAttribute("width",this.element.comp.data.w||0),p.setAttribute("height",this.element.comp.data.h||0),u.push(p)),n=createNS("path"),"n"!=c[i].mode){var P;if(d+=1,n.setAttribute("fill","s"===c[i].mode?"#000000":"#ffffff"),n.setAttribute("clip-rule","nonzero"),0!==c[i].x.k?(v=g="mask",f=PropertyFactory.getProp(this.element,c[i].x,0,null,this.element),P=createElementID(),(l=createNS("filter")).setAttribute("id",P),(m=createNS("feMorphology")).setAttribute("operator","erode"),m.setAttribute("in","SourceGraphic"),m.setAttribute("radius","0"),l.appendChild(m),s.appendChild(l),n.setAttribute("stroke","s"===c[i].mode?"#000000":"#ffffff")):f=m=null,this.storedData[i]={elem:n,x:f,expan:m,lastPath:"",lastOperator:"",filterId:P,lastRadius:0},"i"==c[i].mode){h=u.length;var b=createNS("g");for(o=0;o<h;o+=1)b.appendChild(u[o]);var x=createNS("mask");x.setAttribute("mask-type","alpha"),x.setAttribute("id",y+"_"+d),x.appendChild(n),s.appendChild(x),b.setAttribute("mask","url("+locationHref+"#"+y+"_"+d+")"),u.length=0,u.push(b)}else u.push(n);c[i].inv&&!this.solidPath&&(this.solidPath=this.createLayerSolidPath()),this.viewData[i]={elem:n,lastPath:"",op:PropertyFactory.getProp(this.element,c[i].o,0,.01,this.element),prop:ShapePropertyFactory.getShapeProp(this.element,c[i],3),invRect:p},this.viewData[i].prop.k||this.drawPath(c[i],this.viewData[i].prop.v,this.viewData[i])}else this.viewData[i]={op:PropertyFactory.getProp(this.element,c[i].o,0,.01,this.element),prop:ShapePropertyFactory.getShapeProp(this.element,c[i],3),elem:n,lastPath:""},s.appendChild(n);for(this.maskElement=createNS(g),a=u.length,i=0;i<a;i+=1)this.maskElement.appendChild(u[i]);0<d&&(this.maskElement.setAttribute("id",y),this.element.maskedElement.setAttribute(v,"url("+locationHref+"#"+y+")"),s.appendChild(this.maskElement)),this.viewData.length&&this.element.addRenderableComponent(this)}function HierarchyElement(){}function FrameElement(){}function TransformElement(){}function RenderableElement(){}function RenderableDOMElement(){}function ProcessedElement(t,e){this.elem=t,this.pos=e}function SVGShapeData(t,e,r){this.caches=[],this.styles=[],this.transformers=t,this.lStr="",this.sh=r,this.lvl=e,this._isAnimated=!!r.k;for(var i=0,s=t.length;i<s;){if(t[i].mProps.dynamicProperties.length){this._isAnimated=!0;break}i+=1}}function ShapeGroupData(){this.it=[],this.prevViewData=[],this.gr=createNS("g")}function ShapeTransformManager(){this.sequences={},this.sequenceList=[],this.transform_key_count=0}function CVShapeData(t,e,r,i){this.styledShapes=[],this.tr=[0,0,0,0,0,0];var s=4;"rc"==e.ty?s=5:"el"==e.ty?s=6:"sr"==e.ty&&(s=7),this.sh=ShapePropertyFactory.getShapeProp(t,e,s,t);var a,n,o=r.length;for(a=0;a<o;a+=1)r[a].closed||(n={transforms:i.addTransformSequence(r[a].transforms),trNodes:[]},this.styledShapes.push(n),r[a].elements.push(n))}function BaseElement(){}function NullElement(t,e,r){this.initFrame(),this.initBaseData(t,e,r),this.initFrame(),this.initTransform(t,e,r),this.initHierarchy()}function SVGBaseElement(){}function IShapeElement(){}function ITextElement(){}function ICompElement(){}function IImageElement(t,e,r){this.assetData=e.getAssetData(t.refId),this.initElement(t,e,r),this.sourceRect={top:0,left:0,width:this.assetData.w,height:this.assetData.h}}function ISolidElement(t,e,r){this.initElement(t,e,r)}function SVGShapeElement(t,e,r){this.shapes=[],this.shapesData=t.shapes,this.stylesList=[],this.shapeModifiers=[],this.itemsData=[],this.processedElements=[],this.animatedContents=[],this.initElement(t,e,r),this.prevViewData=[]}function CVContextData(){this.saved=[],this.cArrPos=0,this.cTr=new Matrix,this.cO=1;var t;for(this.savedOp=createTypedArray("float32",15),t=0;t<15;t+=1)this.saved[t]=createTypedArray("float32",16);this._length=15}function CVBaseElement(){}function CVCompElement(t,e,r){this.completeLayers=!1,this.layers=t.layers,this.pendingElements=[],this.elements=createSizedArray(this.layers.length),this.initElement(t,e,r),this.tm=t.tm?PropertyFactory.getProp(this,t.tm,0,e.frameRate,this):{_placeholder:!0}}function CVMaskElement(t,e){this.data=t,this.element=e,this.masksProperties=this.data.masksProperties||[],this.viewData=createSizedArray(this.masksProperties.length);var r,i=this.masksProperties.length,s=!1;for(r=0;r<i;r++)"n"!==this.masksProperties[r].mode&&(s=!0),this.viewData[r]=ShapePropertyFactory.getShapeProp(this.element,this.masksProperties[r],3);(this.hasMasks=s)&&this.element.addRenderableComponent(this)}function CVShapeElement(t,e,r){this.shapes=[],this.shapesData=t.shapes,this.stylesList=[],this.itemsData=[],this.prevViewData=[],this.shapeModifiers=[],this.processedElements=[],this.transformsManager=new ShapeTransformManager,this.initElement(t,e,r)}function CVSolidElement(t,e,r){this.initElement(t,e,r)}function CVEffects(){}BaseRenderer.prototype.checkLayers=function(t){var e,r,i=this.layers.length;for(this.completeLayers=!0,e=i-1;0<=e;e--)this.elements[e]||(r=this.layers[e]).ip-r.st<=t-this.layers[e].st&&r.op-r.st>t-this.layers[e].st&&this.buildItem(e),this.completeLayers=!!this.elements[e]&&this.completeLayers;this.checkPendingElements()},BaseRenderer.prototype.createItem=function(t){switch(t.ty){case 2:return this.createImage(t);case 0:return this.createComp(t);case 1:return this.createSolid(t);case 3:return this.createNull(t);case 4:return this.createShape(t);case 5:return this.createText(t);case 13:return this.createCamera(t)}return this.createNull(t)},BaseRenderer.prototype.createCamera=function(){throw new Error("You're using a 3d camera. Try the html renderer.")},BaseRenderer.prototype.buildAllItems=function(){var t,e=this.layers.length;for(t=0;t<e;t+=1)this.buildItem(t);this.checkPendingElements()},BaseRenderer.prototype.includeLayers=function(t){this.completeLayers=!1;var e,r,i=t.length,s=this.layers.length;for(e=0;e<i;e+=1)for(r=0;r<s;){if(this.layers[r].id==t[e].id){this.layers[r]=t[e];break}r+=1}},BaseRenderer.prototype.setProjectInterface=function(t){this.globalData.projectInterface=t},BaseRenderer.prototype.initItems=function(){this.globalData.progressiveLoad||this.buildAllItems()},BaseRenderer.prototype.buildElementParenting=function(t,e,r){for(var i=this.elements,s=this.layers,a=0,n=s.length;a<n;)s[a].ind==e&&(i[a]&&!0!==i[a]?(r.push(i[a]),i[a].setAsParent(),void 0!==s[a].parent?this.buildElementParenting(t,s[a].parent,r):t.setHierarchy(r)):(this.buildItem(a),this.addPendingElement(t))),a+=1},BaseRenderer.prototype.addPendingElement=function(t){this.pendingElements.push(t)},BaseRenderer.prototype.searchExtraCompositions=function(t){var e,r=t.length;for(e=0;e<r;e+=1)if(t[e].xt){var i=this.createComp(t[e]);i.initExpressions(),this.globalData.projectInterface.registerComposition(i)}},BaseRenderer.prototype.setupGlobalData=function(t,e){this.globalData.fontManager=new FontManager,this.globalData.fontManager.addChars(t.chars),this.globalData.fontManager.addFonts(t.fonts,e),this.globalData.getAssetData=this.animationItem.getAssetData.bind(this.animationItem),this.globalData.getAssetsPath=this.animationItem.getAssetsPath.bind(this.animationItem),this.globalData.imageLoader=this.animationItem.imagePreloader,this.globalData.frameId=0,this.globalData.frameRate=t.fr,this.globalData.nm=t.nm,this.globalData.compSize={w:t.w,h:t.h}},extendPrototype([BaseRenderer],SVGRenderer),SVGRenderer.prototype.createNull=function(t){return new NullElement(t,this.globalData,this)},SVGRenderer.prototype.createShape=function(t){return new SVGShapeElement(t,this.globalData,this)},SVGRenderer.prototype.createText=function(t){return new SVGTextElement(t,this.globalData,this)},SVGRenderer.prototype.createImage=function(t){return new IImageElement(t,this.globalData,this)},SVGRenderer.prototype.createComp=function(t){return new SVGCompElement(t,this.globalData,this)},SVGRenderer.prototype.createSolid=function(t){return new ISolidElement(t,this.globalData,this)},SVGRenderer.prototype.configAnimation=function(t){this.svgElement.setAttribute("xmlns","http://www.w3.org/2000/svg"),this.renderConfig.viewBoxSize?this.svgElement.setAttribute("viewBox",this.renderConfig.viewBoxSize):this.svgElement.setAttribute("viewBox","0 0 "+t.w+" "+t.h),this.renderConfig.viewBoxOnly||(this.svgElement.setAttribute("width",t.w),this.svgElement.setAttribute("height",t.h),this.svgElement.style.width="100%",this.svgElement.style.height="100%",this.svgElement.style.transform="translate3d(0,0,0)"),this.renderConfig.className&&this.svgElement.setAttribute("class",this.renderConfig.className),this.svgElement.setAttribute("preserveAspectRatio",this.renderConfig.preserveAspectRatio),this.animationItem.wrapper.appendChild(this.svgElement);var e=this.globalData.defs;this.setupGlobalData(t,e),this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.data=t;var r=createNS("clipPath"),i=createNS("rect");i.setAttribute("width",t.w),i.setAttribute("height",t.h),i.setAttribute("x",0),i.setAttribute("y",0);var s=createElementID();r.setAttribute("id",s),r.appendChild(i),this.layerElement.setAttribute("clip-path","url("+locationHref+"#"+s+")"),e.appendChild(r),this.layers=t.layers,this.elements=createSizedArray(t.layers.length)},SVGRenderer.prototype.destroy=function(){this.animationItem.wrapper.innerHTML="",this.layerElement=null,this.globalData.defs=null;var t,e=this.layers?this.layers.length:0;for(t=0;t<e;t++)this.elements[t]&&this.elements[t].destroy();this.elements.length=0,this.destroyed=!0,this.animationItem=null},SVGRenderer.prototype.updateContainerSize=function(){},SVGRenderer.prototype.buildItem=function(t){var e=this.elements;if(!e[t]&&99!=this.layers[t].ty){e[t]=!0;var r=this.createItem(this.layers[t]);e[t]=r,expressionsPlugin&&(0===this.layers[t].ty&&this.globalData.projectInterface.registerComposition(r),r.initExpressions()),this.appendElementInPos(r,t),this.layers[t].tt&&(this.elements[t-1]&&!0!==this.elements[t-1]?r.setMatte(e[t-1].layerId):(this.buildItem(t-1),this.addPendingElement(r)))}},SVGRenderer.prototype.checkPendingElements=function(){for(;this.pendingElements.length;){var t=this.pendingElements.pop();if(t.checkParenting(),t.data.tt)for(var e=0,r=this.elements.length;e<r;){if(this.elements[e]===t){t.setMatte(this.elements[e-1].layerId);break}e+=1}}},SVGRenderer.prototype.renderFrame=function(t){if(this.renderedFrame!==t&&!this.destroyed){null===t?t=this.renderedFrame:this.renderedFrame=t,this.globalData.frameNum=t,this.globalData.frameId+=1,this.globalData.projectInterface.currentFrame=t,this.globalData._mdf=!1;var e,r=this.layers.length;for(this.completeLayers||this.checkLayers(t),e=r-1;0<=e;e--)(this.completeLayers||this.elements[e])&&this.elements[e].prepareFrame(t-this.layers[e].st);if(this.globalData._mdf)for(e=0;e<r;e+=1)(this.completeLayers||this.elements[e])&&this.elements[e].renderFrame()}},SVGRenderer.prototype.appendElementInPos=function(t,e){var r=t.getBaseElement();if(r){for(var i,s=0;s<e;)this.elements[s]&&!0!==this.elements[s]&&this.elements[s].getBaseElement()&&(i=this.elements[s].getBaseElement()),s+=1;i?this.layerElement.insertBefore(r,i):this.layerElement.appendChild(r)}},SVGRenderer.prototype.hide=function(){this.layerElement.style.display="none"},SVGRenderer.prototype.show=function(){this.layerElement.style.display="block"},extendPrototype([BaseRenderer],CanvasRenderer),CanvasRenderer.prototype.createShape=function(t){return new CVShapeElement(t,this.globalData,this)},CanvasRenderer.prototype.createText=function(t){return new CVTextElement(t,this.globalData,this)},CanvasRenderer.prototype.createImage=function(t){return new CVImageElement(t,this.globalData,this)},CanvasRenderer.prototype.createComp=function(t){return new CVCompElement(t,this.globalData,this)},CanvasRenderer.prototype.createSolid=function(t){return new CVSolidElement(t,this.globalData,this)},CanvasRenderer.prototype.createNull=SVGRenderer.prototype.createNull,CanvasRenderer.prototype.ctxTransform=function(t){if(1!==t[0]||0!==t[1]||0!==t[4]||1!==t[5]||0!==t[12]||0!==t[13])if(this.renderConfig.clearCanvas){this.transformMat.cloneFromProps(t);var e=this.contextData.cTr.props;this.transformMat.transform(e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15]),this.contextData.cTr.cloneFromProps(this.transformMat.props);var r=this.contextData.cTr.props;this.canvasContext.setTransform(r[0],r[1],r[4],r[5],r[12],r[13])}else this.canvasContext.transform(t[0],t[1],t[4],t[5],t[12],t[13])},CanvasRenderer.prototype.ctxOpacity=function(t){if(!this.renderConfig.clearCanvas)return this.canvasContext.globalAlpha*=t<0?0:t,void(this.globalData.currentGlobalAlpha=this.contextData.cO);this.contextData.cO*=t<0?0:t,this.globalData.currentGlobalAlpha!==this.contextData.cO&&(this.canvasContext.globalAlpha=this.contextData.cO,this.globalData.currentGlobalAlpha=this.contextData.cO)},CanvasRenderer.prototype.reset=function(){this.renderConfig.clearCanvas?this.contextData.reset():this.canvasContext.restore()},CanvasRenderer.prototype.save=function(t){if(this.renderConfig.clearCanvas){t&&this.canvasContext.save();var e=this.contextData.cTr.props;this.contextData._length<=this.contextData.cArrPos&&this.contextData.duplicate();var r,i=this.contextData.saved[this.contextData.cArrPos];for(r=0;r<16;r+=1)i[r]=e[r];this.contextData.savedOp[this.contextData.cArrPos]=this.contextData.cO,this.contextData.cArrPos+=1}else this.canvasContext.save()},CanvasRenderer.prototype.restore=function(t){if(this.renderConfig.clearCanvas){t&&(this.canvasContext.restore(),this.globalData.blendMode="source-over"),this.contextData.cArrPos-=1;var e,r=this.contextData.saved[this.contextData.cArrPos],i=this.contextData.cTr.props;for(e=0;e<16;e+=1)i[e]=r[e];this.canvasContext.setTransform(r[0],r[1],r[4],r[5],r[12],r[13]),r=this.contextData.savedOp[this.contextData.cArrPos],this.contextData.cO=r,this.globalData.currentGlobalAlpha!==r&&(this.canvasContext.globalAlpha=r,this.globalData.currentGlobalAlpha=r)}else this.canvasContext.restore()},CanvasRenderer.prototype.configAnimation=function(t){this.animationItem.wrapper?(this.animationItem.container=createTag("canvas"),this.animationItem.container.style.width="100%",this.animationItem.container.style.height="100%",this.animationItem.container.style.transformOrigin=this.animationItem.container.style.mozTransformOrigin=this.animationItem.container.style.webkitTransformOrigin=this.animationItem.container.style["-webkit-transform"]="0px 0px 0px",this.animationItem.wrapper.appendChild(this.animationItem.container),this.canvasContext=this.animationItem.container.getContext("2d"),this.renderConfig.className&&this.animationItem.container.setAttribute("class",this.renderConfig.className)):this.canvasContext=this.renderConfig.context,this.data=t,this.layers=t.layers,this.transformCanvas={w:t.w,h:t.h,sx:0,sy:0,tx:0,ty:0},this.setupGlobalData(t,document.body),this.globalData.canvasContext=this.canvasContext,(this.globalData.renderer=this).globalData.isDashed=!1,this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.globalData.transformCanvas=this.transformCanvas,this.elements=createSizedArray(t.layers.length),this.updateContainerSize()},CanvasRenderer.prototype.updateContainerSize=function(){var t,e,r,i;if(this.reset(),this.animationItem.wrapper&&this.animationItem.container?(t=this.animationItem.wrapper.offsetWidth,e=this.animationItem.wrapper.offsetHeight,this.animationItem.container.setAttribute("width",t*this.renderConfig.dpr),this.animationItem.container.setAttribute("height",e*this.renderConfig.dpr)):(t=this.canvasContext.canvas.width*this.renderConfig.dpr,e=this.canvasContext.canvas.height*this.renderConfig.dpr),-1!==this.renderConfig.preserveAspectRatio.indexOf("meet")||-1!==this.renderConfig.preserveAspectRatio.indexOf("slice")){var s=this.renderConfig.preserveAspectRatio.split(" "),a=s[1]||"meet",n=s[0]||"xMidYMid",o=n.substr(0,4),h=n.substr(4);(r=t/e)<(i=this.transformCanvas.w/this.transformCanvas.h)&&"meet"===a||i<r&&"slice"===a?(this.transformCanvas.sx=t/(this.transformCanvas.w/this.renderConfig.dpr),this.transformCanvas.sy=t/(this.transformCanvas.w/this.renderConfig.dpr)):(this.transformCanvas.sx=e/(this.transformCanvas.h/this.renderConfig.dpr),this.transformCanvas.sy=e/(this.transformCanvas.h/this.renderConfig.dpr)),this.transformCanvas.tx="xMid"===o&&(i<r&&"meet"===a||r<i&&"slice"===a)?(t-this.transformCanvas.w*(e/this.transformCanvas.h))/2*this.renderConfig.dpr:"xMax"===o&&(i<r&&"meet"===a||r<i&&"slice"===a)?(t-this.transformCanvas.w*(e/this.transformCanvas.h))*this.renderConfig.dpr:0,this.transformCanvas.ty="YMid"===h&&(r<i&&"meet"===a||i<r&&"slice"===a)?(e-this.transformCanvas.h*(t/this.transformCanvas.w))/2*this.renderConfig.dpr:"YMax"===h&&(r<i&&"meet"===a||i<r&&"slice"===a)?(e-this.transformCanvas.h*(t/this.transformCanvas.w))*this.renderConfig.dpr:0}else"none"==this.renderConfig.preserveAspectRatio?(this.transformCanvas.sx=t/(this.transformCanvas.w/this.renderConfig.dpr),this.transformCanvas.sy=e/(this.transformCanvas.h/this.renderConfig.dpr)):(this.transformCanvas.sx=this.renderConfig.dpr,this.transformCanvas.sy=this.renderConfig.dpr),this.transformCanvas.tx=0,this.transformCanvas.ty=0;this.transformCanvas.props=[this.transformCanvas.sx,0,0,0,0,this.transformCanvas.sy,0,0,0,0,1,0,this.transformCanvas.tx,this.transformCanvas.ty,0,1],this.ctxTransform(this.transformCanvas.props),this.canvasContext.beginPath(),this.canvasContext.rect(0,0,this.transformCanvas.w,this.transformCanvas.h),this.canvasContext.closePath(),this.canvasContext.clip(),this.renderFrame(this.renderedFrame,!0)},CanvasRenderer.prototype.destroy=function(){var t;for(this.renderConfig.clearCanvas&&(this.animationItem.wrapper.innerHTML=""),t=(this.layers?this.layers.length:0)-1;0<=t;t-=1)this.elements[t]&&this.elements[t].destroy();this.elements.length=0,this.globalData.canvasContext=null,this.animationItem.container=null,this.destroyed=!0},CanvasRenderer.prototype.renderFrame=function(t,e){if((this.renderedFrame!==t||!0!==this.renderConfig.clearCanvas||e)&&!this.destroyed&&-1!==t){this.renderedFrame=t,this.globalData.frameNum=t-this.animationItem._isFirstFrame,this.globalData.frameId+=1,this.globalData._mdf=!this.renderConfig.clearCanvas||e,this.globalData.projectInterface.currentFrame=t;var r,i=this.layers.length;for(this.completeLayers||this.checkLayers(t),r=0;r<i;r++)(this.completeLayers||this.elements[r])&&this.elements[r].prepareFrame(t-this.layers[r].st);if(this.globalData._mdf){for(!0===this.renderConfig.clearCanvas?this.canvasContext.clearRect(0,0,this.transformCanvas.w,this.transformCanvas.h):this.save(),r=i-1;0<=r;r-=1)(this.completeLayers||this.elements[r])&&this.elements[r].renderFrame();!0!==this.renderConfig.clearCanvas&&this.restore()}}},CanvasRenderer.prototype.buildItem=function(t){var e=this.elements;if(!e[t]&&99!=this.layers[t].ty){var r=this.createItem(this.layers[t],this,this.globalData);(e[t]=r).initExpressions()}},CanvasRenderer.prototype.checkPendingElements=function(){for(;this.pendingElements.length;){this.pendingElements.pop().checkParenting()}},CanvasRenderer.prototype.hide=function(){this.animationItem.container.style.display="none"},CanvasRenderer.prototype.show=function(){this.animationItem.container.style.display="block"},CanvasRenderer.prototype.configAnimation=function(t){this.animationItem.wrapper?(this.animationItem.container=createTag("canvas"),this.animationItem.container.style.width="100%",this.animationItem.container.style.height="100%",this.animationItem.container.style.transformOrigin=this.animationItem.container.style.mozTransformOrigin=this.animationItem.container.style.webkitTransformOrigin=this.animationItem.container.style["-webkit-transform"]="0px 0px 0px",this.animationItem.wrapper.appendChild(this.animationItem.container),this.canvasContext=this.animationItem.container.getContext("2d"),this.renderConfig.className&&this.animationItem.container.setAttribute("class",this.renderConfig.className)):this.canvasContext=this.renderConfig.context,this.data=t,this.layers=t.layers,this.transformCanvas={w:t.w,h:t.h,sx:0,sy:0,tx:0,ty:0},this.globalData.frameId=0,this.globalData.frameRate=t.fr,this.globalData.nm=t.nm,this.globalData.compSize={w:t.w,h:t.h},this.globalData.canvasContext=this.canvasContext,(this.globalData.renderer=this).globalData.isDashed=!1,this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.globalData.transformCanvas=this.transformCanvas,this.elements=createSizedArray(t.layers.length),this.updateContainerSize()},MaskElement.prototype.getMaskProperty=function(t){return this.viewData[t].prop},MaskElement.prototype.renderFrame=function(t){var e,r=this.element.finalTransform.mat,i=this.masksProperties.length;for(e=0;e<i;e++)if((this.viewData[e].prop._mdf||t)&&this.drawPath(this.masksProperties[e],this.viewData[e].prop.v,this.viewData[e]),(this.viewData[e].op._mdf||t)&&this.viewData[e].elem.setAttribute("fill-opacity",this.viewData[e].op.v),"n"!==this.masksProperties[e].mode&&(this.viewData[e].invRect&&(this.element.finalTransform.mProp._mdf||t)&&(this.viewData[e].invRect.setAttribute("x",-r.props[12]),this.viewData[e].invRect.setAttribute("y",-r.props[13])),this.storedData[e].x&&(this.storedData[e].x._mdf||t))){var s=this.storedData[e].expan;this.storedData[e].x.v<0?("erode"!==this.storedData[e].lastOperator&&(this.storedData[e].lastOperator="erode",this.storedData[e].elem.setAttribute("filter","url("+locationHref+"#"+this.storedData[e].filterId+")")),s.setAttribute("radius",-this.storedData[e].x.v)):("dilate"!==this.storedData[e].lastOperator&&(this.storedData[e].lastOperator="dilate",this.storedData[e].elem.setAttribute("filter",null)),this.storedData[e].elem.setAttribute("stroke-width",2*this.storedData[e].x.v))}},MaskElement.prototype.getMaskelement=function(){return this.maskElement},MaskElement.prototype.createLayerSolidPath=function(){var t="M0,0 ";return t+=" h"+this.globalData.compSize.w,t+=" v"+this.globalData.compSize.h,t+=" h-"+this.globalData.compSize.w,t+=" v-"+this.globalData.compSize.h+" "},MaskElement.prototype.drawPath=function(t,e,r){var i,s,a=" M"+e.v[0][0]+","+e.v[0][1];for(s=e._length,i=1;i<s;i+=1)a+=" C"+e.o[i-1][0]+","+e.o[i-1][1]+" "+e.i[i][0]+","+e.i[i][1]+" "+e.v[i][0]+","+e.v[i][1];if(e.c&&1<s&&(a+=" C"+e.o[i-1][0]+","+e.o[i-1][1]+" "+e.i[0][0]+","+e.i[0][1]+" "+e.v[0][0]+","+e.v[0][1]),r.lastPath!==a){var n="";r.elem&&(e.c&&(n=t.inv?this.solidPath+a:a),r.elem.setAttribute("d",n)),r.lastPath=a}},MaskElement.prototype.destroy=function(){this.element=null,this.globalData=null,this.maskElement=null,this.data=null,this.masksProperties=null},HierarchyElement.prototype={initHierarchy:function(){this.hierarchy=[],this._isParent=!1,this.checkParenting()},setHierarchy:function(t){this.hierarchy=t},setAsParent:function(){this._isParent=!0},checkParenting:function(){void 0!==this.data.parent&&this.comp.buildElementParenting(this,this.data.parent,[])}},FrameElement.prototype={initFrame:function(){this._isFirstFrame=!1,this.dynamicProperties=[],this._mdf=!1},prepareProperties:function(t,e){var r,i=this.dynamicProperties.length;for(r=0;r<i;r+=1)(e||this._isParent&&"transform"===this.dynamicProperties[r].propType)&&(this.dynamicProperties[r].getValue(),this.dynamicProperties[r]._mdf&&(this.globalData._mdf=!0,this._mdf=!0))},addDynamicProperty:function(t){-1===this.dynamicProperties.indexOf(t)&&this.dynamicProperties.push(t)}},TransformElement.prototype={initTransform:function(){this.finalTransform={mProp:this.data.ks?TransformPropertyFactory.getTransformProperty(this,this.data.ks,this):{o:0},_matMdf:!1,_opMdf:!1,mat:new Matrix},this.data.ao&&(this.finalTransform.mProp.autoOriented=!0),this.data.ty},renderTransform:function(){if(this.finalTransform._opMdf=this.finalTransform.mProp.o._mdf||this._isFirstFrame,this.finalTransform._matMdf=this.finalTransform.mProp._mdf||this._isFirstFrame,this.hierarchy){var t,e=this.finalTransform.mat,r=0,i=this.hierarchy.length;if(!this.finalTransform._matMdf)for(;r<i;){if(this.hierarchy[r].finalTransform.mProp._mdf){this.finalTransform._matMdf=!0;break}r+=1}if(this.finalTransform._matMdf)for(t=this.finalTransform.mProp.v.props,e.cloneFromProps(t),r=0;r<i;r+=1)t=this.hierarchy[r].finalTransform.mProp.v.props,e.transform(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15])}},globalToLocal:function(t){var e=[];e.push(this.finalTransform);for(var r=!0,i=this.comp;r;)i.finalTransform?(i.data.hasMask&&e.splice(0,0,i.finalTransform),i=i.comp):r=!1;var s,a,n=e.length;for(s=0;s<n;s+=1)a=e[s].mat.applyToPointArray(0,0,0),t=[t[0]-a[0],t[1]-a[1],0];return t},mHelper:new Matrix},RenderableElement.prototype={initRenderable:function(){this.isInRange=!1,this.hidden=!1,this.isTransparent=!1,this.renderableComponents=[]},addRenderableComponent:function(t){-1===this.renderableComponents.indexOf(t)&&this.renderableComponents.push(t)},removeRenderableComponent:function(t){-1!==this.renderableComponents.indexOf(t)&&this.renderableComponents.splice(this.renderableComponents.indexOf(t),1)},prepareRenderableFrame:function(t){this.checkLayerLimits(t)},checkTransparency:function(){this.finalTransform.mProp.o.v<=0?!this.isTransparent&&this.globalData.renderConfig.hideOnTransparent&&(this.isTransparent=!0,this.hide()):this.isTransparent&&(this.isTransparent=!1,this.show())},checkLayerLimits:function(t){this.data.ip-this.data.st<=t&&this.data.op-this.data.st>t?!0!==this.isInRange&&(this.globalData._mdf=!0,this._mdf=!0,this.isInRange=!0,this.show()):!1!==this.isInRange&&(this.globalData._mdf=!0,this.isInRange=!1,this.hide())},renderRenderable:function(){var t,e=this.renderableComponents.length;for(t=0;t<e;t+=1)this.renderableComponents[t].renderFrame(this._isFirstFrame)},sourceRectAtTime:function(){return{top:0,left:0,width:100,height:100}},getLayerSize:function(){return 5===this.data.ty?{w:this.data.textData.width,h:this.data.textData.height}:{w:this.data.width,h:this.data.height}}},extendPrototype([RenderableElement,createProxyFunction({initElement:function(t,e,r){this.initFrame(),this.initBaseData(t,e,r),this.initTransform(t,e,r),this.initHierarchy(),this.initRenderable(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),this.createContent(),this.hide()},hide:function(){this.hidden||this.isInRange&&!this.isTransparent||((this.baseElement||this.layerElement).style.display="none",this.hidden=!0)},show:function(){this.isInRange&&!this.isTransparent&&(this.data.hd||((this.baseElement||this.layerElement).style.display="block"),this.hidden=!1,this._isFirstFrame=!0)},renderFrame:function(){this.data.hd||this.hidden||(this.renderTransform(),this.renderRenderable(),this.renderElement(),this.renderInnerContent(),this._isFirstFrame&&(this._isFirstFrame=!1))},renderInnerContent:function(){},prepareFrame:function(t){this._mdf=!1,this.prepareRenderableFrame(t),this.prepareProperties(t,this.isInRange),this.checkTransparency()},destroy:function(){this.innerElem=null,this.destroyBaseElement()}})],RenderableDOMElement),SVGShapeData.prototype.setAsAnimated=function(){this._isAnimated=!0},ShapeTransformManager.prototype={addTransformSequence:function(t){var e,r=t.length,i="_";for(e=0;e<r;e+=1)i+=t[e].transform.key+"_";var s=this.sequences[i];return s||(s={transforms:[].concat(t),finalTransform:new Matrix,_mdf:!1},this.sequences[i]=s,this.sequenceList.push(s)),s},processSequence:function(t,e){for(var r,i=0,s=t.transforms.length,a=e;i<s&&!e;){if(t.transforms[i].transform.mProps._mdf){a=!0;break}i+=1}if(a)for(t.finalTransform.reset(),i=s-1;0<=i;i-=1)r=t.transforms[i].transform.mProps.v.props,t.finalTransform.transform(r[0],r[1],r[2],r[3],r[4],r[5],r[6],r[7],r[8],r[9],r[10],r[11],r[12],r[13],r[14],r[15]);t._mdf=a},processSequences:function(t){var e,r=this.sequenceList.length;for(e=0;e<r;e+=1)this.processSequence(this.sequenceList[e],t)},getNewKey:function(){return"_"+this.transform_key_count++}},CVShapeData.prototype.setAsAnimated=SVGShapeData.prototype.setAsAnimated,BaseElement.prototype={checkMasks:function(){if(!this.data.hasMask)return!1;for(var t=0,e=this.data.masksProperties.length;t<e;){if("n"!==this.data.masksProperties[t].mode&&!1!==this.data.masksProperties[t].cl)return!0;t+=1}return!1},initExpressions:function(){this.layerInterface=LayerExpressionInterface(this),this.data.hasMask&&this.maskManager&&this.layerInterface.registerMaskInterface(this.maskManager);var t=EffectsExpressionInterface.createEffectsInterface(this,this.layerInterface);this.layerInterface.registerEffectsInterface(t),0===this.data.ty||this.data.xt?this.compInterface=CompExpressionInterface(this):4===this.data.ty?(this.layerInterface.shapeInterface=ShapeExpressionInterface(this.shapesData,this.itemsData,this.layerInterface),this.layerInterface.content=this.layerInterface.shapeInterface):5===this.data.ty&&(this.layerInterface.textInterface=TextExpressionInterface(this),this.layerInterface.text=this.layerInterface.textInterface)},setBlendMode:function(){var t=getBlendMode(this.data.bm);(this.baseElement||this.layerElement).style["mix-blend-mode"]=t},initBaseData:function(t,e,r){this.globalData=e,this.comp=r,this.data=t,this.layerId=createElementID(),this.data.sr||(this.data.sr=1),this.effectsManager=new EffectsManager(this.data,this,this.dynamicProperties)},getType:function(){return this.type},sourceRectAtTime:function(){}},NullElement.prototype.prepareFrame=function(t){this.prepareProperties(t,!0)},NullElement.prototype.renderFrame=function(){},NullElement.prototype.getBaseElement=function(){return null},NullElement.prototype.destroy=function(){},NullElement.prototype.sourceRectAtTime=function(){},NullElement.prototype.hide=function(){},extendPrototype([BaseElement,TransformElement,HierarchyElement,FrameElement],NullElement),SVGBaseElement.prototype={initRendererElement:function(){this.layerElement=createNS("g")},createContainerElements:function(){this.matteElement=createNS("g"),this.transformedElement=this.layerElement,this.maskedElement=this.layerElement,this._sizeChanged=!1;var t,e,r,i=null;if(this.data.td){if(3==this.data.td||1==this.data.td){var s=createNS("mask");s.setAttribute("id",this.layerId),s.setAttribute("mask-type",3==this.data.td?"luminance":"alpha"),s.appendChild(this.layerElement),i=s,this.globalData.defs.appendChild(s),featureSupport.maskType||1!=this.data.td||(s.setAttribute("mask-type","luminance"),t=createElementID(),e=filtersFactory.createFilter(t),this.globalData.defs.appendChild(e),e.appendChild(filtersFactory.createAlphaToLuminanceFilter()),(r=createNS("g")).appendChild(this.layerElement),i=r,s.appendChild(r),r.setAttribute("filter","url("+locationHref+"#"+t+")"))}else if(2==this.data.td){var a=createNS("mask");a.setAttribute("id",this.layerId),a.setAttribute("mask-type","alpha");var n=createNS("g");a.appendChild(n),t=createElementID(),e=filtersFactory.createFilter(t);var o=createNS("feComponentTransfer");o.setAttribute("in","SourceGraphic"),e.appendChild(o);var h=createNS("feFuncA");h.setAttribute("type","table"),h.setAttribute("tableValues","1.0 0.0"),o.appendChild(h),this.globalData.defs.appendChild(e);var p=createNS("rect");p.setAttribute("width",this.comp.data.w),p.setAttribute("height",this.comp.data.h),p.setAttribute("x","0"),p.setAttribute("y","0"),p.setAttribute("fill","#ffffff"),p.setAttribute("opacity","0"),n.setAttribute("filter","url("+locationHref+"#"+t+")"),n.appendChild(p),n.appendChild(this.layerElement),i=n,featureSupport.maskType||(a.setAttribute("mask-type","luminance"),e.appendChild(filtersFactory.createAlphaToLuminanceFilter()),r=createNS("g"),n.appendChild(p),r.appendChild(this.layerElement),i=r,n.appendChild(r)),this.globalData.defs.appendChild(a)}}else this.data.tt?(this.matteElement.appendChild(this.layerElement),i=this.matteElement,this.baseElement=this.matteElement):this.baseElement=this.layerElement;if(this.data.ln&&this.layerElement.setAttribute("id",this.data.ln),this.data.cl&&this.layerElement.setAttribute("class",this.data.cl),0===this.data.ty&&!this.data.hd){var l=createNS("clipPath"),m=createNS("path");m.setAttribute("d","M0,0 L"+this.data.w+",0 L"+this.data.w+","+this.data.h+" L0,"+this.data.h+"z");var f=createElementID();if(l.setAttribute("id",f),l.appendChild(m),this.globalData.defs.appendChild(l),this.checkMasks()){var c=createNS("g");c.setAttribute("clip-path","url("+locationHref+"#"+f+")"),c.appendChild(this.layerElement),this.transformedElement=c,i?i.appendChild(this.transformedElement):this.baseElement=this.transformedElement}else this.layerElement.setAttribute("clip-path","url("+locationHref+"#"+f+")")}0!==this.data.bm&&this.setBlendMode()},renderElement:function(){this.finalTransform._matMdf&&this.transformedElement.setAttribute("transform",this.finalTransform.mat.to2dCSS()),this.finalTransform._opMdf&&this.transformedElement.setAttribute("opacity",this.finalTransform.mProp.o.v)},destroyBaseElement:function(){this.layerElement=null,this.matteElement=null,this.maskManager.destroy()},getBaseElement:function(){return this.data.hd?null:this.baseElement},createRenderableComponents:function(){this.maskManager=new MaskElement(this.data,this,this.globalData),this.renderableEffectsManager=new SVGEffects(this)},setMatte:function(t){this.matteElement&&this.matteElement.setAttribute("mask","url("+locationHref+"#"+t+")")}},IShapeElement.prototype={addShapeToModifiers:function(t){var e,r=this.shapeModifiers.length;for(e=0;e<r;e+=1)this.shapeModifiers[e].addShape(t)},isShapeInAnimatedModifiers:function(t){for(var e=this.shapeModifiers.length;0<e;)if(this.shapeModifiers[0].isAnimatedWithShape(t))return!0;return!1},renderModifiers:function(){if(this.shapeModifiers.length){var t,e=this.shapes.length;for(t=0;t<e;t+=1)this.shapes[t].sh.reset();for(t=(e=this.shapeModifiers.length)-1;0<=t;t-=1)this.shapeModifiers[t].processShapes(this._isFirstFrame)}},lcEnum:{1:"butt",2:"round",3:"square"},ljEnum:{1:"miter",2:"round",3:"bevel"},searchProcessedElement:function(t){for(var e=this.processedElements,r=0,i=e.length;r<i;){if(e[r].elem===t)return e[r].pos;r+=1}return 0},addProcessedElement:function(t,e){for(var r=this.processedElements,i=r.length;i;)if(r[i-=1].elem===t)return void(r[i].pos=e);r.push(new ProcessedElement(t,e))},prepareFrame:function(t){this.prepareRenderableFrame(t),this.prepareProperties(t,this.isInRange)}},ITextElement.prototype.initElement=function(t,e,r){this.lettersChangedFlag=!0,this.initFrame(),this.initBaseData(t,e,r),this.textProperty=new TextProperty(this,t.t,this.dynamicProperties),this.textAnimator=new TextAnimatorProperty(t.t,this.renderType,this),this.initTransform(t,e,r),this.initHierarchy(),this.initRenderable(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),this.createContent(),this.hide(),this.textAnimator.searchProperties(this.dynamicProperties)},ITextElement.prototype.prepareFrame=function(t){this._mdf=!1,this.prepareRenderableFrame(t),this.prepareProperties(t,this.isInRange),(this.textProperty._mdf||this.textProperty._isFirstFrame)&&(this.buildNewText(),this.textProperty._isFirstFrame=!1,this.textProperty._mdf=!1)},ITextElement.prototype.createPathShape=function(t,e){var r,i,s=e.length,a="";for(r=0;r<s;r+=1)i=e[r].ks.k,a+=buildShapeString(i,i.i.length,!0,t);return a},ITextElement.prototype.updateDocumentData=function(t,e){this.textProperty.updateDocumentData(t,e)},ITextElement.prototype.canResizeFont=function(t){this.textProperty.canResizeFont(t)},ITextElement.prototype.setMinimumFontSize=function(t){this.textProperty.setMinimumFontSize(t)},ITextElement.prototype.applyTextPropertiesToMatrix=function(t,e,r,i,s){switch(t.ps&&e.translate(t.ps[0],t.ps[1]+t.ascent,0),e.translate(0,-t.ls,0),t.j){case 1:e.translate(t.justifyOffset+(t.boxWidth-t.lineWidths[r]),0,0);break;case 2:e.translate(t.justifyOffset+(t.boxWidth-t.lineWidths[r])/2,0,0)}e.translate(i,s,0)},ITextElement.prototype.buildColor=function(t){return"rgb("+Math.round(255*t[0])+","+Math.round(255*t[1])+","+Math.round(255*t[2])+")"},ITextElement.prototype.emptyProp=new LetterProps,ITextElement.prototype.destroy=function(){},extendPrototype([BaseElement,TransformElement,HierarchyElement,FrameElement,RenderableDOMElement],ICompElement),ICompElement.prototype.initElement=function(t,e,r){this.initFrame(),this.initBaseData(t,e,r),this.initTransform(t,e,r),this.initRenderable(),this.initHierarchy(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),!this.data.xt&&e.progressiveLoad||this.buildAllItems(),this.hide()},ICompElement.prototype.prepareFrame=function(t){if(this._mdf=!1,this.prepareRenderableFrame(t),this.prepareProperties(t,this.isInRange),this.isInRange||this.data.xt){if(this.tm._placeholder)this.renderedFrame=t/this.data.sr;else{var e=this.tm.v;e===this.data.op&&(e=this.data.op-1),this.renderedFrame=e}var r,i=this.elements.length;for(this.completeLayers||this.checkLayers(this.renderedFrame),r=i-1;0<=r;r-=1)(this.completeLayers||this.elements[r])&&(this.elements[r].prepareFrame(this.renderedFrame-this.layers[r].st),this.elements[r]._mdf&&(this._mdf=!0))}},ICompElement.prototype.renderInnerContent=function(){var t,e=this.layers.length;for(t=0;t<e;t+=1)(this.completeLayers||this.elements[t])&&this.elements[t].renderFrame()},ICompElement.prototype.setElements=function(t){this.elements=t},ICompElement.prototype.getElements=function(){return this.elements},ICompElement.prototype.destroyElements=function(){var t,e=this.layers.length;for(t=0;t<e;t+=1)this.elements[t]&&this.elements[t].destroy()},ICompElement.prototype.destroy=function(){this.destroyElements(),this.destroyBaseElement()},extendPrototype([BaseElement,TransformElement,SVGBaseElement,HierarchyElement,FrameElement,RenderableDOMElement],IImageElement),IImageElement.prototype.createContent=function(){var t=this.globalData.getAssetsPath(this.assetData);this.innerElem=createNS("image"),this.innerElem.setAttribute("width",this.assetData.w+"px"),this.innerElem.setAttribute("height",this.assetData.h+"px"),this.innerElem.setAttribute("preserveAspectRatio",this.assetData.pr||this.globalData.renderConfig.imagePreserveAspectRatio),this.innerElem.setAttributeNS("http://www.w3.org/1999/xlink","href",t),this.layerElement.appendChild(this.innerElem)},IImageElement.prototype.sourceRectAtTime=function(){return this.sourceRect},extendPrototype([IImageElement],ISolidElement),ISolidElement.prototype.createContent=function(){var t=createNS("rect");t.setAttribute("width",this.data.sw),t.setAttribute("height",this.data.sh),t.setAttribute("fill",this.data.sc),this.layerElement.appendChild(t)},extendPrototype([BaseElement,TransformElement,SVGBaseElement,IShapeElement,HierarchyElement,FrameElement,RenderableDOMElement],SVGShapeElement),SVGShapeElement.prototype.initSecondaryElement=function(){},SVGShapeElement.prototype.identityMatrix=new Matrix,SVGShapeElement.prototype.buildExpressionInterface=function(){},SVGShapeElement.prototype.createContent=function(){this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement,0,[],!0),this.filterUniqueShapes()},SVGShapeElement.prototype.filterUniqueShapes=function(){var t,e,r,i,s=this.shapes.length,a=this.stylesList.length,n=[],o=!1;for(r=0;r<a;r+=1){for(i=this.stylesList[r],o=!1,t=n.length=0;t<s;t+=1)-1!==(e=this.shapes[t]).styles.indexOf(i)&&(n.push(e),o=e._isAnimated||o);1<n.length&&o&&this.setShapesAsAnimated(n)}},SVGShapeElement.prototype.setShapesAsAnimated=function(t){var e,r=t.length;for(e=0;e<r;e+=1)t[e].setAsAnimated()},SVGShapeElement.prototype.createStyleElement=function(t,e){var r,i=new SVGStyleData(t,e),s=i.pElem;if("st"===t.ty)r=new SVGStrokeStyleData(this,t,i);else if("fl"===t.ty)r=new SVGFillStyleData(this,t,i);else if("gf"===t.ty||"gs"===t.ty){r=new("gf"===t.ty?SVGGradientFillStyleData:SVGGradientStrokeStyleData)(this,t,i),this.globalData.defs.appendChild(r.gf),r.maskId&&(this.globalData.defs.appendChild(r.ms),this.globalData.defs.appendChild(r.of),s.setAttribute("mask","url("+locationHref+"#"+r.maskId+")"))}return"st"!==t.ty&&"gs"!==t.ty||(s.setAttribute("stroke-linecap",this.lcEnum[t.lc]||"round"),s.setAttribute("stroke-linejoin",this.ljEnum[t.lj]||"round"),s.setAttribute("fill-opacity","0"),1===t.lj&&s.setAttribute("stroke-miterlimit",t.ml)),2===t.r&&s.setAttribute("fill-rule","evenodd"),t.ln&&s.setAttribute("id",t.ln),t.cl&&s.setAttribute("class",t.cl),t.bm&&(s.style["mix-blend-mode"]=getBlendMode(t.bm)),this.stylesList.push(i),this.addToAnimatedContents(t,r),r},SVGShapeElement.prototype.createGroupElement=function(t){var e=new ShapeGroupData;return t.ln&&e.gr.setAttribute("id",t.ln),t.cl&&e.gr.setAttribute("class",t.cl),t.bm&&(e.gr.style["mix-blend-mode"]=getBlendMode(t.bm)),e},SVGShapeElement.prototype.createTransformElement=function(t,e){var r=TransformPropertyFactory.getTransformProperty(this,t,this),i=new SVGTransformData(r,r.o,e);return this.addToAnimatedContents(t,i),i},SVGShapeElement.prototype.createShapeElement=function(t,e,r){var i=4;"rc"===t.ty?i=5:"el"===t.ty?i=6:"sr"===t.ty&&(i=7);var s=new SVGShapeData(e,r,ShapePropertyFactory.getShapeProp(this,t,i,this));return this.shapes.push(s),this.addShapeToModifiers(s),this.addToAnimatedContents(t,s),s},SVGShapeElement.prototype.addToAnimatedContents=function(t,e){for(var r=0,i=this.animatedContents.length;r<i;){if(this.animatedContents[r].element===e)return;r+=1}this.animatedContents.push({fn:SVGElementsRenderer.createRenderFunction(t),element:e,data:t})},SVGShapeElement.prototype.setElementStyles=function(t){var e,r=t.styles,i=this.stylesList.length;for(e=0;e<i;e+=1)this.stylesList[e].closed||r.push(this.stylesList[e])},SVGShapeElement.prototype.reloadShapes=function(){this._isFirstFrame=!0;var t,e=this.itemsData.length;for(t=0;t<e;t+=1)this.prevViewData[t]=this.itemsData[t];for(this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement,0,[],!0),this.filterUniqueShapes(),e=this.dynamicProperties.length,t=0;t<e;t+=1)this.dynamicProperties[t].getValue();this.renderModifiers()},SVGShapeElement.prototype.searchShapes=function(t,e,r,i,s,a,n){var o,h,p,l,m,f,c=[].concat(a),d=t.length-1,u=[],y=[];for(o=d;0<=o;o-=1){if((f=this.searchProcessedElement(t[o]))?e[o]=r[f-1]:t[o]._render=n,"fl"==t[o].ty||"st"==t[o].ty||"gf"==t[o].ty||"gs"==t[o].ty)f?e[o].style.closed=!1:e[o]=this.createStyleElement(t[o],s),t[o]._render&&i.appendChild(e[o].style.pElem),u.push(e[o].style);else if("gr"==t[o].ty){if(f)for(p=e[o].it.length,h=0;h<p;h+=1)e[o].prevViewData[h]=e[o].it[h];else e[o]=this.createGroupElement(t[o]);this.searchShapes(t[o].it,e[o].it,e[o].prevViewData,e[o].gr,s+1,c,n),t[o]._render&&i.appendChild(e[o].gr)}else"tr"==t[o].ty?(f||(e[o]=this.createTransformElement(t[o],i)),l=e[o].transform,c.push(l)):"sh"==t[o].ty||"rc"==t[o].ty||"el"==t[o].ty||"sr"==t[o].ty?(f||(e[o]=this.createShapeElement(t[o],c,s)),this.setElementStyles(e[o])):"tm"==t[o].ty||"rd"==t[o].ty||"ms"==t[o].ty?(f?(m=e[o]).closed=!1:((m=ShapeModifiers.getModifier(t[o].ty)).init(this,t[o]),e[o]=m,this.shapeModifiers.push(m)),y.push(m)):"rp"==t[o].ty&&(f?(m=e[o]).closed=!0:(m=ShapeModifiers.getModifier(t[o].ty),(e[o]=m).init(this,t,o,e),this.shapeModifiers.push(m),n=!1),y.push(m));this.addProcessedElement(t[o],o+1)}for(d=u.length,o=0;o<d;o+=1)u[o].closed=!0;for(d=y.length,o=0;o<d;o+=1)y[o].closed=!0},SVGShapeElement.prototype.renderInnerContent=function(){this.renderModifiers();var t,e=this.stylesList.length;for(t=0;t<e;t+=1)this.stylesList[t].reset();for(this.renderShape(),t=0;t<e;t+=1)(this.stylesList[t]._mdf||this._isFirstFrame)&&(this.stylesList[t].msElem&&(this.stylesList[t].msElem.setAttribute("d",this.stylesList[t].d),this.stylesList[t].d="M0 0"+this.stylesList[t].d),this.stylesList[t].pElem.setAttribute("d",this.stylesList[t].d||"M0 0"))},SVGShapeElement.prototype.renderShape=function(){var t,e,r=this.animatedContents.length;for(t=0;t<r;t+=1)e=this.animatedContents[t],(this._isFirstFrame||e.element._isAnimated)&&!0!==e.data&&e.fn(e.data,e.element,this._isFirstFrame)},SVGShapeElement.prototype.destroy=function(){this.destroyBaseElement(),this.shapesData=null,this.itemsData=null},CVContextData.prototype.duplicate=function(){var t=2*this._length,e=this.savedOp;this.savedOp=createTypedArray("float32",t),this.savedOp.set(e);var r=0;for(r=this._length;r<t;r+=1)this.saved[r]=createTypedArray("float32",16);this._length=t},CVContextData.prototype.reset=function(){this.cArrPos=0,this.cTr.reset(),this.cO=1},CVBaseElement.prototype={createElements:function(){},initRendererElement:function(){},createContainerElements:function(){this.canvasContext=this.globalData.canvasContext,this.renderableEffectsManager=new CVEffects(this)},createContent:function(){},setBlendMode:function(){var t=this.globalData;if(t.blendMode!==this.data.bm){t.blendMode=this.data.bm;var e=getBlendMode(this.data.bm);t.canvasContext.globalCompositeOperation=e}},createRenderableComponents:function(){this.maskManager=new CVMaskElement(this.data,this)},hideElement:function(){this.hidden||this.isInRange&&!this.isTransparent||(this.hidden=!0)},showElement:function(){this.isInRange&&!this.isTransparent&&(this.hidden=!1,this._isFirstFrame=!0,this.maskManager._isFirstFrame=!0)},renderFrame:function(){this.hidden||this.data.hd||(this.renderTransform(),this.renderRenderable(),this.setBlendMode(),this.globalData.renderer.save(),this.globalData.renderer.ctxTransform(this.finalTransform.mat.props),this.globalData.renderer.ctxOpacity(this.finalTransform.mProp.o.v),this.renderInnerContent(),this.globalData.renderer.restore(),this.maskManager.hasMasks&&this.globalData.renderer.restore(!0),this._isFirstFrame&&(this._isFirstFrame=!1))},destroy:function(){this.canvasContext=null,this.data=null,this.globalData=null,this.maskManager.destroy()},mHelper:new Matrix},CVBaseElement.prototype.hide=CVBaseElement.prototype.hideElement,CVBaseElement.prototype.show=CVBaseElement.prototype.showElement,extendPrototype([CanvasRenderer,ICompElement,CVBaseElement],CVCompElement),CVCompElement.prototype.renderInnerContent=function(){var t;for(t=this.layers.length-1;0<=t;t-=1)(this.completeLayers||this.elements[t])&&this.elements[t].renderFrame()},CVCompElement.prototype.destroy=function(){var t;for(t=this.layers.length-1;0<=t;t-=1)this.elements[t]&&this.elements[t].destroy();this.layers=null,this.elements=null},CVMaskElement.prototype.renderFrame=function(){if(this.hasMasks){var t,e,r,i,s=this.element.finalTransform.mat,a=this.element.canvasContext,n=this.masksProperties.length;for(a.beginPath(),t=0;t<n;t++)if("n"!==this.masksProperties[t].mode){this.masksProperties[t].inv&&(a.moveTo(0,0),a.lineTo(this.element.globalData.compSize.w,0),a.lineTo(this.element.globalData.compSize.w,this.element.globalData.compSize.h),a.lineTo(0,this.element.globalData.compSize.h),a.lineTo(0,0)),i=this.viewData[t].v,e=s.applyToPointArray(i.v[0][0],i.v[0][1],0),a.moveTo(e[0],e[1]);var o,h=i._length;for(o=1;o<h;o++)r=s.applyToTriplePoints(i.o[o-1],i.i[o],i.v[o]),a.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5]);r=s.applyToTriplePoints(i.o[o-1],i.i[0],i.v[0]),a.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5])}this.element.globalData.renderer.save(!0),a.clip()}},CVMaskElement.prototype.getMaskProperty=MaskElement.prototype.getMaskProperty,CVMaskElement.prototype.destroy=function(){this.element=null},extendPrototype([BaseElement,TransformElement,CVBaseElement,IShapeElement,HierarchyElement,FrameElement,RenderableElement],CVShapeElement),CVShapeElement.prototype.initElement=RenderableDOMElement.prototype.initElement,CVShapeElement.prototype.transformHelper={opacity:1,_opMdf:!1},CVShapeElement.prototype.dashResetter=[],CVShapeElement.prototype.createContent=function(){this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,!0,[])},CVShapeElement.prototype.createStyleElement=function(t,e){var r={data:t,type:t.ty,preTransforms:this.transformsManager.addTransformSequence(e),transforms:[],elements:[],closed:!0===t.hd},i={};if("fl"==t.ty||"st"==t.ty?(i.c=PropertyFactory.getProp(this,t.c,1,255,this),i.c.k||(r.co="rgb("+bm_floor(i.c.v[0])+","+bm_floor(i.c.v[1])+","+bm_floor(i.c.v[2])+")")):"gf"!==t.ty&&"gs"!==t.ty||(i.s=PropertyFactory.getProp(this,t.s,1,null,this),i.e=PropertyFactory.getProp(this,t.e,1,null,this),i.h=PropertyFactory.getProp(this,t.h||{k:0},0,.01,this),i.a=PropertyFactory.getProp(this,t.a||{k:0},0,degToRads,this),i.g=new GradientProperty(this,t.g,this)),i.o=PropertyFactory.getProp(this,t.o,0,.01,this),"st"==t.ty||"gs"==t.ty){if(r.lc=this.lcEnum[t.lc]||"round",r.lj=this.ljEnum[t.lj]||"round",1==t.lj&&(r.ml=t.ml),i.w=PropertyFactory.getProp(this,t.w,0,null,this),i.w.k||(r.wi=i.w.v),t.d){var s=new DashProperty(this,t.d,"canvas",this);i.d=s,i.d.k||(r.da=i.d.dashArray,r.do=i.d.dashoffset[0])}}else r.r=2===t.r?"evenodd":"nonzero";return this.stylesList.push(r),i.style=r,i},CVShapeElement.prototype.createGroupElement=function(t){return{it:[],prevViewData:[]}},CVShapeElement.prototype.createTransformElement=function(t){return{transform:{opacity:1,_opMdf:!1,key:this.transformsManager.getNewKey(),op:PropertyFactory.getProp(this,t.o,0,.01,this),mProps:TransformPropertyFactory.getTransformProperty(this,t,this)}}},CVShapeElement.prototype.createShapeElement=function(t){var e=new CVShapeData(this,t,this.stylesList,this.transformsManager);return this.shapes.push(e),this.addShapeToModifiers(e),e},CVShapeElement.prototype.reloadShapes=function(){this._isFirstFrame=!0;var t,e=this.itemsData.length;for(t=0;t<e;t+=1)this.prevViewData[t]=this.itemsData[t];for(this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,!0,[]),e=this.dynamicProperties.length,t=0;t<e;t+=1)this.dynamicProperties[t].getValue();this.renderModifiers(),this.transformsManager.processSequences(this._isFirstFrame)},CVShapeElement.prototype.addTransformToStyleList=function(t){var e,r=this.stylesList.length;for(e=0;e<r;e+=1)this.stylesList[e].closed||this.stylesList[e].transforms.push(t)},CVShapeElement.prototype.removeTransformFromStyleList=function(){var t,e=this.stylesList.length;for(t=0;t<e;t+=1)this.stylesList[t].closed||this.stylesList[t].transforms.pop()},CVShapeElement.prototype.closeStyles=function(t){var e,r=t.length;for(e=0;e<r;e+=1)t[e].closed=!0},CVShapeElement.prototype.searchShapes=function(t,e,r,i,s){var a,n,o,h,p,l,m=t.length-1,f=[],c=[],d=[].concat(s);for(a=m;0<=a;a-=1){if((h=this.searchProcessedElement(t[a]))?e[a]=r[h-1]:t[a]._shouldRender=i,"fl"==t[a].ty||"st"==t[a].ty||"gf"==t[a].ty||"gs"==t[a].ty)h?e[a].style.closed=!1:e[a]=this.createStyleElement(t[a],d),f.push(e[a].style);else if("gr"==t[a].ty){if(h)for(o=e[a].it.length,n=0;n<o;n+=1)e[a].prevViewData[n]=e[a].it[n];else e[a]=this.createGroupElement(t[a]);this.searchShapes(t[a].it,e[a].it,e[a].prevViewData,i,d)}else"tr"==t[a].ty?(h||(l=this.createTransformElement(t[a]),e[a]=l),d.push(e[a]),this.addTransformToStyleList(e[a])):"sh"==t[a].ty||"rc"==t[a].ty||"el"==t[a].ty||"sr"==t[a].ty?h||(e[a]=this.createShapeElement(t[a])):"tm"==t[a].ty||"rd"==t[a].ty?(h?(p=e[a]).closed=!1:((p=ShapeModifiers.getModifier(t[a].ty)).init(this,t[a]),e[a]=p,this.shapeModifiers.push(p)),c.push(p)):"rp"==t[a].ty&&(h?(p=e[a]).closed=!0:(p=ShapeModifiers.getModifier(t[a].ty),(e[a]=p).init(this,t,a,e),this.shapeModifiers.push(p),i=!1),c.push(p));this.addProcessedElement(t[a],a+1)}for(this.removeTransformFromStyleList(),this.closeStyles(f),m=c.length,a=0;a<m;a+=1)c[a].closed=!0},CVShapeElement.prototype.renderInnerContent=function(){this.transformHelper.opacity=1,this.transformHelper._opMdf=!1,this.renderModifiers(),this.transformsManager.processSequences(this._isFirstFrame),this.renderShape(this.transformHelper,this.shapesData,this.itemsData,!0)},CVShapeElement.prototype.renderShapeTransform=function(t,e){(t._opMdf||e.op._mdf||this._isFirstFrame)&&(e.opacity=t.opacity,e.opacity*=e.op.v,e._opMdf=!0)},CVShapeElement.prototype.drawLayer=function(){var t,e,r,i,s,a,n,o,h,p=this.stylesList.length,l=this.globalData.renderer,m=this.globalData.canvasContext;for(t=0;t<p;t+=1)if(("st"!==(o=(h=this.stylesList[t]).type)&&"gs"!==o||0!==h.wi)&&h.data._shouldRender&&0!==h.coOp&&0!==this.globalData.currentGlobalAlpha){for(l.save(),a=h.elements,"st"===o||"gs"===o?(m.strokeStyle="st"===o?h.co:h.grd,m.lineWidth=h.wi,m.lineCap=h.lc,m.lineJoin=h.lj,m.miterLimit=h.ml||0):m.fillStyle="fl"===o?h.co:h.grd,l.ctxOpacity(h.coOp),"st"!==o&&"gs"!==o&&m.beginPath(),l.ctxTransform(h.preTransforms.finalTransform.props),r=a.length,e=0;e<r;e+=1){for("st"!==o&&"gs"!==o||(m.beginPath(),h.da&&(m.setLineDash(h.da),m.lineDashOffset=h.do)),s=(n=a[e].trNodes).length,i=0;i<s;i+=1)"m"==n[i].t?m.moveTo(n[i].p[0],n[i].p[1]):"c"==n[i].t?m.bezierCurveTo(n[i].pts[0],n[i].pts[1],n[i].pts[2],n[i].pts[3],n[i].pts[4],n[i].pts[5]):m.closePath();"st"!==o&&"gs"!==o||(m.stroke(),h.da&&m.setLineDash(this.dashResetter))}"st"!==o&&"gs"!==o&&m.fill(h.r),l.restore()}},CVShapeElement.prototype.renderShape=function(t,e,r,i){var s,a;for(a=t,s=e.length-1;0<=s;s-=1)"tr"==e[s].ty?(a=r[s].transform,this.renderShapeTransform(t,a)):"sh"==e[s].ty||"el"==e[s].ty||"rc"==e[s].ty||"sr"==e[s].ty?this.renderPath(e[s],r[s]):"fl"==e[s].ty?this.renderFill(e[s],r[s],a):"st"==e[s].ty?this.renderStroke(e[s],r[s],a):"gf"==e[s].ty||"gs"==e[s].ty?this.renderGradientFill(e[s],r[s],a):"gr"==e[s].ty?this.renderShape(a,e[s].it,r[s].it):e[s].ty;i&&this.drawLayer()},CVShapeElement.prototype.renderStyledShape=function(t,e){if(this._isFirstFrame||e._mdf||t.transforms._mdf){var r,i,s,a=t.trNodes,n=e.paths,o=n._length;a.length=0;var h=t.transforms.finalTransform;for(s=0;s<o;s+=1){var p=n.shapes[s];if(p&&p.v){for(i=p._length,r=1;r<i;r+=1)1===r&&a.push({t:"m",p:h.applyToPointArray(p.v[0][0],p.v[0][1],0)}),a.push({t:"c",pts:h.applyToTriplePoints(p.o[r-1],p.i[r],p.v[r])});1===i&&a.push({t:"m",p:h.applyToPointArray(p.v[0][0],p.v[0][1],0)}),p.c&&i&&(a.push({t:"c",pts:h.applyToTriplePoints(p.o[r-1],p.i[0],p.v[0])}),a.push({t:"z"}))}}t.trNodes=a}},CVShapeElement.prototype.renderPath=function(t,e){if(!0!==t.hd&&t._shouldRender){var r,i=e.styledShapes.length;for(r=0;r<i;r+=1)this.renderStyledShape(e.styledShapes[r],e.sh)}},CVShapeElement.prototype.renderFill=function(t,e,r){var i=e.style;(e.c._mdf||this._isFirstFrame)&&(i.co="rgb("+bm_floor(e.c.v[0])+","+bm_floor(e.c.v[1])+","+bm_floor(e.c.v[2])+")"),(e.o._mdf||r._opMdf||this._isFirstFrame)&&(i.coOp=e.o.v*r.opacity)},CVShapeElement.prototype.renderGradientFill=function(t,e,r){var i=e.style;if(!i.grd||e.g._mdf||e.s._mdf||e.e._mdf||1!==t.t&&(e.h._mdf||e.a._mdf)){var s=this.globalData.canvasContext,a=e.s.v,n=e.e.v;if(1===t.t)f=s.createLinearGradient(a[0],a[1],n[0],n[1]);else var o=Math.sqrt(Math.pow(a[0]-n[0],2)+Math.pow(a[1]-n[1],2)),h=Math.atan2(n[1]-a[1],n[0]-a[0]),p=o*(1<=e.h.v?.99:e.h.v<=-1?-.99:e.h.v),l=Math.cos(h+e.a.v)*p+a[0],m=Math.sin(h+e.a.v)*p+a[1],f=s.createRadialGradient(l,m,0,a[0],a[1],o);var c,d=t.g.p,u=e.g.c,y=1;for(c=0;c<d;c+=1)e.g._hasOpacity&&e.g._collapsable&&(y=e.g.o[2*c+1]),f.addColorStop(u[4*c]/100,"rgba("+u[4*c+1]+","+u[4*c+2]+","+u[4*c+3]+","+y+")");i.grd=f}i.coOp=e.o.v*r.opacity},CVShapeElement.prototype.renderStroke=function(t,e,r){var i=e.style,s=e.d;s&&(s._mdf||this._isFirstFrame)&&(i.da=s.dashArray,i.do=s.dashoffset[0]),(e.c._mdf||this._isFirstFrame)&&(i.co="rgb("+bm_floor(e.c.v[0])+","+bm_floor(e.c.v[1])+","+bm_floor(e.c.v[2])+")"),(e.o._mdf||r._opMdf||this._isFirstFrame)&&(i.coOp=e.o.v*r.opacity),(e.w._mdf||this._isFirstFrame)&&(i.wi=e.w.v)},CVShapeElement.prototype.destroy=function(){this.shapesData=null,this.globalData=null,this.canvasContext=null,this.stylesList.length=0,this.itemsData.length=0},extendPrototype([BaseElement,TransformElement,CVBaseElement,HierarchyElement,FrameElement,RenderableElement],CVSolidElement),CVSolidElement.prototype.initElement=SVGShapeElement.prototype.initElement,CVSolidElement.prototype.prepareFrame=IImageElement.prototype.prepareFrame,CVSolidElement.prototype.renderInnerContent=function(){var t=this.canvasContext;t.fillStyle=this.data.sc,t.fillRect(0,0,this.data.sw,this.data.sh)},CVEffects.prototype.renderFrame=function(){};var animationManager=(tJ={},uJ=[],vJ=0,wJ=0,xJ=0,yJ=!0,zJ=!1,tJ.registerAnimation=BJ,tJ.loadAnimation=function(t){var e=new AnimationItem;return FJ(e,null),e.setParams(t),e},tJ.setSpeed=function(t,e){var r;for(r=0;r<wJ;r+=1)uJ[r].animation.setSpeed(t,e)},tJ.setDirection=function(t,e){var r;for(r=0;r<wJ;r+=1)uJ[r].animation.setDirection(t,e)},tJ.play=function(t){var e;for(e=0;e<wJ;e+=1)uJ[e].animation.play(t)},tJ.pause=function(t){var e;for(e=0;e<wJ;e+=1)uJ[e].animation.pause(t)},tJ.stop=function(t){var e;for(e=0;e<wJ;e+=1)uJ[e].animation.stop(t)},tJ.togglePause=function(t){var e;for(e=0;e<wJ;e+=1)uJ[e].animation.togglePause(t)},tJ.searchAnimations=function(t,e,r){var i,s=[].concat([].slice.call(document.getElementsByClassName("lottie")),[].slice.call(document.getElementsByClassName("bodymovin"))),a=s.length;for(i=0;i<a;i+=1)r&&s[i].setAttribute("data-bm-type",r),BJ(s[i],t);if(e&&0===a){r||(r="svg");var n=document.getElementsByTagName("body")[0];n.innerHTML="";var o=createTag("div");o.style.width="100%",o.style.height="100%",o.setAttribute("data-bm-type",r),n.appendChild(o),BJ(o,t)}},tJ.resize=function(){var t;for(t=0;t<wJ;t+=1)uJ[t].animation.resize()},tJ.goToAndStop=function(t,e,r){var i;for(i=0;i<wJ;i+=1)uJ[i].animation.goToAndStop(t,e,r)},tJ.destroy=function(t){var e;for(e=wJ-1;0<=e;e-=1)uJ[e].animation.destroy(t)},tJ.freeze=function(){zJ=!0},tJ.unfreeze=function(){zJ=!1,TJ()},tJ.getRegisteredAnimations=function(){var t,e=uJ.length,r=[];for(t=0;t<e;t+=1)r.push(uJ[t].animation);return r},tJ),tJ,uJ,vJ,wJ,xJ,yJ,zJ,PK,QK,RK,SK,TK,UK,VK;function AJ(t){for(var e=0,r=t.target;e<wJ;)uJ[e].animation===r&&(uJ.splice(e,1),e-=1,wJ-=1,r.isPaused||EJ()),e+=1}function BJ(t,e){if(!t)return null;for(var r=0;r<wJ;){if(uJ[r].elem==t&&null!==uJ[r].elem)return uJ[r].animation;r+=1}var i=new AnimationItem;return FJ(i,t),i.setData(t,e),i}function DJ(){xJ+=1,TJ()}function EJ(){xJ-=1}function FJ(t,e){t.addEventListener("destroy",AJ),t.addEventListener("_active",DJ),t.addEventListener("_idle",EJ),uJ.push({elem:e,animation:t}),wJ+=1}function KJ(t){var e,r=t-vJ;for(e=0;e<wJ;e+=1)uJ[e].animation.advanceTime(r);vJ=t,xJ&&!zJ?window.requestAnimationFrame(KJ):yJ=!0}function LJ(t){vJ=t,window.requestAnimationFrame(KJ)}function TJ(){!zJ&&xJ&&yJ&&(window.requestAnimationFrame(LJ),yJ=!1)}function WK(t){for(var e=0,r=t.target;e<SK;)QK[e].animation===r&&(QK.splice(e,1),e-=1,SK-=1,r.isPaused||$K()),e+=1}function ZK(){TK+=1,nL()}function $K(){TK-=1}function _K(t,e){t.addEventListener("destroy",WK),t.addEventListener("_active",ZK),t.addEventListener("_idle",$K),QK.push({elem:e,animation:t}),SK+=1}function eL(t){var e,r=t-RK;for(e=0;e<SK;e+=1)QK[e].animation.advanceTime(r);RK=t,TK&&!VK?requestAnimationFrame(eL):UK=!0}function fL(t){RK=t,requestAnimationFrame(eL)}function nL(){!VK&&TK&&UK&&(requestAnimationFrame(fL),UK=!1)}PK={},QK=[],RK=0,SK=0,TK=0,UK=!0,VK=!1,PK.registerAnimation=function(t,e){if(!t)return null;for(var r=0;r<SK;){if(QK[r].elem==t&&null!==QK[r].elem)return QK[r].animation;r+=1}var i=new AnimationItem;return _K(i,t),i.setData(t,e),i},PK.loadAnimation=function(t){var e=new AnimationItem;return _K(e,null),e.setParams(t),e},PK.setSpeed=function(t,e){var r;for(r=0;r<SK;r+=1)QK[r].animation.setSpeed(t,e)},PK.setDirection=function(t,e){var r;for(r=0;r<SK;r+=1)QK[r].animation.setDirection(t,e)},PK.play=function(t){var e;for(e=0;e<SK;e+=1)QK[e].animation.play(t)},PK.pause=function(t){var e;for(e=0;e<SK;e+=1)QK[e].animation.pause(t)},PK.stop=function(t){var e;for(e=0;e<SK;e+=1)QK[e].animation.stop(t)},PK.togglePause=function(t){var e;for(e=0;e<SK;e+=1)QK[e].animation.togglePause(t)},PK.searchAnimations=function(t,e,r){throw new Error("Cannot access DOM from worker thread")},PK.resize=function(){var t;for(t=0;t<SK;t+=1)QK[t].animation.resize()},PK.goToAndStop=function(t,e,r){var i;for(i=0;i<SK;i+=1)QK[i].animation.goToAndStop(t,e,r)},PK.destroy=function(t){var e;for(e=SK-1;0<=e;e-=1)QK[e].animation.destroy(t)},PK.freeze=function(){VK=!0},PK.unfreeze=function(){VK=!1,nL()},PK.getRegisteredAnimations=function(){var t,e=QK.length,r=[];for(t=0;t<e;t+=1)r.push(QK[t].animation);return r},animationManager=PK;var AnimationItem=function(){this._cbs=[],this.name="",this.path="",this.isLoaded=!1,this.currentFrame=0,this.currentRawFrame=0,this.totalFrames=0,this.frameRate=0,this.frameMult=0,this.playSpeed=1,this.playDirection=1,this.playCount=0,this.animationData={},this.assets=[],this.isPaused=!0,this.autoplay=!1,this.loop=!0,this.renderer=null,this.animationID=createElementID(),this.assetsPath="",this.timeCompleted=0,this.segmentPos=0,this.subframeEnabled=subframeEnabled,this.segments=[],this._idle=!0,this._completedLoop=!1,this.projectInterface=ProjectInterface(),this.imagePreloader=new ImagePreloader};extendPrototype([BaseEvent],AnimationItem),AnimationItem.prototype.setParams=function(t){t.context&&(this.context=t.context),(t.wrapper||t.container)&&(this.wrapper=t.wrapper||t.container);var e=t.animType?t.animType:t.renderer?t.renderer:"svg";switch(e){case"canvas":this.renderer=new CanvasRenderer(this,t.rendererSettings);break;case"svg":this.renderer=new SVGRenderer(this,t.rendererSettings);break;default:this.renderer=new HybridRenderer(this,t.rendererSettings)}this.renderer.setProjectInterface(this.projectInterface),this.animType=e,""===t.loop||null===t.loop||(!1===t.loop?this.loop=!1:!0===t.loop?this.loop=!0:this.loop=parseInt(t.loop)),this.autoplay=!("autoplay"in t)||t.autoplay,this.name=t.name?t.name:"",this.autoloadSegments=!t.hasOwnProperty("autoloadSegments")||t.autoloadSegments,this.assetsPath=t.assetsPath,t.animationData?this.configAnimation(t.animationData):t.path&&("json"!=t.path.substr(-4)&&("/"!=t.path.substr(-1,1)&&(t.path+="/"),t.path+="data.json"),-1!=t.path.lastIndexOf("\\")?this.path=t.path.substr(0,t.path.lastIndexOf("\\")+1):this.path=t.path.substr(0,t.path.lastIndexOf("/")+1),this.fileName=t.path.substr(t.path.lastIndexOf("/")+1),this.fileName=this.fileName.substr(0,this.fileName.lastIndexOf(".json")),assetLoader.load(t.path,this.configAnimation.bind(this),function(){this.trigger("data_failed")}.bind(this)))},AnimationItem.prototype.setData=function(t,e){var r={wrapper:t,animationData:e?"object"==typeof e?e:JSON.parse(e):null},i=t.attributes;r.path=i.getNamedItem("data-animation-path")?i.getNamedItem("data-animation-path").value:i.getNamedItem("data-bm-path")?i.getNamedItem("data-bm-path").value:i.getNamedItem("bm-path")?i.getNamedItem("bm-path").value:"",r.animType=i.getNamedItem("data-anim-type")?i.getNamedItem("data-anim-type").value:i.getNamedItem("data-bm-type")?i.getNamedItem("data-bm-type").value:i.getNamedItem("bm-type")?i.getNamedItem("bm-type").value:i.getNamedItem("data-bm-renderer")?i.getNamedItem("data-bm-renderer").value:i.getNamedItem("bm-renderer")?i.getNamedItem("bm-renderer").value:"canvas";var s=i.getNamedItem("data-anim-loop")?i.getNamedItem("data-anim-loop").value:i.getNamedItem("data-bm-loop")?i.getNamedItem("data-bm-loop").value:i.getNamedItem("bm-loop")?i.getNamedItem("bm-loop").value:"";""===s||(r.loop="false"!==s&&("true"===s||parseInt(s)));var a=i.getNamedItem("data-anim-autoplay")?i.getNamedItem("data-anim-autoplay").value:i.getNamedItem("data-bm-autoplay")?i.getNamedItem("data-bm-autoplay").value:!i.getNamedItem("bm-autoplay")||i.getNamedItem("bm-autoplay").value;r.autoplay="false"!==a,r.name=i.getNamedItem("data-name")?i.getNamedItem("data-name").value:i.getNamedItem("data-bm-name")?i.getNamedItem("data-bm-name").value:i.getNamedItem("bm-name")?i.getNamedItem("bm-name").value:"","false"===(i.getNamedItem("data-anim-prerender")?i.getNamedItem("data-anim-prerender").value:i.getNamedItem("data-bm-prerender")?i.getNamedItem("data-bm-prerender").value:i.getNamedItem("bm-prerender")?i.getNamedItem("bm-prerender").value:"")&&(r.prerender=!1),this.setParams(r)},AnimationItem.prototype.includeLayers=function(t){t.op>this.animationData.op&&(this.animationData.op=t.op,this.totalFrames=Math.floor(t.op-this.animationData.ip));var e,r,i=this.animationData.layers,s=i.length,a=t.layers,n=a.length;for(r=0;r<n;r+=1)for(e=0;e<s;){if(i[e].id==a[r].id){i[e]=a[r];break}e+=1}if((t.chars||t.fonts)&&(this.renderer.globalData.fontManager.addChars(t.chars),this.renderer.globalData.fontManager.addFonts(t.fonts,this.renderer.globalData.defs)),t.assets)for(s=t.assets.length,e=0;e<s;e+=1)this.animationData.assets.push(t.assets[e]);this.animationData.__complete=!1,dataManager.completeData(this.animationData,this.renderer.globalData.fontManager),this.renderer.includeLayers(t.layers),expressionsPlugin&&expressionsPlugin.initExpressions(this),this.loadNextSegment()},AnimationItem.prototype.loadNextSegment=function(){var t=this.animationData.segments;if(!t||0===t.length||!this.autoloadSegments)return this.trigger("data_ready"),void(this.timeCompleted=this.totalFrames);var e=t.shift();this.timeCompleted=e.time*this.frameRate;var r=this.path+this.fileName+"_"+this.segmentPos+".json";this.segmentPos+=1,assetLoader.load(r,this.includeLayers.bind(this),function(){this.trigger("data_failed")}.bind(this))},AnimationItem.prototype.loadSegments=function(){this.animationData.segments||(this.timeCompleted=this.totalFrames),this.loadNextSegment()},AnimationItem.prototype.imagesLoaded=function(){this.trigger("loaded_images"),this.checkLoaded()},AnimationItem.prototype.preloadImages=function(){this.imagePreloader.setAssetsPath(this.assetsPath),this.imagePreloader.setPath(this.path),this.imagePreloader.loadAssets(this.animationData.assets,this.imagesLoaded.bind(this))},AnimationItem.prototype.configAnimation=function(t){this.renderer&&(this.animationData=t,this.totalFrames=Math.floor(this.animationData.op-this.animationData.ip),this.renderer.configAnimation(t),t.assets||(t.assets=[]),this.renderer.searchExtraCompositions(t.assets),this.assets=this.animationData.assets,this.frameRate=this.animationData.fr,this.firstFrame=Math.round(this.animationData.ip),this.frameMult=this.animationData.fr/1e3,this.trigger("config_ready"),this.preloadImages(),this.loadSegments(),this.updaFrameModifier(),this.waitForFontsLoaded())},AnimationItem.prototype.waitForFontsLoaded=function(){this.renderer&&(this.renderer.globalData.fontManager.loaded()?this.checkLoaded():setTimeout(this.waitForFontsLoaded.bind(this),20))},AnimationItem.prototype.checkLoaded=function(){this.isLoaded||!this.renderer.globalData.fontManager.loaded()||!this.imagePreloader.loaded()&&"canvas"===this.renderer.rendererType||(this.isLoaded=!0,dataManager.completeData(this.animationData,this.renderer.globalData.fontManager),expressionsPlugin&&expressionsPlugin.initExpressions(this),this.renderer.initItems(),setTimeout(function(){this.trigger("DOMLoaded")}.bind(this),0),this.gotoFrame(),this.autoplay&&this.play())},AnimationItem.prototype.resize=function(){this.renderer.updateContainerSize()},AnimationItem.prototype.setSubframe=function(t){this.subframeEnabled=!!t},AnimationItem.prototype.gotoFrame=function(){this.currentFrame=this.subframeEnabled?this.currentRawFrame:~~this.currentRawFrame,this.timeCompleted!==this.totalFrames&&this.currentFrame>this.timeCompleted&&(this.currentFrame=this.timeCompleted),this.trigger("enterFrame"),this.renderFrame()},AnimationItem.prototype.renderFrame=function(){!1!==this.isLoaded&&this.renderer.renderFrame(this.currentFrame+this.firstFrame)},AnimationItem.prototype.play=function(t){t&&this.name!=t||!0===this.isPaused&&(this.isPaused=!1,this._idle&&(this._idle=!1,this.trigger("_active")))},AnimationItem.prototype.pause=function(t){t&&this.name!=t||!1===this.isPaused&&(this.isPaused=!0,this._idle=!0,this.trigger("_idle"))},AnimationItem.prototype.togglePause=function(t){t&&this.name!=t||(!0===this.isPaused?this.play():this.pause())},AnimationItem.prototype.stop=function(t){t&&this.name!=t||(this.pause(),this.playCount=0,this._completedLoop=!1,this.setCurrentRawFrameValue(0))},AnimationItem.prototype.goToAndStop=function(t,e,r){r&&this.name!=r||(e?this.setCurrentRawFrameValue(t):this.setCurrentRawFrameValue(t*this.frameModifier),this.pause())},AnimationItem.prototype.goToAndPlay=function(t,e,r){this.goToAndStop(t,e,r),this.play()},AnimationItem.prototype.advanceTime=function(t){if(!0!==this.isPaused&&!1!==this.isLoaded){var e=this.currentRawFrame+t*this.frameModifier,r=!1;e>=this.totalFrames-1&&0<this.frameModifier?this.loop&&this.playCount!==this.loop?e>=this.totalFrames?(this.playCount+=1,this.checkSegments(e%this.totalFrames)||(this.setCurrentRawFrameValue(e%this.totalFrames),this._completedLoop=!0,this.trigger("loopComplete"))):this.setCurrentRawFrameValue(e):this.checkSegments(e>this.totalFrames?e%this.totalFrames:0)||(r=!0,e=this.totalFrames-1):e<0?this.checkSegments(e%this.totalFrames)||(!this.loop||this.playCount--<=0&&!0!==this.loop?(r=!0,e=0):(this.setCurrentRawFrameValue(this.totalFrames+e%this.totalFrames),this._completedLoop?this.trigger("loopComplete"):this._completedLoop=!0)):this.setCurrentRawFrameValue(e),r&&(this.setCurrentRawFrameValue(e),this.pause(),this.trigger("complete"))}},AnimationItem.prototype.adjustSegment=function(t,e){this.playCount=0,t[1]<t[0]?(0<this.frameModifier&&(this.playSpeed<0?this.setSpeed(-this.playSpeed):this.setDirection(-1)),this.timeCompleted=this.totalFrames=t[0]-t[1],this.firstFrame=t[1],this.setCurrentRawFrameValue(this.totalFrames-.001-e)):t[1]>t[0]&&(this.frameModifier<0&&(this.playSpeed<0?this.setSpeed(-this.playSpeed):this.setDirection(1)),this.timeCompleted=this.totalFrames=t[1]-t[0],this.firstFrame=t[0],this.setCurrentRawFrameValue(.001+e)),this.trigger("segmentStart")},AnimationItem.prototype.setSegment=function(t,e){var r=-1;this.isPaused&&(this.currentRawFrame+this.firstFrame<t?r=t:this.currentRawFrame+this.firstFrame>e&&(r=e-t)),this.firstFrame=t,this.timeCompleted=this.totalFrames=e-t,-1!==r&&this.goToAndStop(r,!0)},AnimationItem.prototype.playSegments=function(t,e){if(e&&(this.segments.length=0),"object"==typeof t[0]){var r,i=t.length;for(r=0;r<i;r+=1)this.segments.push(t[r])}else this.segments.push(t);this.segments.length&&e&&this.adjustSegment(this.segments.shift(),0),this.isPaused&&this.play()},AnimationItem.prototype.resetSegments=function(t){this.segments.length=0,this.segments.push([this.animationData.ip,this.animationData.op]),t&&this.checkSegments(0)},AnimationItem.prototype.checkSegments=function(t){return!!this.segments.length&&(this.adjustSegment(this.segments.shift(),t),!0)},AnimationItem.prototype.destroy=function(t){t&&this.name!=t||!this.renderer||(this.renderer.destroy(),this.imagePreloader.destroy(),this.trigger("destroy"),this._cbs=null,this.onEnterFrame=this.onLoopComplete=this.onComplete=this.onSegmentStart=this.onDestroy=null,this.renderer=null)},AnimationItem.prototype.setCurrentRawFrameValue=function(t){this.currentRawFrame=t,this.gotoFrame()},AnimationItem.prototype.setSpeed=function(t){this.playSpeed=t,this.updaFrameModifier()},AnimationItem.prototype.setDirection=function(t){this.playDirection=t<0?-1:1,this.updaFrameModifier()},AnimationItem.prototype.updaFrameModifier=function(){this.frameModifier=this.frameMult*this.playSpeed*this.playDirection},AnimationItem.prototype.getPath=function(){return this.path},AnimationItem.prototype.getAssetsPath=function(t){var e="";if(t.e)e=t.p;else if(this.assetsPath){var r=t.p;-1!==r.indexOf("images/")&&(r=r.split("/")[1]),e=this.assetsPath+r}else e=this.path,e+=t.u?t.u:"",e+=t.p;return e},AnimationItem.prototype.getAssetData=function(t){for(var e=0,r=this.assets.length;e<r;){if(t==this.assets[e].id)return this.assets[e];e+=1}},AnimationItem.prototype.hide=function(){this.renderer.hide()},AnimationItem.prototype.show=function(){this.renderer.show()},AnimationItem.prototype.getDuration=function(t){return t?this.totalFrames:this.totalFrames/this.frameRate},AnimationItem.prototype.trigger=function(t){if(this._cbs&&this._cbs[t])switch(t){case"enterFrame":this.triggerEvent(t,new BMEnterFrameEvent(t,this.currentFrame,this.totalFrames,this.frameModifier));break;case"loopComplete":this.triggerEvent(t,new BMCompleteLoopEvent(t,this.loop,this.playCount,this.frameMult));break;case"complete":this.triggerEvent(t,new BMCompleteEvent(t,this.frameMult));break;case"segmentStart":this.triggerEvent(t,new BMSegmentStartEvent(t,this.firstFrame,this.totalFrames));break;case"destroy":this.triggerEvent(t,new BMDestroyEvent(t,this));break;default:this.triggerEvent(t)}"enterFrame"===t&&this.onEnterFrame&&this.onEnterFrame.call(this,new BMEnterFrameEvent(t,this.currentFrame,this.totalFrames,this.frameMult)),"loopComplete"===t&&this.onLoopComplete&&this.onLoopComplete.call(this,new BMCompleteLoopEvent(t,this.loop,this.playCount,this.frameMult)),"complete"===t&&this.onComplete&&this.onComplete.call(this,new BMCompleteEvent(t,this.frameMult)),"segmentStart"===t&&this.onSegmentStart&&this.onSegmentStart.call(this,new BMSegmentStartEvent(t,this.firstFrame,this.totalFrames)),"destroy"===t&&this.onDestroy&&this.onDestroy.call(this,new BMDestroyEvent(t,this))},AnimationItem.prototype.setParams=function(t){t.context&&(this.context=t.context);var e=t.animType?t.animType:t.renderer?t.renderer:"svg";switch(e){case"canvas":this.renderer=new CanvasRenderer(this,t.rendererSettings);break;default:throw new Error("Only canvas renderer is supported when using worker.")}if(this.renderer.setProjectInterface(this.projectInterface),this.animType=e,""===t.loop||null===t.loop||(!1===t.loop?this.loop=!1:!0===t.loop?this.loop=!0:this.loop=parseInt(t.loop)),this.autoplay=!("autoplay"in t)||t.autoplay,this.name=t.name?t.name:"",this.autoloadSegments=!t.hasOwnProperty("autoloadSegments")||t.autoloadSegments,this.assetsPath=null,t.animationData)this.configAnimation(t.animationData);else if(t.path)throw new Error("Canvas worker renderer cannot load animation from url")},AnimationItem.prototype.setData=function(t,e){throw new Error("Cannot set data on wrapper for canvas worker renderer")},AnimationItem.prototype.includeLayers=function(t){t.op>this.animationData.op&&(this.animationData.op=t.op,this.totalFrames=Math.floor(t.op-this.animationData.ip));var e,r,i=this.animationData.layers,s=i.length,a=t.layers,n=a.length;for(r=0;r<n;r+=1)for(e=0;e<s;){if(i[e].id==a[r].id){i[e]=a[r];break}e+=1}this.animationData.__complete=!1,dataManager.completeData(this.animationData,this.renderer.globalData.fontManager),this.renderer.includeLayers(t.layers),expressionsPlugin&&expressionsPlugin.initExpressions(this),this.loadNextSegment()},AnimationItem.prototype.loadNextSegment=function(){var t=this.animationData.segments;if(t&&0!==t.length&&this.autoloadSegments)throw new Error("Cannot load multiple segments in worker.");this.timeCompleted=this.totalFrames},AnimationItem.prototype.loadSegments=function(){this.animationData.segments||(this.timeCompleted=this.totalFrames),this.loadNextSegment()},AnimationItem.prototype.imagesLoaded=null,AnimationItem.prototype.preloadImages=null,AnimationItem.prototype.configAnimation=function(t){this.renderer&&(this.animationData=t,this.totalFrames=Math.floor(this.animationData.op-this.animationData.ip),this.renderer.configAnimation(t),t.assets||(t.assets=[]),this.renderer.searchExtraCompositions(t.assets),this.assets=this.animationData.assets,this.frameRate=this.animationData.fr,this.firstFrame=Math.round(this.animationData.ip),this.frameMult=this.animationData.fr/1e3,this.loadSegments(),this.updaFrameModifier(),this.checkLoaded())},AnimationItem.prototype.waitForFontsLoaded=null,AnimationItem.prototype.checkLoaded=function(){this.isLoaded||(this.isLoaded=!0,dataManager.completeData(this.animationData,null),expressionsPlugin&&expressionsPlugin.initExpressions(this),this.renderer.initItems(),this.gotoFrame())},AnimationItem.prototype.destroy=function(t){t&&this.name!=t||!this.renderer||(this.renderer.destroy(),this._cbs=null,this.onEnterFrame=this.onLoopComplete=this.onComplete=this.onSegmentStart=this.onDestroy=null,this.renderer=null)},AnimationItem.prototype.getPath=null;var Expressions=(xN={},xN.initExpressions=function(t){var e=0,r=[];t.renderer.compInterface=CompExpressionInterface(t.renderer),t.renderer.globalData.projectInterface.registerComposition(t.renderer),t.renderer.globalData.pushExpression=function(){e+=1},t.renderer.globalData.popExpression=function(){0==(e-=1)&&function(){var t,e=r.length;for(t=0;t<e;t+=1)r[t].release();r.length=0}()},t.renderer.globalData.registerExpressionProperty=function(t){-1===r.indexOf(t)&&r.push(t)}},xN),xN;expressionsPlugin=Expressions;var ExpressionManager=function(){var ob={},Math=BMMath,window=null,document=null;function $bm_isInstanceOfArray(t){return t.constructor===Array||t.constructor===Float32Array}function isNumerable(t,e){return"number"===t||"boolean"===t||"string"===t||e instanceof Number}function $bm_neg(t){var e=typeof t;if("number"==e||"boolean"==e||t instanceof Number)return-t;if($bm_isInstanceOfArray(t)){var r,i=t.length,s=[];for(r=0;r<i;r+=1)s[r]=-t[r];return s}return t.propType?t.v:void 0}var easeInBez=BezierFactory.getBezierEasing(.333,0,.833,.833,"easeIn").get,easeOutBez=BezierFactory.getBezierEasing(.167,.167,.667,1,"easeOut").get,easeInOutBez=BezierFactory.getBezierEasing(.33,0,.667,1,"easeInOut").get;function sum(t,e){var r=typeof t,i=typeof e;if("string"==r||"string"==i)return t+e;if(isNumerable(r,t)&&isNumerable(i,e))return t+e;if($bm_isInstanceOfArray(t)&&isNumerable(i,e))return(t=t.slice(0))[0]=t[0]+e,t;if(isNumerable(r,t)&&$bm_isInstanceOfArray(e))return(e=e.slice(0))[0]=t+e[0],e;if($bm_isInstanceOfArray(t)&&$bm_isInstanceOfArray(e)){for(var s=0,a=t.length,n=e.length,o=[];s<a||s<n;)("number"==typeof t[s]||t[s]instanceof Number)&&("number"==typeof e[s]||e[s]instanceof Number)?o[s]=t[s]+e[s]:o[s]=void 0===e[s]?t[s]:t[s]||e[s],s+=1;return o}return 0}var add=sum;function sub(t,e){var r=typeof t,i=typeof e;if(isNumerable(r,t)&&isNumerable(i,e))return"string"==r&&(t=parseInt(t)),"string"==i&&(e=parseInt(e)),t-e;if($bm_isInstanceOfArray(t)&&isNumerable(i,e))return(t=t.slice(0))[0]=t[0]-e,t;if(isNumerable(r,t)&&$bm_isInstanceOfArray(e))return(e=e.slice(0))[0]=t-e[0],e;if($bm_isInstanceOfArray(t)&&$bm_isInstanceOfArray(e)){for(var s=0,a=t.length,n=e.length,o=[];s<a||s<n;)("number"==typeof t[s]||t[s]instanceof Number)&&("number"==typeof e[s]||e[s]instanceof Number)?o[s]=t[s]-e[s]:o[s]=void 0===e[s]?t[s]:t[s]||e[s],s+=1;return o}return 0}function mul(t,e){var r,i,s,a=typeof t,n=typeof e;if(isNumerable(a,t)&&isNumerable(n,e))return t*e;if($bm_isInstanceOfArray(t)&&isNumerable(n,e)){for(s=t.length,r=createTypedArray("float32",s),i=0;i<s;i+=1)r[i]=t[i]*e;return r}if(isNumerable(a,t)&&$bm_isInstanceOfArray(e)){for(s=e.length,r=createTypedArray("float32",s),i=0;i<s;i+=1)r[i]=t*e[i];return r}return 0}function div(t,e){var r,i,s,a=typeof t,n=typeof e;if(isNumerable(a,t)&&isNumerable(n,e))return t/e;if($bm_isInstanceOfArray(t)&&isNumerable(n,e)){for(s=t.length,r=createTypedArray("float32",s),i=0;i<s;i+=1)r[i]=t[i]/e;return r}if(isNumerable(a,t)&&$bm_isInstanceOfArray(e)){for(s=e.length,r=createTypedArray("float32",s),i=0;i<s;i+=1)r[i]=t/e[i];return r}return 0}function mod(t,e){return"string"==typeof t&&(t=parseInt(t)),"string"==typeof e&&(e=parseInt(e)),t%e}var $bm_sum=sum,$bm_sub=sub,$bm_mul=mul,$bm_div=div,$bm_mod=mod;function clamp(t,e,r){if(r<e){var i=r;r=e,e=i}return Math.min(Math.max(t,e),r)}function radiansToDegrees(t){return t/degToRads}var radians_to_degrees=radiansToDegrees;function degreesToRadians(t){return t*degToRads}var degrees_to_radians=radiansToDegrees,helperLengthArray=[0,0,0,0,0,0];function length(t,e){if("number"==typeof t||t instanceof Number)return e=e||0,Math.abs(t-e);e||(e=helperLengthArray);var r,i=Math.min(t.length,e.length),s=0;for(r=0;r<i;r+=1)s+=Math.pow(e[r]-t[r],2);return Math.sqrt(s)}function normalize(t){return div(t,length(t))}function rgbToHsl(t){var e,r,i=t[0],s=t[1],a=t[2],n=Math.max(i,s,a),o=Math.min(i,s,a),h=(n+o)/2;if(n==o)e=r=0;else{var p=n-o;switch(r=.5<h?p/(2-n-o):p/(n+o),n){case i:e=(s-a)/p+(s<a?6:0);break;case s:e=(a-i)/p+2;break;case a:e=(i-s)/p+4}e/=6}return[e,r,h,t[3]]}function hue2rgb(t,e,r){return r<0&&(r+=1),1<r&&(r-=1),r<1/6?t+6*(e-t)*r:r<.5?e:r<2/3?t+(e-t)*(2/3-r)*6:t}function hslToRgb(t){var e,r,i,s=t[0],a=t[1],n=t[2];if(0===a)e=r=i=n;else{var o=n<.5?n*(1+a):n+a-n*a,h=2*n-o;e=hue2rgb(h,o,s+1/3),r=hue2rgb(h,o,s),i=hue2rgb(h,o,s-1/3)}return[e,r,i,t[3]]}function linear(t,e,r,i,s){if(void 0!==i&&void 0!==s||(i=e,s=r,e=0,r=1),r<e){var a=r;r=e,e=a}if(t<=e)return i;if(r<=t)return s;var n=r===e?0:(t-e)/(r-e);if(!i.length)return i+(s-i)*n;var o,h=i.length,p=createTypedArray("float32",h);for(o=0;o<h;o+=1)p[o]=i[o]+(s[o]-i[o])*n;return p}function random(t,e){if(void 0===e&&(void 0===t?(t=0,e=1):(e=t,t=void 0)),e.length){var r,i=e.length;t||(t=createTypedArray("float32",i));var s=createTypedArray("float32",i),a=BMMath.random();for(r=0;r<i;r+=1)s[r]=t[r]+a*(e[r]-t[r]);return s}return void 0===t&&(t=0),t+BMMath.random()*(e-t)}function createPath(t,e,r,i){var s,a=t.length,n=shape_pool.newElement();n.setPathData(!!i,a);var o,h,p=[0,0];for(s=0;s<a;s+=1)o=e&&e[s]?e[s]:p,h=r&&r[s]?r[s]:p,n.setTripleAt(t[s][0],t[s][1],h[0]+t[s][0],h[1]+t[s][1],o[0]+t[s][0],o[1]+t[s][1],s,!0);return n}function initiateExpression(elem,data,property){var val=data.x,needsVelocity=/velocity(?![\w\d])/.test(val),_needsRandom=-1!==val.indexOf("random"),elemType=elem.data.ty,transform,$bm_transform,content,effect,thisProperty=property;thisProperty.valueAtTime=thisProperty.getValueAtTime,Object.defineProperty(thisProperty,"value",{get:function(){return thisProperty.v}}),elem.comp.frameDuration=1/elem.comp.globalData.frameRate,elem.comp.displayStartTime=0;var inPoint=elem.data.ip/elem.comp.globalData.frameRate,outPoint=elem.data.op/elem.comp.globalData.frameRate,width=elem.data.sw?elem.data.sw:0,height=elem.data.sh?elem.data.sh:0,name=elem.data.nm,loopIn,loop_in,loopOut,loop_out,smooth,toWorld,fromWorld,fromComp,toComp,fromCompToSurface,position,rotation,anchorPoint,scale,thisLayer,thisComp,mask,valueAtTime,velocityAtTime,__expression_functions=[],scoped_bm_rt;if(data.xf){var i,len=data.xf.length;for(i=0;i<len;i+=1)__expression_functions[i]=eval("(function(){ return "+data.xf[i]+"}())")}var expression_function=eval("[function _expression_function(){"+val+";scoped_bm_rt=$bm_rt}]")[0],numKeys=property.kf?data.k.length:0,active=!this.data||!0!==this.data.hd,wiggle=function(t,e){var r,i,s=this.pv.length?this.pv.length:1,a=createTypedArray("float32",s);var n=Math.floor(5*time);for(i=r=0;r<n;){for(i=0;i<s;i+=1)a[i]+=-e+2*e*BMMath.random();r+=1}var o=5*time,h=o-Math.floor(o),p=createTypedArray("float32",s);if(1<s){for(i=0;i<s;i+=1)p[i]=this.pv[i]+a[i]+(-e+2*e*BMMath.random())*h;return p}return this.pv+a[0]+(-e+2*e*BMMath.random())*h}.bind(this);function loopInDuration(t,e){return loopIn(t,e,!0)}function loopOutDuration(t,e){return loopOut(t,e,!0)}thisProperty.loopIn&&(loopIn=thisProperty.loopIn.bind(thisProperty),loop_in=loopIn),thisProperty.loopOut&&(loopOut=thisProperty.loopOut.bind(thisProperty),loop_out=loopOut),thisProperty.smooth&&(smooth=thisProperty.smooth.bind(thisProperty)),this.getValueAtTime&&(valueAtTime=this.getValueAtTime.bind(this)),this.getVelocityAtTime&&(velocityAtTime=this.getVelocityAtTime.bind(this));var comp=elem.comp.globalData.projectInterface.bind(elem.comp.globalData.projectInterface),time,velocity,value,text,textIndex,textTotal,selectorValue;function lookAt(t,e){var r=[e[0]-t[0],e[1]-t[1],e[2]-t[2]],i=Math.atan2(r[0],Math.sqrt(r[1]*r[1]+r[2]*r[2]))/degToRads;return[-Math.atan2(r[1],r[2])/degToRads,i,0]}function easeOut(t,e,r,i,s){return applyEase(easeOutBez,t,e,r,i,s)}function easeIn(t,e,r,i,s){return applyEase(easeInBez,t,e,r,i,s)}function ease(t,e,r,i,s){return applyEase(easeInOutBez,t,e,r,i,s)}function applyEase(t,e,r,i,s,a){void 0===s?(s=r,a=i):e=(e-r)/(i-r);var n=t(e=1<e?1:e<0?0:e);if($bm_isInstanceOfArray(s)){var o,h=s.length,p=createTypedArray("float32",h);for(o=0;o<h;o+=1)p[o]=(a[o]-s[o])*n+s[o];return p}return(a-s)*n+s}function nearestKey(t){var e,r,i,s=data.k.length;if(data.k.length&&"number"!=typeof data.k[0])if(r=-1,(t*=elem.comp.globalData.frameRate)<data.k[0].t)r=1,i=data.k[0].t;else{for(e=0;e<s-1;e+=1){if(t===data.k[e].t){r=e+1,i=data.k[e].t;break}if(t>data.k[e].t&&t<data.k[e+1].t){i=t-data.k[e].t>data.k[e+1].t-t?(r=e+2,data.k[e+1].t):(r=e+1,data.k[e].t);break}}-1===r&&(r=e+1,i=data.k[e].t)}else i=r=0;var a={};return a.index=r,a.time=i/elem.comp.globalData.frameRate,a}function key(t){var e,r,i,s;if(!data.k.length||"number"==typeof data.k[0])throw new Error("The property has no keyframe at index "+t);for(t-=1,e={time:data.k[t].t/elem.comp.globalData.frameRate,value:[]},i=(s=t!==data.k.length-1||data.k[t].h?data.k[t].s:data.k[t].s||0===data.k[t].s?data.k[t-1].s:data.k[t].e).length,r=0;r<i;r+=1)e[r]=s[r],e.value[r]=s[r];return e}function framesToTime(t,e){return e||(e=elem.comp.globalData.frameRate),t/e}function timeToFrames(t,e){return t||0===t||(t=time),e||(e=elem.comp.globalData.frameRate),t*e}function seedRandom(t){BMMath.seedrandom(randSeed+t)}function sourceRectAtTime(){return elem.sourceRectAtTime()}function substring(t,e){return"string"==typeof value?void 0===e?value.substring(t):value.substring(t,e):""}function substr(t,e){return"string"==typeof value?void 0===e?value.substr(t):value.substr(t,e):""}var index=elem.data.ind,hasParent=!(!elem.hierarchy||!elem.hierarchy.length),parent,randSeed=Math.floor(1e6*Math.random()),globalData=elem.globalData;function executeExpression(t){return value=t,_needsRandom&&seedRandom(randSeed),this.frameExpressionId===elem.globalData.frameId&&"textSelector"!==this.propType?value:("textSelector"===this.propType&&(textIndex=this.textIndex,textTotal=this.textTotal,selectorValue=this.selectorValue),thisLayer||(text=elem.layerInterface.text,thisLayer=elem.layerInterface,thisComp=elem.comp.compInterface,toWorld=thisLayer.toWorld.bind(thisLayer),fromWorld=thisLayer.fromWorld.bind(thisLayer),fromComp=thisLayer.fromComp.bind(thisLayer),toComp=thisLayer.toComp.bind(thisLayer),mask=thisLayer.mask?thisLayer.mask.bind(thisLayer):null,fromCompToSurface=fromComp),transform||(transform=elem.layerInterface("ADBE Transform Group"),($bm_transform=transform)&&(anchorPoint=transform.anchorPoint)),4!==elemType||content||(content=thisLayer("ADBE Root Vectors Group")),effect||(effect=thisLayer(4)),(hasParent=!(!elem.hierarchy||!elem.hierarchy.length))&&!parent&&(parent=elem.hierarchy[0].layerInterface),time=this.comp.renderedFrame/this.comp.globalData.frameRate,needsVelocity&&(velocity=velocityAtTime(time)),expression_function(),this.frameExpressionId=elem.globalData.frameId,"shape"===scoped_bm_rt.propType&&(scoped_bm_rt=scoped_bm_rt.v),scoped_bm_rt)}return executeExpression}return ob.initiateExpression=initiateExpression,ob}(),expressionHelpers={searchExpressions:function(t,e,r){e.x&&(r.k=!0,r.x=!0,r.initiateExpression=ExpressionManager.initiateExpression,r.effectsSequence.push(r.initiateExpression(t,e,r).bind(r)))},getSpeedAtTime:function(t){var e=this.getValueAtTime(t),r=this.getValueAtTime(t+-.01),i=0;if(e.length){var s;for(s=0;s<e.length;s+=1)i+=Math.pow(r[s]-e[s],2);i=100*Math.sqrt(i)}else i=0;return i},getVelocityAtTime:function(t){if(void 0!==this.vel)return this.vel;var e,r,i=this.getValueAtTime(t),s=this.getValueAtTime(t+-.001);if(i.length)for(e=createTypedArray("float32",i.length),r=0;r<i.length;r+=1)e[r]=(s[r]-i[r])/-.001;else e=(s-i)/-.001;return e},getValueAtTime:function(t){return t*=this.elem.globalData.frameRate,(t-=this.offsetTime)!==this._cachingAtTime.lastFrame&&(this._cachingAtTime.lastIndex=this._cachingAtTime.lastFrame<t?this._cachingAtTime.lastIndex:0,this._cachingAtTime.value=this.interpolateValue(t,this._cachingAtTime),this._cachingAtTime.lastFrame=t),this._cachingAtTime.value},getStaticValueAtTime:function(){return this.pv},setGroupProperty:function(t){this.propertyGroup=t}};!function(){function o(t,e,r){if(!this.k||!this.keyframes)return this.pv;t=t?t.toLowerCase():"";var i,s,a,n,o,h=this.comp.renderedFrame,p=this.keyframes,l=p[p.length-1].t;if(h<=l)return this.pv;if(r?s=l-(i=e?Math.abs(l-elem.comp.globalData.frameRate*e):Math.max(0,l-this.elem.data.ip)):((!e||e>p.length-1)&&(e=p.length-1),i=l-(s=p[p.length-1-e].t)),"pingpong"===t){if(Math.floor((h-s)/i)%2!=0)return this.getValueAtTime((i-(h-s)%i+s)/this.comp.globalData.frameRate,0)}else{if("offset"===t){var m=this.getValueAtTime(s/this.comp.globalData.frameRate,0),f=this.getValueAtTime(l/this.comp.globalData.frameRate,0),c=this.getValueAtTime(((h-s)%i+s)/this.comp.globalData.frameRate,0),d=Math.floor((h-s)/i);if(this.pv.length){for(n=(o=new Array(m.length)).length,a=0;a<n;a+=1)o[a]=(f[a]-m[a])*d+c[a];return o}return(f-m)*d+c}if("continue"===t){var u=this.getValueAtTime(l/this.comp.globalData.frameRate,0),y=this.getValueAtTime((l-.001)/this.comp.globalData.frameRate,0);if(this.pv.length){for(n=(o=new Array(u.length)).length,a=0;a<n;a+=1)o[a]=u[a]+(u[a]-y[a])*((h-l)/this.comp.globalData.frameRate)/5e-4;return o}return u+(h-l)/.001*(u-y)}}return this.getValueAtTime(((h-s)%i+s)/this.comp.globalData.frameRate,0)}function h(t,e,r){if(!this.k)return this.pv;t=t?t.toLowerCase():"";var i,s,a,n,o,h=this.comp.renderedFrame,p=this.keyframes,l=p[0].t;if(l<=h)return this.pv;if(r?s=l+(i=e?Math.abs(elem.comp.globalData.frameRate*e):Math.max(0,this.elem.data.op-l)):((!e||e>p.length-1)&&(e=p.length-1),i=(s=p[e].t)-l),"pingpong"===t){if(Math.floor((l-h)/i)%2==0)return this.getValueAtTime(((l-h)%i+l)/this.comp.globalData.frameRate,0)}else{if("offset"===t){var m=this.getValueAtTime(l/this.comp.globalData.frameRate,0),f=this.getValueAtTime(s/this.comp.globalData.frameRate,0),c=this.getValueAtTime((i-(l-h)%i+l)/this.comp.globalData.frameRate,0),d=Math.floor((l-h)/i)+1;if(this.pv.length){for(n=(o=new Array(m.length)).length,a=0;a<n;a+=1)o[a]=c[a]-(f[a]-m[a])*d;return o}return c-(f-m)*d}if("continue"===t){var u=this.getValueAtTime(l/this.comp.globalData.frameRate,0),y=this.getValueAtTime((l+.001)/this.comp.globalData.frameRate,0);if(this.pv.length){for(n=(o=new Array(u.length)).length,a=0;a<n;a+=1)o[a]=u[a]+(u[a]-y[a])*(l-h)/.001;return o}return u+(u-y)*(l-h)/.001}}return this.getValueAtTime((i-(l-h)%i+l)/this.comp.globalData.frameRate,0)}function p(t,e){if(!this.k)return this.pv;if(t=.5*(t||.4),(e=Math.floor(e||5))<=1)return this.pv;var r,i,s=this.comp.renderedFrame/this.comp.globalData.frameRate,a=s-t,n=1<e?(s+t-a)/(e-1):1,o=0,h=0;for(r=this.pv.length?createTypedArray("float32",this.pv.length):0;o<e;){if(i=this.getValueAtTime(a+o*n),this.pv.length)for(h=0;h<this.pv.length;h+=1)r[h]+=i[h];else r+=i;o+=1}if(this.pv.length)for(h=0;h<this.pv.length;h+=1)r[h]/=e;else r/=e;return r}var s=TransformPropertyFactory.getTransformProperty;TransformPropertyFactory.getTransformProperty=function(t,e,r){var i=s(t,e,r);return i.dynamicProperties.length?i.getValueAtTime=function(t){console.warn("Transform at time not supported")}.bind(i):i.getValueAtTime=function(t){}.bind(i),i.setGroupProperty=expressionHelpers.setGroupProperty,i};var l=PropertyFactory.getProp;PropertyFactory.getProp=function(t,e,r,i,s){var a=l(t,e,r,i,s);a.kf?a.getValueAtTime=expressionHelpers.getValueAtTime.bind(a):a.getValueAtTime=expressionHelpers.getStaticValueAtTime.bind(a),a.setGroupProperty=expressionHelpers.setGroupProperty,a.loopOut=o,a.loopIn=h,a.smooth=p,a.getVelocityAtTime=expressionHelpers.getVelocityAtTime.bind(a),a.getSpeedAtTime=expressionHelpers.getSpeedAtTime.bind(a),a.numKeys=1===e.a?e.k.length:0,a.propertyIndex=e.ix;var n=0;return 0!==r&&(n=createTypedArray("float32",1===e.a?e.k[0].s.length:e.k.length)),a._cachingAtTime={lastFrame:initialDefaultFrame,lastIndex:0,value:n},expressionHelpers.searchExpressions(t,e,a),a.k&&s.addDynamicProperty(a),a};var t=ShapePropertyFactory.getConstructorFunction(),e=ShapePropertyFactory.getKeyframedConstructorFunction();function r(){}r.prototype={vertices:function(t,e){this.k&&this.getValue();var r=this.v;void 0!==e&&(r=this.getValueAtTime(e,0));var i,s=r._length,a=r[t],n=r.v,o=createSizedArray(s);for(i=0;i<s;i+=1)o[i]="i"===t||"o"===t?[a[i][0]-n[i][0],a[i][1]-n[i][1]]:[a[i][0],a[i][1]];return o},points:function(t){return this.vertices("v",t)},inTangents:function(t){return this.vertices("i",t)},outTangents:function(t){return this.vertices("o",t)},isClosed:function(){return this.v.c},pointOnPath:function(t,e){var r=this.v;void 0!==e&&(r=this.getValueAtTime(e,0)),this._segmentsLength||(this._segmentsLength=bez.getSegmentsLength(r));for(var i,s=this._segmentsLength,a=s.lengths,n=s.totalLength*t,o=0,h=a.length,p=0;o<h;){if(p+a[o].addedLength>n){var l=o,m=r.c&&o===h-1?0:o+1,f=(n-p)/a[o].addedLength;i=bez.getPointInSegment(r.v[l],r.v[m],r.o[l],r.i[m],f,a[o]);break}p+=a[o].addedLength,o+=1}return i||(i=r.c?[r.v[0][0],r.v[0][1]]:[r.v[r._length-1][0],r.v[r._length-1][1]]),i},vectorOnPath:function(t,e,r){t=1==t?this.v.c?0:.999:t;var i=this.pointOnPath(t,e),s=this.pointOnPath(t+.001,e),a=s[0]-i[0],n=s[1]-i[1],o=Math.sqrt(Math.pow(a,2)+Math.pow(n,2));return"tangent"===r?[a/o,n/o]:[-n/o,a/o]},tangentOnPath:function(t,e){return this.vectorOnPath(t,e,"tangent")},normalOnPath:function(t,e){return this.vectorOnPath(t,e,"normal")},setGroupProperty:expressionHelpers.setGroupProperty,getValueAtTime:expressionHelpers.getStaticValueAtTime},extendPrototype([r],t),extendPrototype([r],e),e.prototype.getValueAtTime=function(t){return this._cachingAtTime||(this._cachingAtTime={shapeValue:shape_pool.clone(this.pv),lastIndex:0,lastTime:initialDefaultFrame}),t*=this.elem.globalData.frameRate,(t-=this.offsetTime)!==this._cachingAtTime.lastTime&&(this._cachingAtTime.lastIndex=this._cachingAtTime.lastTime<t?this._caching.lastIndex:0,this._cachingAtTime.lastTime=t,this.interpolateShape(t,this._cachingAtTime.shapeValue,this._cachingAtTime)),this._cachingAtTime.shapeValue},e.prototype.initiateExpression=ExpressionManager.initiateExpression;var n=ShapePropertyFactory.getShapeProp;ShapePropertyFactory.getShapeProp=function(t,e,r,i,s){var a=n(t,e,r,i,s);return a.propertyIndex=e.ix,a.lock=!1,3===r?expressionHelpers.searchExpressions(t,e.pt,a):4===r&&expressionHelpers.searchExpressions(t,e.ks,a),a.k&&t.addDynamicProperty(a),a}}(),TextProperty.prototype.getExpressionValue=function(t,e){var r=this.calculateExpression(e);if(t.t===r)return t;var i={};return this.copyData(i,t),i.t=r.toString(),i.__complete=!1,i},TextProperty.prototype.searchProperty=function(){var t=this.searchKeyframes(),e=this.searchExpressions();return this.kf=t||e,this.kf},TextProperty.prototype.searchExpressions=function(){if(this.data.d.x)return this.calculateExpression=ExpressionManager.initiateExpression.bind(this)(this.elem,this.data.d,this),this.addEffect(this.getExpressionValue.bind(this)),!0};var ShapeExpressionInterface=function(t,e,r){var i;function s(t){if("number"==typeof t)return i[t-1];for(var e=0,r=i.length;e<r;){if(i[e]._name===t)return i[e];e+=1}}return s.propertyGroup=r,i=DT(t,e,s),s.numProperties=i.length,s};function DT(t,e,r){var i,s=[],a=t?t.length:0;for(i=0;i<a;i+=1)"gr"==t[i].ty?s.push(FT(t[i],e[i],r)):"fl"==t[i].ty?s.push(GT(t[i],e[i],r)):"st"==t[i].ty?s.push(HT(t[i],e[i],r)):"tm"==t[i].ty?s.push(IT(t[i],e[i],r)):"tr"==t[i].ty||("el"==t[i].ty?s.push(KT(t[i],e[i],r)):"sr"==t[i].ty?s.push(LT(t[i],e[i],r)):"sh"==t[i].ty?s.push(PT(t[i],e[i],r)):"rc"==t[i].ty?s.push(MT(t[i],e[i],r)):"rd"==t[i].ty?s.push(NT(t[i],e[i],r)):"rp"==t[i].ty&&s.push(OT(t[i],e[i],r)));return s}function FT(t,e,r){var i=function(t){switch(t){case"ADBE Vectors Group":case"Contents":case 2:return i.content;default:return i.transform}};i.propertyGroup=function(t){return 1===t?i:r(t-1)};var s=function(t,e,r){function i(t){for(var e=0,r=s.length;e<r;){if(s[e]._name===t||s[e].mn===t||s[e].propertyIndex===t||s[e].ix===t||s[e].ind===t)return s[e];e+=1}if("number"==typeof t)return s[t-1]}var s;return i.propertyGroup=function(t){return 1===t?i:r(t-1)},s=DT(t.it,e.it,i.propertyGroup),i.numProperties=s.length,i.propertyIndex=t.cix,i._name=t.nm,i}(t,e,i.propertyGroup),a=function(e,t,r){function i(t){return 1==t?s:r(--t)}t.transform.mProps.o.setGroupProperty(i),t.transform.mProps.p.setGroupProperty(i),t.transform.mProps.a.setGroupProperty(i),t.transform.mProps.s.setGroupProperty(i),t.transform.mProps.r.setGroupProperty(i),t.transform.mProps.sk&&(t.transform.mProps.sk.setGroupProperty(i),t.transform.mProps.sa.setGroupProperty(i));function s(t){return e.a.ix===t||"Anchor Point"===t?s.anchorPoint:e.o.ix===t||"Opacity"===t?s.opacity:e.p.ix===t||"Position"===t?s.position:e.r.ix===t||"Rotation"===t||"ADBE Vector Rotation"===t?s.rotation:e.s.ix===t||"Scale"===t?s.scale:e.sk&&e.sk.ix===t||"Skew"===t?s.skew:e.sa&&e.sa.ix===t||"Skew Axis"===t?s.skewAxis:void 0}return t.transform.op.setGroupProperty(i),Object.defineProperties(s,{opacity:{get:ExpressionPropertyInterface(t.transform.mProps.o)},position:{get:ExpressionPropertyInterface(t.transform.mProps.p)},anchorPoint:{get:ExpressionPropertyInterface(t.transform.mProps.a)},scale:{get:ExpressionPropertyInterface(t.transform.mProps.s)},rotation:{get:ExpressionPropertyInterface(t.transform.mProps.r)},skew:{get:ExpressionPropertyInterface(t.transform.mProps.sk)},skewAxis:{get:ExpressionPropertyInterface(t.transform.mProps.sa)},_name:{value:e.nm}}),s.ty="tr",s.mn=e.mn,s.propertyGroup=r,s}(t.it[t.it.length-1],e.it[e.it.length-1],i.propertyGroup);return i.content=s,i.transform=a,Object.defineProperty(i,"_name",{get:function(){return t.nm}}),i.numProperties=t.np,i.propertyIndex=t.ix,i.nm=t.nm,i.mn=t.mn,i}function GT(t,e,r){function i(t){return"Color"===t||"color"===t?i.color:"Opacity"===t||"opacity"===t?i.opacity:void 0}return Object.defineProperties(i,{color:{get:ExpressionPropertyInterface(e.c)},opacity:{get:ExpressionPropertyInterface(e.o)},_name:{value:t.nm},mn:{value:t.mn}}),e.c.setGroupProperty(r),e.o.setGroupProperty(r),i}function HT(t,e,r){function i(t){return 1===t?ob:r(t-1)}function s(t){return 1===t?h:i(t-1)}var a,n,o=t.d?t.d.length:0,h={};for(a=0;a<o;a+=1)n=a,Object.defineProperty(h,t.d[n].nm,{get:ExpressionPropertyInterface(e.d.dataProps[n].p)}),e.d.dataProps[a].p.setGroupProperty(s);function p(t){return"Color"===t||"color"===t?p.color:"Opacity"===t||"opacity"===t?p.opacity:"Stroke Width"===t||"stroke width"===t?p.strokeWidth:void 0}return Object.defineProperties(p,{color:{get:ExpressionPropertyInterface(e.c)},opacity:{get:ExpressionPropertyInterface(e.o)},strokeWidth:{get:ExpressionPropertyInterface(e.w)},dash:{get:function(){return h}},_name:{value:t.nm},mn:{value:t.mn}}),e.c.setGroupProperty(i),e.o.setGroupProperty(i),e.w.setGroupProperty(i),p}function IT(e,t,r){function i(t){return 1==t?s:r(--t)}function s(t){return t===e.e.ix||"End"===t||"end"===t?s.end:t===e.s.ix?s.start:t===e.o.ix?s.offset:void 0}return s.propertyIndex=e.ix,t.s.setGroupProperty(i),t.e.setGroupProperty(i),t.o.setGroupProperty(i),s.propertyIndex=e.ix,s.propertyGroup=r,Object.defineProperties(s,{start:{get:ExpressionPropertyInterface(t.s)},end:{get:ExpressionPropertyInterface(t.e)},offset:{get:ExpressionPropertyInterface(t.o)},_name:{value:e.nm}}),s.mn=e.mn,s}function KT(e,t,r){function i(t){return 1==t?a:r(--t)}a.propertyIndex=e.ix;var s="tm"===t.sh.ty?t.sh.prop:t.sh;function a(t){return e.p.ix===t?a.position:e.s.ix===t?a.size:void 0}return s.s.setGroupProperty(i),s.p.setGroupProperty(i),Object.defineProperties(a,{size:{get:ExpressionPropertyInterface(s.s)},position:{get:ExpressionPropertyInterface(s.p)},_name:{value:e.nm}}),a.mn=e.mn,a}function LT(e,t,r){function i(t){return 1==t?a:r(--t)}var s="tm"===t.sh.ty?t.sh.prop:t.sh;function a(t){return e.p.ix===t?a.position:e.r.ix===t?a.rotation:e.pt.ix===t?a.points:e.or.ix===t||"ADBE Vector Star Outer Radius"===t?a.outerRadius:e.os.ix===t?a.outerRoundness:!e.ir||e.ir.ix!==t&&"ADBE Vector Star Inner Radius"!==t?e.is&&e.is.ix===t?a.innerRoundness:void 0:a.innerRadius}return a.propertyIndex=e.ix,s.or.setGroupProperty(i),s.os.setGroupProperty(i),s.pt.setGroupProperty(i),s.p.setGroupProperty(i),s.r.setGroupProperty(i),e.ir&&(s.ir.setGroupProperty(i),s.is.setGroupProperty(i)),Object.defineProperties(a,{position:{get:ExpressionPropertyInterface(s.p)},rotation:{get:ExpressionPropertyInterface(s.r)},points:{get:ExpressionPropertyInterface(s.pt)},outerRadius:{get:ExpressionPropertyInterface(s.or)},outerRoundness:{get:ExpressionPropertyInterface(s.os)},innerRadius:{get:ExpressionPropertyInterface(s.ir)},innerRoundness:{get:ExpressionPropertyInterface(s.is)},_name:{value:e.nm}}),a.mn=e.mn,a}function MT(e,t,r){function i(t){return 1==t?a:r(--t)}var s="tm"===t.sh.ty?t.sh.prop:t.sh;function a(t){return e.p.ix===t?a.position:e.r.ix===t?a.roundness:e.s.ix===t||"Size"===t||"ADBE Vector Rect Size"===t?a.size:void 0}return a.propertyIndex=e.ix,s.p.setGroupProperty(i),s.s.setGroupProperty(i),s.r.setGroupProperty(i),Object.defineProperties(a,{position:{get:ExpressionPropertyInterface(s.p)},roundness:{get:ExpressionPropertyInterface(s.r)},size:{get:ExpressionPropertyInterface(s.s)},_name:{value:e.nm}}),a.mn=e.mn,a}function NT(e,t,r){var i=t;function s(t){if(e.r.ix===t||"Round Corners 1"===t)return s.radius}return s.propertyIndex=e.ix,i.rd.setGroupProperty(function(t){return 1==t?s:r(--t)}),Object.defineProperties(s,{radius:{get:ExpressionPropertyInterface(i.rd)},_name:{value:e.nm}}),s.mn=e.mn,s}function OT(e,t,r){function i(t){return 1==t?a:r(--t)}var s=t;function a(t){return e.c.ix===t||"Copies"===t?a.copies:e.o.ix===t||"Offset"===t?a.offset:void 0}return a.propertyIndex=e.ix,s.c.setGroupProperty(i),s.o.setGroupProperty(i),Object.defineProperties(a,{copies:{get:ExpressionPropertyInterface(s.c)},offset:{get:ExpressionPropertyInterface(s.o)},_name:{value:e.nm}}),a.mn=e.mn,a}function PT(t,e,r){var i=e.sh;function s(t){if("Shape"===t||"shape"===t||"Path"===t||"path"===t||"ADBE Vector Shape"===t||2===t)return s.path}return i.setGroupProperty(function(t){return 1==t?s:r(--t)}),Object.defineProperties(s,{path:{get:function(){return i.k&&i.getValue(),i}},shape:{get:function(){return i.k&&i.getValue(),i}},_name:{value:t.nm},ix:{value:t.ix},mn:{value:t.mn}}),s}var TextExpressionInterface=function(e){var r;function t(){}return Object.defineProperty(t,"sourceText",{get:function(){e.textProperty.getValue();var t=e.textProperty.currentData.t;return void 0!==t&&(e.textProperty.currentData.t=void 0,(r=new String(t)).value=t||new String(t)),r}}),t},LayerExpressionInterface=function(e){var r;function i(t){switch(t){case"ADBE Root Vectors Group":case"Contents":case 2:return i.shapeInterface;case 1:case 6:case"Transform":case"transform":case"ADBE Transform Group":return r;case 4:case"ADBE Effect Parade":case"effects":case"Effects":return i.effect}}i.toWorld=_V,i.fromWorld=aW,i.toComp=_V,i.fromComp=bW,i.sampleImage=cW,i.sourceRectAtTime=e.sourceRectAtTime.bind(e);var t=getDescriptor(r=TransformExpressionInterface((i._elem=e).finalTransform.mProp),"anchorPoint");return Object.defineProperties(i,{hasParent:{get:function(){return e.hierarchy.length}},parent:{get:function(){return e.hierarchy[0].layerInterface}},rotation:getDescriptor(r,"rotation"),scale:getDescriptor(r,"scale"),position:getDescriptor(r,"position"),opacity:getDescriptor(r,"opacity"),anchorPoint:t,anchor_point:t,transform:{get:function(){return r}},active:{get:function(){return e.isInRange}}}),i.startTime=e.data.st,i.index=e.data.ind,i.source=e.data.refId,i.height=0===e.data.ty?e.data.h:100,i.width=0===e.data.ty?e.data.w:100,i.inPoint=e.data.ip/e.comp.globalData.frameRate,i.outPoint=e.data.op/e.comp.globalData.frameRate,i._name=e.data.nm,i.registerMaskInterface=function(t){i.mask=new MaskManagerInterface(t,e)},i.registerEffectsInterface=function(t){i.effect=t},i};function _V(t,e){var r=new Matrix;if(r.reset(),this._elem.finalTransform.mProp.applyToMatrix(r),this._elem.hierarchy&&this._elem.hierarchy.length){var i,s=this._elem.hierarchy.length;for(i=0;i<s;i+=1)this._elem.hierarchy[i].finalTransform.mProp.applyToMatrix(r);return r.applyToPointArray(t[0],t[1],t[2]||0)}return r.applyToPointArray(t[0],t[1],t[2]||0)}function aW(t,e){var r=new Matrix;if(r.reset(),this._elem.finalTransform.mProp.applyToMatrix(r),this._elem.hierarchy&&this._elem.hierarchy.length){var i,s=this._elem.hierarchy.length;for(i=0;i<s;i+=1)this._elem.hierarchy[i].finalTransform.mProp.applyToMatrix(r);return r.inversePoint(t)}return r.inversePoint(t)}function bW(t){var e=new Matrix;if(e.reset(),this._elem.finalTransform.mProp.applyToMatrix(e),this._elem.hierarchy&&this._elem.hierarchy.length){var r,i=this._elem.hierarchy.length;for(r=0;r<i;r+=1)this._elem.hierarchy[r].finalTransform.mProp.applyToMatrix(e);return e.inversePoint(t)}return e.inversePoint(t)}function cW(){return[1,1,1,1]}var CompExpressionInterface=function(i){function t(t){for(var e=0,r=i.layers.length;e<r;){if(i.layers[e].nm===t||i.layers[e].ind===t)return i.elements[e].layerInterface;e+=1}return null}return Object.defineProperty(t,"_name",{value:i.data.nm}),(t.layer=t).pixelAspect=1,t.height=i.data.h||i.globalData.compSize.h,t.width=i.data.w||i.globalData.compSize.w,t.pixelAspect=1,t.frameDuration=1/i.globalData.frameRate,t.displayStartTime=0,t.numLayers=i.layers.length,t},TransformExpressionInterface=function(t){function e(t){switch(t){case"scale":case"Scale":case"ADBE Scale":case 6:return e.scale;case"rotation":case"Rotation":case"ADBE Rotation":case"ADBE Rotate Z":case 10:return e.rotation;case"ADBE Rotate X":return e.xRotation;case"ADBE Rotate Y":return e.yRotation;case"position":case"Position":case"ADBE Position":case 2:return e.position;case"ADBE Position_0":return e.xPosition;case"ADBE Position_1":return e.yPosition;case"ADBE Position_2":return e.zPosition;case"anchorPoint":case"AnchorPoint":case"Anchor Point":case"ADBE AnchorPoint":case 1:return e.anchorPoint;case"opacity":case"Opacity":case 11:return e.opacity}}if(Object.defineProperty(e,"rotation",{get:ExpressionPropertyInterface(t.r||t.rz)}),Object.defineProperty(e,"zRotation",{get:ExpressionPropertyInterface(t.rz||t.r)}),Object.defineProperty(e,"xRotation",{get:ExpressionPropertyInterface(t.rx)}),Object.defineProperty(e,"yRotation",{get:ExpressionPropertyInterface(t.ry)}),Object.defineProperty(e,"scale",{get:ExpressionPropertyInterface(t.s)}),t.p)var r=ExpressionPropertyInterface(t.p);return Object.defineProperty(e,"position",{get:function(){return t.p?r():[t.px.v,t.py.v,t.pz?t.pz.v:0]}}),Object.defineProperty(e,"xPosition",{get:ExpressionPropertyInterface(t.px)}),Object.defineProperty(e,"yPosition",{get:ExpressionPropertyInterface(t.py)}),Object.defineProperty(e,"zPosition",{get:ExpressionPropertyInterface(t.pz)}),Object.defineProperty(e,"anchorPoint",{get:ExpressionPropertyInterface(t.a)}),Object.defineProperty(e,"opacity",{get:ExpressionPropertyInterface(t.o)}),Object.defineProperty(e,"skew",{get:ExpressionPropertyInterface(t.sk)}),Object.defineProperty(e,"skewAxis",{get:ExpressionPropertyInterface(t.sa)}),Object.defineProperty(e,"orientation",{get:ExpressionPropertyInterface(t.or)}),e},ProjectInterface=function(){function t(t){for(var e=0,r=this.compositions.length;e<r;){if(this.compositions[e].data&&this.compositions[e].data.nm===t)return this.compositions[e].prepareFrame&&this.compositions[e].data.xt&&this.compositions[e].prepareFrame(this.currentFrame),this.compositions[e].compInterface;e+=1}}return t.compositions=[],t.currentFrame=0,t.registerComposition=LW,t};function LW(t){this.compositions.push(t)}var EffectsExpressionInterface={createEffectsInterface:function(s,t){if(s.effectsManager){var e,a=[],r=s.data.ef,i=s.effectsManager.effectElements.length;for(e=0;e<i;e+=1)a.push(TW(r[e],s.effectsManager.effectElements[e],t,s));return function(t){for(var e=s.data.ef||[],r=0,i=e.length;r<i;){if(t===e[r].nm||t===e[r].mn||t===e[r].ix)return a[r];r+=1}}}}};function TW(s,t,e,r){var i,a=[],n=s.ef.length;for(i=0;i<n;i+=1)5===s.ef[i].ty?a.push(TW(s.ef[i],t.effectElements[i],t.effectElements[i].propertyGroup,r)):a.push(UW(t.effectElements[i],s.ef[i].ty,r,o));function o(t){return 1===t?h:e(t-1)}var h=function(t){for(var e=s.ef,r=0,i=e.length;r<i;){if(t===e[r].nm||t===e[r].mn||t===e[r].ix)return 5===e[r].ty?a[r]:a[r]();r+=1}return a[0]()};return h.propertyGroup=o,"ADBE Color Control"===s.mn&&Object.defineProperty(h,"color",{get:function(){return a[0]()}}),Object.defineProperty(h,"numProperties",{get:function(){return s.np}}),h.active=h.enabled=0!==s.en,h}function UW(t,e,r,i){var s=ExpressionPropertyInterface(t.p);return t.p.setGroupProperty&&t.p.setGroupProperty(i),function(){return 10===e?r.comp.compInterface(t.p.v):s()}}var MaskManagerInterface=function(){function a(t,e){this._mask=t,this._data=e}Object.defineProperty(a.prototype,"maskPath",{get:function(){return this._mask.prop.k&&this._mask.prop.getValue(),this._mask.prop}});return function(e,t){var r,i=createSizedArray(e.viewData.length),s=e.viewData.length;for(r=0;r<s;r+=1)i[r]=new a(e.viewData[r],e.masksProperties[r]);return function(t){for(r=0;r<s;){if(e.masksProperties[r].nm===t)return i[r];r+=1}}}}(),ExpressionPropertyInterface=(KX={pv:0,v:0,mult:1},LX={pv:[0,0,0],v:[0,0,0],mult:1},function(t){return t?"unidimensional"===t.propType?function(t){t&&"pv"in t||(t=KX);var e=1/t.mult,r=t.pv*e,i=new Number(r);return i.value=r,MX(i,t,"unidimensional"),function(){return t.k&&t.getValue(),r=t.v*e,i.value!==r&&((i=new Number(r)).value=r,MX(i,t,"unidimensional")),i}}(t):function(e){e&&"pv"in e||(e=LX);var r=1/e.mult,i=e.pv.length,s=createTypedArray("float32",i),a=createTypedArray("float32",i);return s.value=a,MX(s,e,"multidimensional"),function(){e.k&&e.getValue();for(var t=0;t<i;t+=1)s[t]=a[t]=e.v[t]*r;return s}}(t):PX}),KX,LX,fY,gY;function MX(i,s,a){Object.defineProperty(i,"velocity",{get:function(){return s.getVelocityAtTime(s.comp.currentFrame)}}),i.numKeys=s.keyframes?s.keyframes.length:0,i.key=function(t){if(i.numKeys){var e="";e="s"in s.keyframes[t-1]?s.keyframes[t-1].s:"e"in s.keyframes[t-2]?s.keyframes[t-2].e:s.keyframes[t-2].s;var r="unidimensional"===a?new Number(e):Object.assign({},e);return r.time=s.keyframes[t-1].t/s.elem.comp.globalData.frameRate,r}return 0},i.valueAtTime=s.getValueAtTime,i.speedAtTime=s.getSpeedAtTime,i.velocityAtTime=s.getVelocityAtTime,i.propertyGroup=s.propertyGroup}function PX(){return KX}function hY(t,e){return this.textIndex=t+1,this.textTotal=e,this.v=this.getValue()*this.mult,this.v}function SliderEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function AngleEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function ColorEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,1,0,r)}function PointEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,1,0,r)}function LayerIndexEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function MaskIndexEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function CheckboxEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function NoValueEffect(){this.p={}}function EffectsManager(t,e){var r=t.ef||[];this.effectElements=[];var i,s,a=r.length;for(i=0;i<a;i++)s=new GroupEffect(r[i],e),this.effectElements.push(s)}function GroupEffect(t,e){this.init(t,e)}fY=function(t,e){this.pv=1,this.comp=t.comp,this.elem=t,this.mult=.01,this.propType="textSelector",this.textTotal=e.totalChars,this.selectorValue=100,this.lastValue=[1,1,1],this.k=!0,this.x=!0,this.getValue=ExpressionManager.initiateExpression.bind(this)(t,e,this),this.getMult=hY,this.getVelocityAtTime=expressionHelpers.getVelocityAtTime,this.kf?this.getValueAtTime=expressionHelpers.getValueAtTime.bind(this):this.getValueAtTime=expressionHelpers.getStaticValueAtTime.bind(this),this.setGroupProperty=expressionHelpers.setGroupProperty},gY=TextSelectorProp.getTextSelectorProp,TextSelectorProp.getTextSelectorProp=function(t,e,r){return 1===e.t?new fY(t,e,r):gY(t,e,r)},extendPrototype([DynamicPropertyContainer],GroupEffect),GroupEffect.prototype.getValue=GroupEffect.prototype.iterateDynamicProperties,GroupEffect.prototype.init=function(t,e){this.data=t,this.effectElements=[],this.initDynamicPropertyContainer(e);var r,i,s=this.data.ef.length,a=this.data.ef;for(r=0;r<s;r+=1){switch(i=null,a[r].ty){case 0:i=new SliderEffect(a[r],e,this);break;case 1:i=new AngleEffect(a[r],e,this);break;case 2:i=new ColorEffect(a[r],e,this);break;case 3:i=new PointEffect(a[r],e,this);break;case 4:case 7:i=new CheckboxEffect(a[r],e,this);break;case 10:i=new LayerIndexEffect(a[r],e,this);break;case 11:i=new MaskIndexEffect(a[r],e,this);break;case 5:i=new EffectsManager(a[r],e,this);break;default:i=new NoValueEffect(a[r],e,this)}i&&this.effectElements.push(i)}};var lottiejs={},_isFrozen=!1;function loadAnimation(t){return animationManager.loadAnimation(t)}function setQuality(t){if("string"==typeof t)switch(t){case"high":defaultCurveSegments=200;break;case"medium":defaultCurveSegments=50;break;case"low":defaultCurveSegments=10}else!isNaN(t)&&1<t&&(defaultCurveSegments=t);roundValues(!(50<=defaultCurveSegments))}lottiejs.play=animationManager.play,lottiejs.pause=animationManager.pause,lottiejs.togglePause=animationManager.togglePause,lottiejs.setSpeed=animationManager.setSpeed,lottiejs.setDirection=animationManager.setDirection,lottiejs.stop=animationManager.stop,lottiejs.registerAnimation=animationManager.registerAnimation,lottiejs.loadAnimation=loadAnimation,lottiejs.resize=animationManager.resize,lottiejs.goToAndStop=animationManager.goToAndStop,lottiejs.destroy=animationManager.destroy,lottiejs.setQuality=setQuality,lottiejs.freeze=animationManager.freeze,lottiejs.unfreeze=animationManager.unfreeze,lottiejs.getRegisteredAnimations=animationManager.getRegisteredAnimations,lottiejs.version="5.5.2";var renderer="";return lottiejs}({}),currentAnimation=null;onmessage=function(t){if(t&&t.data){var e=null;if(currentAnimation)e=currentAnimation.renderer.canvasContext.canvas;else{if(!t.data.canvas)return;e=t.data.canvas}if(t.data.drawSize&&(e.height=t.data.drawSize.height,e.width=t.data.drawSize.width,currentAnimation&¤tAnimation.resize()),!currentAnimation){if(!t.data.animationData||!t.data.params)return;var r=t.data.params,i=e.getContext("2d");(currentAnimation=lottiejs.loadAnimation({renderer:"canvas",loop:r.loop,autoplay:r.autoplay,animationData:t.data.animationData,rendererSettings:{context:i,scaleMode:"noScale",clearCanvas:!0}})).play()}}}; \ No newline at end of file +var lottiejs=function(window){"use strict";var svgNS="http://www.w3.org/2000/svg",locationHref="",initialDefaultFrame=-999999,subframeEnabled=!0,expressionsPlugin,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent),cachedColors={},bm_rounder=Math.round,bm_rnd,bm_pow=Math.pow,bm_sqrt=Math.sqrt,bm_abs=Math.abs,bm_floor=Math.floor,bm_max=Math.max,bm_min=Math.min,blitter=10,BMMath={};function ProjectInterface(){return{}}!function(){var t,e=["abs","acos","acosh","asin","asinh","atan","atanh","atan2","ceil","cbrt","expm1","clz32","cos","cosh","exp","floor","fround","hypot","imul","log","log1p","log2","log10","max","min","pow","random","round","sign","sin","sinh","sqrt","tan","tanh","trunc","E","LN10","LN2","LOG10E","LOG2E","PI","SQRT1_2","SQRT2"],r=e.length;for(t=0;t<r;t+=1)BMMath[e[t]]=Math[e[t]]}(),BMMath.random=Math.random,BMMath.abs=function(t){if("object"==typeof t&&t.length){var e,r=createSizedArray(t.length),i=t.length;for(e=0;e<i;e+=1)r[e]=Math.abs(t[e]);return r}return Math.abs(t)};var defaultCurveSegments=150,degToRads=Math.PI/180,roundCorner=.5519;function roundValues(t){bm_rnd=t?Math.round:function(t){return t}}function styleDiv(t){t.style.position="absolute",t.style.top=0,t.style.left=0,t.style.display="block",t.style.transformOrigin=t.style.webkitTransformOrigin="0 0",t.style.backfaceVisibility=t.style.webkitBackfaceVisibility="visible",t.style.transformStyle=t.style.webkitTransformStyle=t.style.mozTransformStyle="preserve-3d"}function BMEnterFrameEvent(t,e,r,i){this.type=t,this.currentTime=e,this.totalTime=r,this.direction=i<0?-1:1}function BMCompleteEvent(t,e){this.type=t,this.direction=e<0?-1:1}function BMCompleteLoopEvent(t,e,r,i){this.type=t,this.currentLoop=r,this.totalLoops=e,this.direction=i<0?-1:1}function BMSegmentStartEvent(t,e,r){this.type=t,this.firstFrame=e,this.totalFrames=r}function BMDestroyEvent(t,e){this.type=t,this.target=e}roundValues(!1);var createElementID=(B=0,function(){return"__lottie_element_"+ ++B}),B;function HSVtoRGB(t,e,r){var i,s,a,n,o,h,p,l;switch(h=r*(1-e),p=r*(1-(o=6*t-(n=Math.floor(6*t)))*e),l=r*(1-(1-o)*e),n%6){case 0:i=r,s=l,a=h;break;case 1:i=p,s=r,a=h;break;case 2:i=h,s=r,a=l;break;case 3:i=h,s=p,a=r;break;case 4:i=l,s=h,a=r;break;case 5:i=r,s=h,a=p}return[i,s,a]}function RGBtoHSV(t,e,r){var i,s=Math.max(t,e,r),a=Math.min(t,e,r),n=s-a,o=0===s?0:n/s,h=s/255;switch(s){case a:i=0;break;case t:i=e-r+n*(e<r?6:0),i/=6*n;break;case e:i=r-t+2*n,i/=6*n;break;case r:i=t-e+4*n,i/=6*n}return[i,o,h]}function addSaturationToRGB(t,e){var r=RGBtoHSV(255*t[0],255*t[1],255*t[2]);return r[1]+=e,1<r[1]?r[1]=1:r[1]<=0&&(r[1]=0),HSVtoRGB(r[0],r[1],r[2])}function addBrightnessToRGB(t,e){var r=RGBtoHSV(255*t[0],255*t[1],255*t[2]);return r[2]+=e,1<r[2]?r[2]=1:r[2]<0&&(r[2]=0),HSVtoRGB(r[0],r[1],r[2])}function addHueToRGB(t,e){var r=RGBtoHSV(255*t[0],255*t[1],255*t[2]);return r[0]+=e/360,1<r[0]?r[0]-=1:r[0]<0&&(r[0]+=1),HSVtoRGB(r[0],r[1],r[2])}var rgbToHex=function(){var t,e,i=[];for(t=0;t<256;t+=1)e=t.toString(16),i[t]=1==e.length?"0"+e:e;return function(t,e,r){return t<0&&(t=0),e<0&&(e=0),r<0&&(r=0),"#"+i[t]+i[e]+i[r]}}();function BaseEvent(){}BaseEvent.prototype={triggerEvent:function(t,e){if(this._cbs[t])for(var r=this._cbs[t].length,i=0;i<r;i++)this._cbs[t][i](e)},addEventListener:function(t,e){return this._cbs[t]||(this._cbs[t]=[]),this._cbs[t].push(e),function(){this.removeEventListener(t,e)}.bind(this)},removeEventListener:function(t,e){if(e){if(this._cbs[t]){for(var r=0,i=this._cbs[t].length;r<i;)this._cbs[t][r]===e&&(this._cbs[t].splice(r,1),r-=1,i-=1),r+=1;this._cbs[t].length||(this._cbs[t]=null)}}else this._cbs[t]=null}};var createTypedArray="function"==typeof Uint8ClampedArray&&"function"==typeof Float32Array?function(t,e){return"float32"===t?new Float32Array(e):"int16"===t?new Int16Array(e):"uint8c"===t?new Uint8ClampedArray(e):void 0}:function(t,e){var r,i=0,s=[];switch(t){case"int16":case"uint8c":r=1;break;default:r=1.1}for(i=0;i<e;i+=1)s.push(r);return s};function createSizedArray(t){return Array.apply(null,{length:t})}function createTag(t){return document.createElement(t)}function DynamicPropertyContainer(){}DynamicPropertyContainer.prototype={addDynamicProperty:function(t){-1===this.dynamicProperties.indexOf(t)&&(this.dynamicProperties.push(t),this.container.addDynamicProperty(this),this._isAnimated=!0)},iterateDynamicProperties:function(){this._mdf=!1;var t,e=this.dynamicProperties.length;for(t=0;t<e;t+=1)this.dynamicProperties[t].getValue(),this.dynamicProperties[t]._mdf&&(this._mdf=!0)},initDynamicPropertyContainer:function(t){this.container=t,this.dynamicProperties=[],this._mdf=!1,this._isAnimated=!1}};var getBlendMode=(Ja={0:"source-over",1:"multiply",2:"screen",3:"overlay",4:"darken",5:"lighten",6:"color-dodge",7:"color-burn",8:"hard-light",9:"soft-light",10:"difference",11:"exclusion",12:"hue",13:"saturation",14:"color",15:"luminosity"},function(t){return Ja[t]||""}),Ja,Matrix=(La=Math.cos,Ma=Math.sin,Na=Math.tan,Oa=Math.round,function(){this.reset=Pa,this.rotate=Qa,this.rotateX=Ra,this.rotateY=Sa,this.rotateZ=Ta,this.skew=Va,this.skewFromAxis=Wa,this.shear=Ua,this.scale=Xa,this.setTransform=Ya,this.translate=Za,this.transform=$a,this.applyToPoint=db,this.applyToX=eb,this.applyToY=fb,this.applyToZ=gb,this.applyToPointArray=kb,this.applyToTriplePoints=jb,this.applyToPointStringified=lb,this.toCSS=mb,this.to2dCSS=pb,this.clone=bb,this.cloneFromProps=cb,this.equals=ab,this.inversePoints=ib,this.inversePoint=hb,this._t=this.transform,this.isIdentity=_a,this._identity=!0,this._identityCalculated=!1,this.props=createTypedArray("float32",16),this.reset()}),La,Ma,Na,Oa;function Pa(){return this.props[0]=1,this.props[1]=0,this.props[2]=0,this.props[3]=0,this.props[4]=0,this.props[5]=1,this.props[6]=0,this.props[7]=0,this.props[8]=0,this.props[9]=0,this.props[10]=1,this.props[11]=0,this.props[12]=0,this.props[13]=0,this.props[14]=0,this.props[15]=1,this}function Qa(t){if(0===t)return this;var e=La(t),r=Ma(t);return this._t(e,-r,0,0,r,e,0,0,0,0,1,0,0,0,0,1)}function Ra(t){if(0===t)return this;var e=La(t),r=Ma(t);return this._t(1,0,0,0,0,e,-r,0,0,r,e,0,0,0,0,1)}function Sa(t){if(0===t)return this;var e=La(t),r=Ma(t);return this._t(e,0,r,0,0,1,0,0,-r,0,e,0,0,0,0,1)}function Ta(t){if(0===t)return this;var e=La(t),r=Ma(t);return this._t(e,-r,0,0,r,e,0,0,0,0,1,0,0,0,0,1)}function Ua(t,e){return this._t(1,e,t,1,0,0)}function Va(t,e){return this.shear(Na(t),Na(e))}function Wa(t,e){var r=La(e),i=Ma(e);return this._t(r,i,0,0,-i,r,0,0,0,0,1,0,0,0,0,1)._t(1,0,0,0,Na(t),1,0,0,0,0,1,0,0,0,0,1)._t(r,-i,0,0,i,r,0,0,0,0,1,0,0,0,0,1)}function Xa(t,e,r){return r||0===r||(r=1),1===t&&1===e&&1===r?this:this._t(t,0,0,0,0,e,0,0,0,0,r,0,0,0,0,1)}function Ya(t,e,r,i,s,a,n,o,h,p,l,m,f,c,d,u){return this.props[0]=t,this.props[1]=e,this.props[2]=r,this.props[3]=i,this.props[4]=s,this.props[5]=a,this.props[6]=n,this.props[7]=o,this.props[8]=h,this.props[9]=p,this.props[10]=l,this.props[11]=m,this.props[12]=f,this.props[13]=c,this.props[14]=d,this.props[15]=u,this}function Za(t,e,r){return r=r||0,0!==t||0!==e||0!==r?this._t(1,0,0,0,0,1,0,0,0,0,1,0,t,e,r,1):this}function $a(t,e,r,i,s,a,n,o,h,p,l,m,f,c,d,u){var y=this.props;if(1===t&&0===e&&0===r&&0===i&&0===s&&1===a&&0===n&&0===o&&0===h&&0===p&&1===l&&0===m)return y[12]=y[12]*t+y[15]*f,y[13]=y[13]*a+y[15]*c,y[14]=y[14]*l+y[15]*d,y[15]=y[15]*u,this._identityCalculated=!1,this;var g=y[0],v=y[1],P=y[2],b=y[3],x=y[4],_=y[5],S=y[6],T=y[7],A=y[8],C=y[9],E=y[10],k=y[11],D=y[12],I=y[13],M=y[14],w=y[15];return y[0]=g*t+v*s+P*h+b*f,y[1]=g*e+v*a+P*p+b*c,y[2]=g*r+v*n+P*l+b*d,y[3]=g*i+v*o+P*m+b*u,y[4]=x*t+_*s+S*h+T*f,y[5]=x*e+_*a+S*p+T*c,y[6]=x*r+_*n+S*l+T*d,y[7]=x*i+_*o+S*m+T*u,y[8]=A*t+C*s+E*h+k*f,y[9]=A*e+C*a+E*p+k*c,y[10]=A*r+C*n+E*l+k*d,y[11]=A*i+C*o+E*m+k*u,y[12]=D*t+I*s+M*h+w*f,y[13]=D*e+I*a+M*p+w*c,y[14]=D*r+I*n+M*l+w*d,y[15]=D*i+I*o+M*m+w*u,this._identityCalculated=!1,this}function _a(){return this._identityCalculated||(this._identity=!(1!==this.props[0]||0!==this.props[1]||0!==this.props[2]||0!==this.props[3]||0!==this.props[4]||1!==this.props[5]||0!==this.props[6]||0!==this.props[7]||0!==this.props[8]||0!==this.props[9]||1!==this.props[10]||0!==this.props[11]||0!==this.props[12]||0!==this.props[13]||0!==this.props[14]||1!==this.props[15]),this._identityCalculated=!0),this._identity}function ab(t){for(var e=0;e<16;){if(t.props[e]!==this.props[e])return!1;e+=1}return!0}function bb(t){var e;for(e=0;e<16;e+=1)t.props[e]=this.props[e]}function cb(t){var e;for(e=0;e<16;e+=1)this.props[e]=t[e]}function db(t,e,r){return{x:t*this.props[0]+e*this.props[4]+r*this.props[8]+this.props[12],y:t*this.props[1]+e*this.props[5]+r*this.props[9]+this.props[13],z:t*this.props[2]+e*this.props[6]+r*this.props[10]+this.props[14]}}function eb(t,e,r){return t*this.props[0]+e*this.props[4]+r*this.props[8]+this.props[12]}function fb(t,e,r){return t*this.props[1]+e*this.props[5]+r*this.props[9]+this.props[13]}function gb(t,e,r){return t*this.props[2]+e*this.props[6]+r*this.props[10]+this.props[14]}function hb(t){var e=this.props[0]*this.props[5]-this.props[1]*this.props[4],r=this.props[5]/e,i=-this.props[1]/e,s=-this.props[4]/e,a=this.props[0]/e,n=(this.props[4]*this.props[13]-this.props[5]*this.props[12])/e,o=-(this.props[0]*this.props[13]-this.props[1]*this.props[12])/e;return[t[0]*r+t[1]*s+n,t[0]*i+t[1]*a+o,0]}function ib(t){var e,r=t.length,i=[];for(e=0;e<r;e+=1)i[e]=hb(t[e]);return i}function jb(t,e,r){var i=createTypedArray("float32",6);if(this.isIdentity())i[0]=t[0],i[1]=t[1],i[2]=e[0],i[3]=e[1],i[4]=r[0],i[5]=r[1];else{var s=this.props[0],a=this.props[1],n=this.props[4],o=this.props[5],h=this.props[12],p=this.props[13];i[0]=t[0]*s+t[1]*n+h,i[1]=t[0]*a+t[1]*o+p,i[2]=e[0]*s+e[1]*n+h,i[3]=e[0]*a+e[1]*o+p,i[4]=r[0]*s+r[1]*n+h,i[5]=r[0]*a+r[1]*o+p}return i}function kb(t,e,r){return this.isIdentity()?[t,e,r]:[t*this.props[0]+e*this.props[4]+r*this.props[8]+this.props[12],t*this.props[1]+e*this.props[5]+r*this.props[9]+this.props[13],t*this.props[2]+e*this.props[6]+r*this.props[10]+this.props[14]]}function lb(t,e){if(this.isIdentity())return t+","+e;var r=this.props;return Math.round(100*(t*r[0]+e*r[4]+r[12]))/100+","+Math.round(100*(t*r[1]+e*r[5]+r[13]))/100}function mb(){for(var t=0,e=this.props,r="matrix3d(";t<16;)r+=Oa(1e4*e[t])/1e4,r+=15===t?")":",",t+=1;return r}function nb(t){return t<1e-6&&0<t||-1e-6<t&&t<0?Oa(1e4*t)/1e4:t}function pb(){var t=this.props;return"matrix("+nb(t[0])+","+nb(t[1])+","+nb(t[4])+","+nb(t[5])+","+nb(t[12])+","+nb(t[13])+")"}!function(o,h){var p,l=this,m=256,f=6,c="random",d=h.pow(m,f),u=h.pow(2,52),y=2*u,g=m-1;function v(t){var e,r=t.length,n=this,i=0,s=n.i=n.j=0,a=n.S=[];for(r||(t=[r++]);i<m;)a[i]=i++;for(i=0;i<m;i++)a[i]=a[s=g&s+t[i%r]+(e=a[i])],a[s]=e;n.g=function(t){for(var e,r=0,i=n.i,s=n.j,a=n.S;t--;)e=a[i=g&i+1],r=r*m+a[g&(a[i]=a[s=g&s+e])+(a[s]=e)];return n.i=i,n.j=s,r}}function P(t,e){return e.i=t.i,e.j=t.j,e.S=t.S.slice(),e}function b(t,e){for(var r,i=t+"",s=0;s<i.length;)e[g&s]=g&(r^=19*e[g&s])+i.charCodeAt(s++);return x(e)}function x(t){return String.fromCharCode.apply(0,t)}h["seed"+c]=function(t,e,r){function i(){for(var t=n.g(f),e=d,r=0;t<u;)t=(t+r)*m,e*=m,r=n.g(1);for(;y<=t;)t/=2,e/=2,r>>>=1;return(t+r)/e}var s=[],a=b(function t(e,r){var i,s=[],a=typeof e;if(r&&"object"==a)for(i in e)try{s.push(t(e[i],r-1))}catch(t){}return s.length?s:"string"==a?e:e+"\0"}((e=!0===e?{entropy:!0}:e||{}).entropy?[t,x(o)]:null===t?function(){try{if(p)return x(p.randomBytes(m));var t=new Uint8Array(m);return(l.crypto||l.msCrypto).getRandomValues(t),x(t)}catch(t){var e=l.navigator,r=e&&e.plugins;return[+new Date,l,r,l.screen,x(o)]}}():t,3),s),n=new v(s);return i.int32=function(){return 0|n.g(4)},i.quick=function(){return n.g(4)/4294967296},i.double=i,b(x(n.S),o),(e.pass||r||function(t,e,r,i){return i&&(i.S&&P(i,n),t.state=function(){return P(n,{})}),r?(h[c]=t,e):t})(i,a,"global"in e?e.global:this==h,e.state)},b(h.random(),o)}([],BMMath);var BezierFactory=(_e={getBezierEasing:function(t,e,r,i,s){var a=s||("bez_"+t+"_"+e+"_"+r+"_"+i).replace(/\./g,"p");if(af[a])return af[a];var n=new rf([t,e,r,i]);return af[a]=n}},af={},gf=11,hf=1/(gf-1),jf="function"==typeof Float32Array,rf.prototype={get:function(t){var e=this._p[0],r=this._p[1],i=this._p[2],s=this._p[3];return this._precomputed||this._precompute(),e===r&&i===s?t:0===t?0:1===t?1:nf(this._getTForX(t),r,s)},_precompute:function(){var t=this._p[0],e=this._p[1],r=this._p[2],i=this._p[3];this._precomputed=!0,t===e&&r===i||this._calcSampleValues()},_calcSampleValues:function(){for(var t=this._p[0],e=this._p[2],r=0;r<gf;++r)this._mSampleValues[r]=nf(r*hf,t,e)},_getTForX:function(t){for(var e=this._p[0],r=this._p[2],i=this._mSampleValues,s=0,a=1,n=gf-1;a!==n&&i[a]<=t;++a)s+=hf;var o=s+(t-i[--a])/(i[a+1]-i[a])*hf,h=of(o,e,r);return.001<=h?function(t,e,r,i){for(var s=0;s<4;++s){var a=of(e,r,i);if(0===a)return e;e-=(nf(e,r,i)-t)/a}return e}(t,o,e,r):0===h?o:function(t,e,r,i,s){for(var a,n,o=0;0<(a=nf(n=e+(r-e)/2,i,s)-t)?r=n:e=n,1e-7<Math.abs(a)&&++o<10;);return n}(t,s,s+hf,e,r)}},_e),_e,af,gf,hf,jf;function kf(t,e){return 1-3*e+3*t}function lf(t,e){return 3*e-6*t}function mf(t){return 3*t}function nf(t,e,r){return((kf(e,r)*t+lf(e,r))*t+mf(e))*t}function of(t,e,r){return 3*kf(e,r)*t*t+2*lf(e,r)*t+mf(e)}function rf(t){this._p=t,this._mSampleValues=jf?new Float32Array(gf):new Array(gf),this._precomputed=!1,this.get=this.get.bind(this)}function extendPrototype(t,e){var r,i,s=t.length;for(r=0;r<s;r+=1)for(var a in i=t[r].prototype)i.hasOwnProperty(a)&&(e.prototype[a]=i[a])}function getDescriptor(t,e){return Object.getOwnPropertyDescriptor(t,e)}function createProxyFunction(t){function e(){}return e.prototype=t,e}function bezFunction(){Math;function y(t,e,r,i,s,a){var n=t*i+e*s+r*a-s*i-a*t-r*e;return-.001<n&&n<.001}var l=function(t,e,r,i){var s,a,n,o,h,p,l=defaultCurveSegments,m=0,f=[],c=[],d=bezier_length_pool.newElement();for(n=r.length,s=0;s<l;s+=1){for(h=s/(l-1),a=p=0;a<n;a+=1)o=bm_pow(1-h,3)*t[a]+3*bm_pow(1-h,2)*h*r[a]+3*(1-h)*bm_pow(h,2)*i[a]+bm_pow(h,3)*e[a],f[a]=o,null!==c[a]&&(p+=bm_pow(f[a]-c[a],2)),c[a]=f[a];p&&(m+=p=bm_sqrt(p)),d.percents[s]=h,d.lengths[s]=m}return d.addedLength=m,d};function g(t){this.segmentLength=0,this.points=new Array(t)}function v(t,e){this.partialLength=t,this.point=e}var P,t=(P={},function(t,e,r,i){var s=(t[0]+"_"+t[1]+"_"+e[0]+"_"+e[1]+"_"+r[0]+"_"+r[1]+"_"+i[0]+"_"+i[1]).replace(/\./g,"p");if(!P[s]){var a,n,o,h,p,l,m,f=defaultCurveSegments,c=0,d=null;2===t.length&&(t[0]!=e[0]||t[1]!=e[1])&&y(t[0],t[1],e[0],e[1],t[0]+r[0],t[1]+r[1])&&y(t[0],t[1],e[0],e[1],e[0]+i[0],e[1]+i[1])&&(f=2);var u=new g(f);for(o=r.length,a=0;a<f;a+=1){for(m=createSizedArray(o),p=a/(f-1),n=l=0;n<o;n+=1)h=bm_pow(1-p,3)*t[n]+3*bm_pow(1-p,2)*p*(t[n]+r[n])+3*(1-p)*bm_pow(p,2)*(e[n]+i[n])+bm_pow(p,3)*e[n],m[n]=h,null!==d&&(l+=bm_pow(m[n]-d[n],2));c+=l=bm_sqrt(l),u.points[a]=new v(l,m),d=m}u.segmentLength=c,P[s]=u}return P[s]});function D(t,e){var r=e.percents,i=e.lengths,s=r.length,a=bm_floor((s-1)*t),n=t*e.addedLength,o=0;if(a===s-1||0===a||n===i[a])return r[a];for(var h=i[a]>n?-1:1,p=!0;p;)if(i[a]<=n&&i[a+1]>n?(o=(n-i[a])/(i[a+1]-i[a]),p=!1):a+=h,a<0||s-1<=a){if(a===s-1)return r[a];p=!1}return r[a]+(r[a+1]-r[a])*o}var I=createTypedArray("float32",8);return{getSegmentsLength:function(t){var e,r=segments_length_pool.newElement(),i=t.c,s=t.v,a=t.o,n=t.i,o=t._length,h=r.lengths,p=0;for(e=0;e<o-1;e+=1)h[e]=l(s[e],s[e+1],a[e],n[e+1]),p+=h[e].addedLength;return i&&o&&(h[e]=l(s[e],s[0],a[e],n[0]),p+=h[e].addedLength),r.totalLength=p,r},getNewSegment:function(t,e,r,i,s,a,n){var o,h=D(s=s<0?0:1<s?1:s,n),p=D(a=1<a?1:a,n),l=t.length,m=1-h,f=1-p,c=m*m*m,d=h*m*m*3,u=h*h*m*3,y=h*h*h,g=m*m*f,v=h*m*f+m*h*f+m*m*p,P=h*h*f+m*h*p+h*m*p,b=h*h*p,x=m*f*f,_=h*f*f+m*p*f+m*f*p,S=h*p*f+m*p*p+h*f*p,T=h*p*p,A=f*f*f,C=p*f*f+f*p*f+f*f*p,E=p*p*f+f*p*p+p*f*p,k=p*p*p;for(o=0;o<l;o+=1)I[4*o]=Math.round(1e3*(c*t[o]+d*r[o]+u*i[o]+y*e[o]))/1e3,I[4*o+1]=Math.round(1e3*(g*t[o]+v*r[o]+P*i[o]+b*e[o]))/1e3,I[4*o+2]=Math.round(1e3*(x*t[o]+_*r[o]+S*i[o]+T*e[o]))/1e3,I[4*o+3]=Math.round(1e3*(A*t[o]+C*r[o]+E*i[o]+k*e[o]))/1e3;return I},getPointInSegment:function(t,e,r,i,s,a){var n=D(s,a),o=1-n;return[Math.round(1e3*(o*o*o*t[0]+(n*o*o+o*n*o+o*o*n)*r[0]+(n*n*o+o*n*n+n*o*n)*i[0]+n*n*n*e[0]))/1e3,Math.round(1e3*(o*o*o*t[1]+(n*o*o+o*n*o+o*o*n)*r[1]+(n*n*o+o*n*n+n*o*n)*i[1]+n*n*n*e[1]))/1e3]},buildBezierData:t,pointOnLine2D:y,pointOnLine3D:function(t,e,r,i,s,a,n,o,h){if(0===r&&0===a&&0===h)return y(t,e,i,s,n,o);var p,l=Math.sqrt(Math.pow(i-t,2)+Math.pow(s-e,2)+Math.pow(a-r,2)),m=Math.sqrt(Math.pow(n-t,2)+Math.pow(o-e,2)+Math.pow(h-r,2)),f=Math.sqrt(Math.pow(n-i,2)+Math.pow(o-s,2)+Math.pow(h-a,2));return-1e-4<(p=m<l?f<l?l-m-f:f-m-l:m<f?f-m-l:m-l-f)&&p<1e-4}}}!function(){for(var a=0,t=["ms","moz","webkit","o"],e=0;e<t.length&&!window.requestAnimationFrame;++e)window.requestAnimationFrame=window[t[e]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[t[e]+"CancelAnimationFrame"]||window[t[e]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(t,e){var r=(new Date).getTime(),i=Math.max(0,16-(r-a)),s=setTimeout(function(){t(r+i)},i);return a=r+i,s}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(t){clearTimeout(t)})}();var bez=bezFunction();function dataFunctionManager(){function m(t,e,r){var i,s,a,n,o,h,p=t.length;for(s=0;s<p;s+=1)if("ks"in(i=t[s])&&!i.completed){if(i.completed=!0,i.tt&&(t[s-1].td=i.tt),[],-1,i.hasMask){var l=i.masksProperties;for(n=l.length,a=0;a<n;a+=1)if(l[a].pt.k.i)d(l[a].pt.k);else for(h=l[a].pt.k.length,o=0;o<h;o+=1)l[a].pt.k[o].s&&d(l[a].pt.k[o].s[0]),l[a].pt.k[o].e&&d(l[a].pt.k[o].e[0])}0===i.ty?(i.layers=f(i.refId,e),m(i.layers,e,r)):4===i.ty?c(i.shapes):5==i.ty&&b(i,r)}}function f(t,e){for(var r=0,i=e.length;r<i;){if(e[r].id===t)return e[r].layers.__used?JSON.parse(JSON.stringify(e[r].layers)):(e[r].layers.__used=!0,e[r].layers);r+=1}}function c(t){var e,r,i;for(e=t.length-1;0<=e;e-=1)if("sh"==t[e].ty){if(t[e].ks.k.i)d(t[e].ks.k);else for(i=t[e].ks.k.length,r=0;r<i;r+=1)t[e].ks.k[r].s&&d(t[e].ks.k[r].s[0]),t[e].ks.k[r].e&&d(t[e].ks.k[r].e[0]);!0}else"gr"==t[e].ty&&c(t[e].it)}function d(t){var e,r=t.i.length;for(e=0;e<r;e+=1)t.i[e][0]+=t.v[e][0],t.i[e][1]+=t.v[e][1],t.o[e][0]+=t.v[e][0],t.o[e][1]+=t.v[e][1]}function o(t,e){var r=e?e.split("."):[100,100,100];return t[0]>r[0]||!(r[0]>t[0])&&(t[1]>r[1]||!(r[1]>t[1])&&(t[2]>r[2]||!(r[2]>t[2])&&void 0))}var i,r=(i=[4,4,14],function(t){if(o(i,t.v)&&(s(t.layers),t.assets)){var e,r=t.assets.length;for(e=0;e<r;e+=1)t.assets[e].layers&&s(t.assets[e].layers)}});function s(t){var e,r,i,s=t.length;for(e=0;e<s;e+=1)5===t[e].ty&&(r=t[e],void 0,i=r.t.d,r.t.d={k:[{s:i,t:0}]})}var h,a,n=(h=[4,7,99],function(t){if(t.chars&&!o(h,t.v)){var e,r,i,s,a,n=t.chars.length;for(e=0;e<n;e+=1)if(t.chars[e].data&&t.chars[e].data.shapes)for(i=(a=t.chars[e].data.shapes[0].it).length,r=0;r<i;r+=1)(s=a[r].ks.k).__converted||(d(a[r].ks.k),s.__converted=!0)}}),p=(a=[4,1,9],function(t){if(o(a,t.v)&&(u(t.layers),t.assets)){var e,r=t.assets.length;for(e=0;e<r;e+=1)t.assets[e].layers&&u(t.assets[e].layers)}});function l(t){var e,r,i,s=t.length;for(e=0;e<s;e+=1)if("gr"===t[e].ty)l(t[e].it);else if("fl"===t[e].ty||"st"===t[e].ty)if(t[e].c.k&&t[e].c.k[0].i)for(i=t[e].c.k.length,r=0;r<i;r+=1)t[e].c.k[r].s&&(t[e].c.k[r].s[0]/=255,t[e].c.k[r].s[1]/=255,t[e].c.k[r].s[2]/=255,t[e].c.k[r].s[3]/=255),t[e].c.k[r].e&&(t[e].c.k[r].e[0]/=255,t[e].c.k[r].e[1]/=255,t[e].c.k[r].e[2]/=255,t[e].c.k[r].e[3]/=255);else t[e].c.k[0]/=255,t[e].c.k[1]/=255,t[e].c.k[2]/=255,t[e].c.k[3]/=255}function u(t){var e,r=t.length;for(e=0;e<r;e+=1)4===t[e].ty&&l(t[e].shapes)}var y,g=(y=[4,4,18],function(t){if(o(y,t.v)&&(P(t.layers),t.assets)){var e,r=t.assets.length;for(e=0;e<r;e+=1)t.assets[e].layers&&P(t.assets[e].layers)}});function v(t){var e,r,i;for(e=t.length-1;0<=e;e-=1)if("sh"==t[e].ty){if(t[e].ks.k.i)t[e].ks.k.c=t[e].closed;else for(i=t[e].ks.k.length,r=0;r<i;r+=1)t[e].ks.k[r].s&&(t[e].ks.k[r].s[0].c=t[e].closed),t[e].ks.k[r].e&&(t[e].ks.k[r].e[0].c=t[e].closed);!0}else"gr"==t[e].ty&&v(t[e].it)}function P(t){var e,r,i,s,a,n,o=t.length;for(r=0;r<o;r+=1){if((e=t[r]).hasMask){var h=e.masksProperties;for(s=h.length,i=0;i<s;i+=1)if(h[i].pt.k.i)h[i].pt.k.c=h[i].cl;else for(n=h[i].pt.k.length,a=0;a<n;a+=1)h[i].pt.k[a].s&&(h[i].pt.k[a].s[0].c=h[i].cl),h[i].pt.k[a].e&&(h[i].pt.k[a].e[0].c=h[i].cl)}4===e.ty&&v(e.shapes)}}function b(t,e){0!==t.t.a.length||"m"in t.t.p||(t.singleShape=!0)}var t={completeData:function(t,e){t.__complete||(p(t),r(t),n(t),g(t),m(t.layers,t.assets,e),t.__complete=!0)}};return t.checkColors=p,t.checkChars=n,t.checkShapes=g,t.completeLayers=m,t}var dataManager=dataFunctionManager();dataManager.completeData=function(t,e){t.__complete||(this.checkColors(t),this.checkChars(t),this.checkShapes(t),this.completeLayers(t.layers,t.assets,e),t.__complete=!0)};var FontManager=function(){var a={w:0,size:0,shapes:[]},t=[];function u(t,e){var r=createTag("span");r.style.fontFamily=e;var i=createTag("span");i.innerHTML="giItT1WQy@!-/#",r.style.position="absolute",r.style.left="-10000px",r.style.top="-10000px",r.style.fontSize="300px",r.style.fontVariant="normal",r.style.fontStyle="normal",r.style.fontWeight="normal",r.style.letterSpacing="0",r.appendChild(i),document.body.appendChild(r);var s=i.offsetWidth;return i.style.fontFamily=t+", "+e,{node:i,w:s,parent:r}}t=t.concat([2304,2305,2306,2307,2362,2363,2364,2364,2366,2367,2368,2369,2370,2371,2372,2373,2374,2375,2376,2377,2378,2379,2380,2381,2382,2383,2387,2388,2389,2390,2391,2402,2403]);function e(){this.fonts=[],this.chars=null,this.typekitLoaded=0,this.isLoaded=!1,this.initTime=Date.now()}return e.getCombinedCharacterCodes=function(){return t},e.prototype.addChars=function(t){if(t){this.chars||(this.chars=[]);var e,r,i,s=t.length,a=this.chars.length;for(e=0;e<s;e+=1){for(r=0,i=!1;r<a;)this.chars[r].style===t[e].style&&this.chars[r].fFamily===t[e].fFamily&&this.chars[r].ch===t[e].ch&&(i=!0),r+=1;i||(this.chars.push(t[e]),a+=1)}}},e.prototype.addFonts=function(t,e){if(t){if(this.chars)return this.isLoaded=!0,void(this.fonts=t.list);var r,i,s,a,n=t.list,o=n.length,h=o;for(r=0;r<o;r+=1){var p,l,m=!0;if(n[r].loaded=!1,n[r].monoCase=u(n[r].fFamily,"monospace"),n[r].sansCase=u(n[r].fFamily,"sans-serif"),n[r].fPath){if("p"===n[r].fOrigin||3===n[r].origin){if(0<(p=document.querySelectorAll('style[f-forigin="p"][f-family="'+n[r].fFamily+'"], style[f-origin="3"][f-family="'+n[r].fFamily+'"]')).length&&(m=!1),m){var f=createTag("style");f.setAttribute("f-forigin",n[r].fOrigin),f.setAttribute("f-origin",n[r].origin),f.setAttribute("f-family",n[r].fFamily),f.type="text/css",f.innerHTML="@font-face {font-family: "+n[r].fFamily+"; font-style: normal; src: url('"+n[r].fPath+"');}",e.appendChild(f)}}else if("g"===n[r].fOrigin||1===n[r].origin){for(p=document.querySelectorAll('link[f-forigin="g"], link[f-origin="1"]'),l=0;l<p.length;l++)-1!==p[l].href.indexOf(n[r].fPath)&&(m=!1);if(m){var c=createTag("link");c.setAttribute("f-forigin",n[r].fOrigin),c.setAttribute("f-origin",n[r].origin),c.type="text/css",c.rel="stylesheet",c.href=n[r].fPath,document.body.appendChild(c)}}else if("t"===n[r].fOrigin||2===n[r].origin){for(p=document.querySelectorAll('script[f-forigin="t"], script[f-origin="2"]'),l=0;l<p.length;l++)n[r].fPath===p[l].src&&(m=!1);if(m){var d=createTag("link");d.setAttribute("f-forigin",n[r].fOrigin),d.setAttribute("f-origin",n[r].origin),d.setAttribute("rel","stylesheet"),d.setAttribute("href",n[r].fPath),e.appendChild(d)}}}else n[r].loaded=!0,h-=1;n[r].helper=(i=e,s=n[r],a=void 0,(a=createNS("text")).style.fontSize="100px",a.setAttribute("font-family",s.fFamily),a.setAttribute("font-style",s.fStyle),a.setAttribute("font-weight",s.fWeight),a.textContent="1",s.fClass?(a.style.fontFamily="inherit",a.setAttribute("class",s.fClass)):a.style.fontFamily=s.fFamily,i.appendChild(a),createTag("canvas").getContext("2d").font=s.fWeight+" "+s.fStyle+" 100px "+s.fFamily,a),n[r].cache={},this.fonts.push(n[r])}0===h?this.isLoaded=!0:setTimeout(this.checkLoadedFonts.bind(this),100)}else this.isLoaded=!0},e.prototype.getCharData=function(t,e,r){for(var i=0,s=this.chars.length;i<s;){if(this.chars[i].ch===t&&this.chars[i].style===e&&this.chars[i].fFamily===r)return this.chars[i];i+=1}return console&&console.warn&&console.warn("Missing character from exported characters list: ",t,e,r),a},e.prototype.getFontByName=function(t){for(var e=0,r=this.fonts.length;e<r;){if(this.fonts[e].fName===t)return this.fonts[e];e+=1}return this.fonts[0]},e.prototype.measureText=function(t,e,r){var i=this.getFontByName(e),s=t.charCodeAt(0);if(!i.cache[s+1]){var a=i.helper;if(" "===t){a.textContent="|"+t+"|";var n=a.getComputedTextLength();a.textContent="||";var o=a.getComputedTextLength();i.cache[s+1]=(n-o)/100}else a.textContent=t,i.cache[s+1]=a.getComputedTextLength()/100}return i.cache[s+1]*r},e.prototype.checkLoadedFonts=function(){var t,e,r,i=this.fonts.length,s=i;for(t=0;t<i;t+=1)this.fonts[t].loaded?s-=1:"n"===this.fonts[t].fOrigin||0===this.fonts[t].origin?this.fonts[t].loaded=!0:(e=this.fonts[t].monoCase.node,r=this.fonts[t].monoCase.w,e.offsetWidth!==r?(s-=1,this.fonts[t].loaded=!0):(e=this.fonts[t].sansCase.node,r=this.fonts[t].sansCase.w,e.offsetWidth!==r&&(s-=1,this.fonts[t].loaded=!0)),this.fonts[t].loaded&&(this.fonts[t].sansCase.parent.parentNode.removeChild(this.fonts[t].sansCase.parent),this.fonts[t].monoCase.parent.parentNode.removeChild(this.fonts[t].monoCase.parent)));0!==s&&Date.now()-this.initTime<5e3?setTimeout(this.checkLoadedFonts.bind(this),20):setTimeout(function(){this.isLoaded=!0}.bind(this),0)},e.prototype.loaded=function(){return this.isLoaded},e}();FontManager=function(){this.fonts=[],this.chars=null,this.typekitLoaded=0,this.isLoaded=!1,this.initTime=Date.now()};var PropertyFactory=(km=initialDefaultFrame,lm=Math.abs,{getProp:function(t,e,r,i,s){var a;if(e.k.length)if("number"==typeof e.k[0])a=new vm(t,e,i,s);else switch(r){case 0:a=new wm(t,e,i,s);break;case 1:a=new xm(t,e,i,s)}else a=new um(t,e,i,s);return a.effectsSequence.length&&s.addDynamicProperty(a),a}}),km,lm;function mm(t,e){var r,i=this.offsetTime;"multidimensional"===this.propType&&(r=createTypedArray("float32",this.pv.length));for(var s,a,n,o,h,p,l,m,f=e.lastIndex,c=f,d=this.keyframes.length-1,u=!0;u;){if(s=this.keyframes[c],a=this.keyframes[c+1],c===d-1&&t>=a.t-i){s.h&&(s=a),f=0;break}if(a.t-i>t){f=c;break}c<d-1?c+=1:(f=0,u=!1)}var y,g=a.t-i,v=s.t-i;if(s.to){s.bezierData||(s.bezierData=bez.buildBezierData(s.s,a.s||s.e,s.to,s.ti));var P=s.bezierData;if(g<=t||t<v){var b=g<=t?P.points.length-1:0;for(o=P.points[b].point.length,n=0;n<o;n+=1)r[n]=P.points[b].point[n]}else{s.__fnct?m=s.__fnct:(m=BezierFactory.getBezierEasing(s.o.x,s.o.y,s.i.x,s.i.y,s.n).get,s.__fnct=m),h=m((t-v)/(g-v));var x,_=P.segmentLength*h,S=e.lastFrame<t&&e._lastKeyframeIndex===c?e._lastAddedLength:0;for(l=e.lastFrame<t&&e._lastKeyframeIndex===c?e._lastPoint:0,u=!0,p=P.points.length;u;){if(S+=P.points[l].partialLength,0==_||0===h||l===P.points.length-1){for(o=P.points[l].point.length,n=0;n<o;n+=1)r[n]=P.points[l].point[n];break}if(S<=_&&_<S+P.points[l+1].partialLength){for(x=(_-S)/P.points[l+1].partialLength,o=P.points[l].point.length,n=0;n<o;n+=1)r[n]=P.points[l].point[n]+(P.points[l+1].point[n]-P.points[l].point[n])*x;break}l<p-1?l+=1:u=!1}e._lastPoint=l,e._lastAddedLength=S-P.points[l].partialLength,e._lastKeyframeIndex=c}}else{var T,A,C,E,k;if(d=s.s.length,y=a.s||s.e,this.sh&&1!==s.h)if(g<=t)r[0]=y[0],r[1]=y[1],r[2]=y[2];else if(t<=v)r[0]=s.s[0],r[1]=s.s[1],r[2]=s.s[2];else{!function(t,e){var r=e[0],i=e[1],s=e[2],a=e[3],n=Math.atan2(2*i*a-2*r*s,1-2*i*i-2*s*s),o=Math.asin(2*r*i+2*s*a),h=Math.atan2(2*r*a-2*i*s,1-2*r*r-2*s*s);t[0]=n/degToRads,t[1]=o/degToRads,t[2]=h/degToRads}(r,function(t,e,r){var i,s,a,n,o,h=[],p=t[0],l=t[1],m=t[2],f=t[3],c=e[0],d=e[1],u=e[2],y=e[3];(s=p*c+l*d+m*u+f*y)<0&&(s=-s,c=-c,d=-d,u=-u,y=-y);o=1e-6<1-s?(i=Math.acos(s),a=Math.sin(i),n=Math.sin((1-r)*i)/a,Math.sin(r*i)/a):(n=1-r,r);return h[0]=n*p+o*c,h[1]=n*l+o*d,h[2]=n*m+o*u,h[3]=n*f+o*y,h}(pm(s.s),pm(y),(t-v)/(g-v)))}else for(c=0;c<d;c+=1)1!==s.h&&(h=g<=t?1:t<v?0:(s.o.x.constructor===Array?(s.__fnct||(s.__fnct=[]),s.__fnct[c]?m=s.__fnct[c]:(T=void 0===s.o.x[c]?s.o.x[0]:s.o.x[c],A=void 0===s.o.y[c]?s.o.y[0]:s.o.y[c],C=void 0===s.i.x[c]?s.i.x[0]:s.i.x[c],E=void 0===s.i.y[c]?s.i.y[0]:s.i.y[c],m=BezierFactory.getBezierEasing(T,A,C,E).get,s.__fnct[c]=m)):s.__fnct?m=s.__fnct:(T=s.o.x,A=s.o.y,C=s.i.x,E=s.i.y,m=BezierFactory.getBezierEasing(T,A,C,E).get,s.__fnct=m),m((t-v)/(g-v)))),y=a.s||s.e,k=1===s.h?s.s[c]:s.s[c]+(y[c]-s.s[c])*h,1===d?r=k:r[c]=k}return e.lastIndex=f,r}function pm(t){var e=t[0]*degToRads,r=t[1]*degToRads,i=t[2]*degToRads,s=Math.cos(e/2),a=Math.cos(r/2),n=Math.cos(i/2),o=Math.sin(e/2),h=Math.sin(r/2),p=Math.sin(i/2);return[o*h*n+s*a*p,o*a*n+s*h*p,s*h*n-o*a*p,s*a*n-o*h*p]}function qm(){var t=this.comp.renderedFrame-this.offsetTime,e=this.keyframes[0].t-this.offsetTime,r=this.keyframes[this.keyframes.length-1].t-this.offsetTime;if(!(t===this._caching.lastFrame||this._caching.lastFrame!==km&&(this._caching.lastFrame>=r&&r<=t||this._caching.lastFrame<e&&t<e))){this._caching.lastFrame>=t&&(this._caching._lastKeyframeIndex=-1,this._caching.lastIndex=0);var i=this.interpolateValue(t,this._caching);this.pv=i}return this._caching.lastFrame=t,this.pv}function rm(t){var e;if("unidimensional"===this.propType)e=t*this.mult,1e-5<lm(this.v-e)&&(this.v=e,this._mdf=!0);else for(var r=0,i=this.v.length;r<i;)e=t[r]*this.mult,1e-5<lm(this.v[r]-e)&&(this.v[r]=e,this._mdf=!0),r+=1}function sm(){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length)if(this.lock)this.setVValue(this.pv);else{this.lock=!0,this._mdf=this._isFirstFrame;var t,e=this.effectsSequence.length,r=this.kf?this.pv:this.data.k;for(t=0;t<e;t+=1)r=this.effectsSequence[t](r);this.setVValue(r),this._isFirstFrame=!1,this.lock=!1,this.frameId=this.elem.globalData.frameId}}function tm(t){this.effectsSequence.push(t),this.container.addDynamicProperty(this)}function um(t,e,r,i){this.propType="unidimensional",this.mult=r||1,this.data=e,this.v=r?e.k*r:e.k,this.pv=e.k,this._mdf=!1,this.elem=t,this.container=i,this.comp=t.comp,this.k=!1,this.kf=!1,this.vel=0,this.effectsSequence=[],this._isFirstFrame=!0,this.getValue=sm,this.setVValue=rm,this.addEffect=tm}function vm(t,e,r,i){this.propType="multidimensional",this.mult=r||1,this.data=e,this._mdf=!1,this.elem=t,this.container=i,this.comp=t.comp,this.k=!1,this.kf=!1,this.frameId=-1;var s,a=e.k.length;this.v=createTypedArray("float32",a),this.pv=createTypedArray("float32",a);createTypedArray("float32",a);for(this.vel=createTypedArray("float32",a),s=0;s<a;s+=1)this.v[s]=e.k[s]*this.mult,this.pv[s]=e.k[s];this._isFirstFrame=!0,this.effectsSequence=[],this.getValue=sm,this.setVValue=rm,this.addEffect=tm}function wm(t,e,r,i){this.propType="unidimensional",this.keyframes=e.k,this.offsetTime=t.data.st,this.frameId=-1,this._caching={lastFrame:km,lastIndex:0,value:0,_lastKeyframeIndex:-1},this.k=!0,this.kf=!0,this.data=e,this.mult=r||1,this.elem=t,this.container=i,this.comp=t.comp,this.v=km,this.pv=km,this._isFirstFrame=!0,this.getValue=sm,this.setVValue=rm,this.interpolateValue=mm,this.effectsSequence=[qm.bind(this)],this.addEffect=tm}function xm(t,e,r,i){this.propType="multidimensional";var s,a,n,o,h,p=e.k.length;for(s=0;s<p-1;s+=1)e.k[s].to&&e.k[s].s&&e.k[s].e&&(a=e.k[s].s,n=e.k[s].e,o=e.k[s].to,h=e.k[s].ti,(2===a.length&&(a[0]!==n[0]||a[1]!==n[1])&&bez.pointOnLine2D(a[0],a[1],n[0],n[1],a[0]+o[0],a[1]+o[1])&&bez.pointOnLine2D(a[0],a[1],n[0],n[1],n[0]+h[0],n[1]+h[1])||3===a.length&&(a[0]!==n[0]||a[1]!==n[1]||a[2]!==n[2])&&bez.pointOnLine3D(a[0],a[1],a[2],n[0],n[1],n[2],a[0]+o[0],a[1]+o[1],a[2]+o[2])&&bez.pointOnLine3D(a[0],a[1],a[2],n[0],n[1],n[2],n[0]+h[0],n[1]+h[1],n[2]+h[2]))&&(e.k[s].to=null,e.k[s].ti=null),a[0]===n[0]&&a[1]===n[1]&&0===o[0]&&0===o[1]&&0===h[0]&&0===h[1]&&(2===a.length||a[2]===n[2]&&0===o[2]&&0===h[2])&&(e.k[s].to=null,e.k[s].ti=null));this.effectsSequence=[qm.bind(this)],this.keyframes=e.k,this.offsetTime=t.data.st,this.k=!0,this.kf=!0,this._isFirstFrame=!0,this.mult=r||1,this.elem=t,this.container=i,this.comp=t.comp,this.getValue=sm,this.setVValue=rm,this.interpolateValue=mm,this.frameId=-1;var l=e.k[0].s.length;for(this.v=createTypedArray("float32",l),this.pv=createTypedArray("float32",l),s=0;s<l;s+=1)this.v[s]=km,this.pv[s]=km;this._caching={lastFrame:km,lastIndex:0,value:createTypedArray("float32",l)},this.addEffect=tm}var TransformPropertyFactory=(Qo.prototype={applyToMatrix:function(t){var e=this._mdf;this.iterateDynamicProperties(),this._mdf=this._mdf||e,this.a&&t.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.s&&t.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.sk&&t.skewFromAxis(-this.sk.v,this.sa.v),this.r?t.rotate(-this.r.v):t.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.data.p.s?this.data.p.z?t.translate(this.px.v,this.py.v,-this.pz.v):t.translate(this.px.v,this.py.v,0):t.translate(this.p.v[0],this.p.v[1],-this.p.v[2])},getValue:function(t){if(this.elem.globalData.frameId!==this.frameId){if(this._isDirty&&(this.precalculateMatrix(),this._isDirty=!1),this.iterateDynamicProperties(),this._mdf||t){if(this.v.cloneFromProps(this.pre.props),this.appliedTransformations<1&&this.v.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.appliedTransformations<2&&this.v.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.sk&&this.appliedTransformations<3&&this.v.skewFromAxis(-this.sk.v,this.sa.v),this.r&&this.appliedTransformations<4?this.v.rotate(-this.r.v):!this.r&&this.appliedTransformations<4&&this.v.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.autoOriented){var e,r,i=this.elem.globalData.frameRate;if(this.p&&this.p.keyframes&&this.p.getValueAtTime)r=this.p._caching.lastFrame+this.p.offsetTime<=this.p.keyframes[0].t?(e=this.p.getValueAtTime((this.p.keyframes[0].t+.01)/i,0),this.p.getValueAtTime(this.p.keyframes[0].t/i,0)):this.p._caching.lastFrame+this.p.offsetTime>=this.p.keyframes[this.p.keyframes.length-1].t?(e=this.p.getValueAtTime(this.p.keyframes[this.p.keyframes.length-1].t/i,0),this.p.getValueAtTime((this.p.keyframes[this.p.keyframes.length-1].t-.01)/i,0)):(e=this.p.pv,this.p.getValueAtTime((this.p._caching.lastFrame+this.p.offsetTime-.01)/i,this.p.offsetTime));else if(this.px&&this.px.keyframes&&this.py.keyframes&&this.px.getValueAtTime&&this.py.getValueAtTime){e=[],r=[];var s=this.px,a=this.py;s._caching.lastFrame+s.offsetTime<=s.keyframes[0].t?(e[0]=s.getValueAtTime((s.keyframes[0].t+.01)/i,0),e[1]=a.getValueAtTime((a.keyframes[0].t+.01)/i,0),r[0]=s.getValueAtTime(s.keyframes[0].t/i,0),r[1]=a.getValueAtTime(a.keyframes[0].t/i,0)):s._caching.lastFrame+s.offsetTime>=s.keyframes[s.keyframes.length-1].t?(e[0]=s.getValueAtTime(s.keyframes[s.keyframes.length-1].t/i,0),e[1]=a.getValueAtTime(a.keyframes[a.keyframes.length-1].t/i,0),r[0]=s.getValueAtTime((s.keyframes[s.keyframes.length-1].t-.01)/i,0),r[1]=a.getValueAtTime((a.keyframes[a.keyframes.length-1].t-.01)/i,0)):(e=[s.pv,a.pv],r[0]=s.getValueAtTime((s._caching.lastFrame+s.offsetTime-.01)/i,s.offsetTime),r[1]=a.getValueAtTime((a._caching.lastFrame+a.offsetTime-.01)/i,a.offsetTime))}this.v.rotate(-Math.atan2(e[1]-r[1],e[0]-r[0]))}this.data.p&&this.data.p.s?this.data.p.z?this.v.translate(this.px.v,this.py.v,-this.pz.v):this.v.translate(this.px.v,this.py.v,0):this.v.translate(this.p.v[0],this.p.v[1],-this.p.v[2])}this.frameId=this.elem.globalData.frameId}},precalculateMatrix:function(){if(!this.a.k&&(this.pre.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.appliedTransformations=1,!this.s.effectsSequence.length)){if(this.pre.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.appliedTransformations=2,this.sk){if(this.sk.effectsSequence.length||this.sa.effectsSequence.length)return;this.pre.skewFromAxis(-this.sk.v,this.sa.v),this.appliedTransformations=3}if(this.r){if(this.r.effectsSequence.length)return;this.pre.rotate(-this.r.v),this.appliedTransformations=4}else this.rz.effectsSequence.length||this.ry.effectsSequence.length||this.rx.effectsSequence.length||this.or.effectsSequence.length||(this.pre.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.appliedTransformations=4)}},autoOrient:function(){}},extendPrototype([DynamicPropertyContainer],Qo),Qo.prototype.addDynamicProperty=function(t){this._addDynamicProperty(t),this.elem.addDynamicProperty(t),this._isDirty=!0},Qo.prototype._addDynamicProperty=DynamicPropertyContainer.prototype.addDynamicProperty,{getTransformProperty:function(t,e,r){return new Qo(t,e,r)}});function Qo(t,e,r){if(this.elem=t,this.frameId=-1,this.propType="transform",this.data=e,this.v=new Matrix,this.pre=new Matrix,this.appliedTransformations=0,this.initDynamicPropertyContainer(r||t),e.p&&e.p.s?(this.px=PropertyFactory.getProp(t,e.p.x,0,0,this),this.py=PropertyFactory.getProp(t,e.p.y,0,0,this),e.p.z&&(this.pz=PropertyFactory.getProp(t,e.p.z,0,0,this))):this.p=PropertyFactory.getProp(t,e.p||{k:[0,0,0]},1,0,this),e.rx){if(this.rx=PropertyFactory.getProp(t,e.rx,0,degToRads,this),this.ry=PropertyFactory.getProp(t,e.ry,0,degToRads,this),this.rz=PropertyFactory.getProp(t,e.rz,0,degToRads,this),e.or.k[0].ti){var i,s=e.or.k.length;for(i=0;i<s;i+=1)e.or.k[i].to=e.or.k[i].ti=null}this.or=PropertyFactory.getProp(t,e.or,1,degToRads,this),this.or.sh=!0}else this.r=PropertyFactory.getProp(t,e.r||{k:0},0,degToRads,this);e.sk&&(this.sk=PropertyFactory.getProp(t,e.sk,0,degToRads,this),this.sa=PropertyFactory.getProp(t,e.sa,0,degToRads,this)),this.a=PropertyFactory.getProp(t,e.a||{k:[0,0,0]},1,0,this),this.s=PropertyFactory.getProp(t,e.s||{k:[100,100,100]},1,.01,this),e.o?this.o=PropertyFactory.getProp(t,e.o,0,.01,t):this.o={_mdf:!1,v:1},this._isDirty=!0,this.dynamicProperties.length||this.getValue(!0)}function ShapePath(){this.c=!1,this._length=0,this._maxLength=8,this.v=createSizedArray(this._maxLength),this.o=createSizedArray(this._maxLength),this.i=createSizedArray(this._maxLength)}ShapePath.prototype.setPathData=function(t,e){this.c=t,this.setLength(e);for(var r=0;r<e;)this.v[r]=point_pool.newElement(),this.o[r]=point_pool.newElement(),this.i[r]=point_pool.newElement(),r+=1},ShapePath.prototype.setLength=function(t){for(;this._maxLength<t;)this.doubleArrayLength();this._length=t},ShapePath.prototype.doubleArrayLength=function(){this.v=this.v.concat(createSizedArray(this._maxLength)),this.i=this.i.concat(createSizedArray(this._maxLength)),this.o=this.o.concat(createSizedArray(this._maxLength)),this._maxLength*=2},ShapePath.prototype.setXYAt=function(t,e,r,i,s){var a;switch(this._length=Math.max(this._length,i+1),this._length>=this._maxLength&&this.doubleArrayLength(),r){case"v":a=this.v;break;case"i":a=this.i;break;case"o":a=this.o}a[i]&&(!a[i]||s)||(a[i]=point_pool.newElement()),a[i][0]=t,a[i][1]=e},ShapePath.prototype.setTripleAt=function(t,e,r,i,s,a,n,o){this.setXYAt(t,e,"v",n,o),this.setXYAt(r,i,"o",n,o),this.setXYAt(s,a,"i",n,o)},ShapePath.prototype.reverse=function(){var t=new ShapePath;t.setPathData(this.c,this._length);var e=this.v,r=this.o,i=this.i,s=0;this.c&&(t.setTripleAt(e[0][0],e[0][1],i[0][0],i[0][1],r[0][0],r[0][1],0,!1),s=1);var a,n=this._length-1,o=this._length;for(a=s;a<o;a+=1)t.setTripleAt(e[n][0],e[n][1],i[n][0],i[n][1],r[n][0],r[n][1],a,!1),n-=1;return t};var ShapePropertyFactory=function(){var s=-999999;function t(t,e,r){var i,s,a,n,o,h,p,l,m,f=r.lastIndex,c=this.keyframes;if(t<c[0].t-this.offsetTime)i=c[0].s[0],a=!0,f=0;else if(t>=c[c.length-1].t-this.offsetTime)i=c[c.length-1].s?c[c.length-1].s[0]:c[c.length-2].e[0],a=!0;else{for(var d,u,y=f,g=c.length-1,v=!0;v&&(d=c[y],!((u=c[y+1]).t-this.offsetTime>t));)y<g-1?y+=1:v=!1;if(f=y,!(a=1===d.h)){if(t>=u.t-this.offsetTime)l=1;else if(t<d.t-this.offsetTime)l=0;else{var P;d.__fnct?P=d.__fnct:(P=BezierFactory.getBezierEasing(d.o.x,d.o.y,d.i.x,d.i.y).get,d.__fnct=P),l=P((t-(d.t-this.offsetTime))/(u.t-this.offsetTime-(d.t-this.offsetTime)))}s=u.s?u.s[0]:d.e[0]}i=d.s[0]}for(h=e._length,p=i.i[0].length,r.lastIndex=f,n=0;n<h;n+=1)for(o=0;o<p;o+=1)m=a?i.i[n][o]:i.i[n][o]+(s.i[n][o]-i.i[n][o])*l,e.i[n][o]=m,m=a?i.o[n][o]:i.o[n][o]+(s.o[n][o]-i.o[n][o])*l,e.o[n][o]=m,m=a?i.v[n][o]:i.v[n][o]+(s.v[n][o]-i.v[n][o])*l,e.v[n][o]=m}function a(){this.paths=this.localShapeCollection}function e(t){!function(t,e){if(t._length!==e._length||t.c!==e.c)return!1;var r,i=t._length;for(r=0;r<i;r+=1)if(t.v[r][0]!==e.v[r][0]||t.v[r][1]!==e.v[r][1]||t.o[r][0]!==e.o[r][0]||t.o[r][1]!==e.o[r][1]||t.i[r][0]!==e.i[r][0]||t.i[r][1]!==e.i[r][1])return!1;return!0}(this.v,t)&&(this.v=shape_pool.clone(t),this.localShapeCollection.releaseShapes(),this.localShapeCollection.addShape(this.v),this._mdf=!0,this.paths=this.localShapeCollection)}function r(){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length)if(this.lock)this.setVValue(this.pv);else{this.lock=!0,this._mdf=!1;var t,e=this.kf?this.pv:this.data.ks?this.data.ks.k:this.data.pt.k,r=this.effectsSequence.length;for(t=0;t<r;t+=1)e=this.effectsSequence[t](e);this.setVValue(e),this.lock=!1,this.frameId=this.elem.globalData.frameId}}function n(t,e,r){this.propType="shape",this.comp=t.comp,this.container=t,this.elem=t,this.data=e,this.k=!1,this.kf=!1,this._mdf=!1;var i=3===r?e.pt.k:e.ks.k;this.v=shape_pool.clone(i),this.pv=shape_pool.clone(this.v),this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.paths=this.localShapeCollection,this.paths.addShape(this.v),this.reset=a,this.effectsSequence=[]}function i(t){this.effectsSequence.push(t),this.container.addDynamicProperty(this)}function o(t,e,r){this.propType="shape",this.comp=t.comp,this.elem=t,this.container=t,this.offsetTime=t.data.st,this.keyframes=3===r?e.pt.k:e.ks.k,this.k=!0,this.kf=!0;var i=this.keyframes[0].s[0].i.length;this.keyframes[0].s[0].i[0].length;this.v=shape_pool.newElement(),this.v.setPathData(this.keyframes[0].s[0].c,i),this.pv=shape_pool.clone(this.v),this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.paths=this.localShapeCollection,this.paths.addShape(this.v),this.lastFrame=s,this.reset=a,this._caching={lastFrame:s,lastIndex:0},this.effectsSequence=[function(){var t=this.comp.renderedFrame-this.offsetTime,e=this.keyframes[0].t-this.offsetTime,r=this.keyframes[this.keyframes.length-1].t-this.offsetTime,i=this._caching.lastFrame;return i!==s&&(i<e&&t<e||r<i&&r<t)||(this._caching.lastIndex=i<t?this._caching.lastIndex:0,this.interpolateShape(t,this.pv,this._caching)),this._caching.lastFrame=t,this.pv}.bind(this)]}n.prototype.interpolateShape=t,n.prototype.getValue=r,n.prototype.setVValue=e,n.prototype.addEffect=i,o.prototype.getValue=r,o.prototype.interpolateShape=t,o.prototype.setVValue=e,o.prototype.addEffect=i;var h,p=(h=roundCorner,l.prototype={reset:a,getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertEllToPath())},convertEllToPath:function(){var t=this.p.v[0],e=this.p.v[1],r=this.s.v[0]/2,i=this.s.v[1]/2,s=3!==this.d,a=this.v;a.v[0][0]=t,a.v[0][1]=e-i,a.v[1][0]=s?t+r:t-r,a.v[1][1]=e,a.v[2][0]=t,a.v[2][1]=e+i,a.v[3][0]=s?t-r:t+r,a.v[3][1]=e,a.i[0][0]=s?t-r*h:t+r*h,a.i[0][1]=e-i,a.i[1][0]=s?t+r:t-r,a.i[1][1]=e-i*h,a.i[2][0]=s?t+r*h:t-r*h,a.i[2][1]=e+i,a.i[3][0]=s?t-r:t+r,a.i[3][1]=e+i*h,a.o[0][0]=s?t+r*h:t-r*h,a.o[0][1]=e-i,a.o[1][0]=s?t+r:t-r,a.o[1][1]=e+i*h,a.o[2][0]=s?t-r*h:t+r*h,a.o[2][1]=e+i,a.o[3][0]=s?t-r:t+r,a.o[3][1]=e-i*h}},extendPrototype([DynamicPropertyContainer],l),l);function l(t,e){this.v=shape_pool.newElement(),this.v.setPathData(!0,4),this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.paths=this.localShapeCollection,this.localShapeCollection.addShape(this.v),this.d=e.d,this.elem=t,this.comp=t.comp,this.frameId=-1,this.initDynamicPropertyContainer(t),this.p=PropertyFactory.getProp(t,e.p,1,0,this),this.s=PropertyFactory.getProp(t,e.s,1,0,this),this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertEllToPath())}var m=(f.prototype={reset:a,getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertToPath())},convertStarToPath:function(){var t,e,r,i,s=2*Math.floor(this.pt.v),a=2*Math.PI/s,n=!0,o=this.or.v,h=this.ir.v,p=this.os.v,l=this.is.v,m=2*Math.PI*o/(2*s),f=2*Math.PI*h/(2*s),c=-Math.PI/2;c+=this.r.v;var d=3===this.data.d?-1:1;for(t=this.v._length=0;t<s;t+=1){r=n?p:l,i=n?m:f;var u=(e=n?o:h)*Math.cos(c),y=e*Math.sin(c),g=0===u&&0===y?0:y/Math.sqrt(u*u+y*y),v=0===u&&0===y?0:-u/Math.sqrt(u*u+y*y);u+=+this.p.v[0],y+=+this.p.v[1],this.v.setTripleAt(u,y,u-g*i*r*d,y-v*i*r*d,u+g*i*r*d,y+v*i*r*d,t,!0),n=!n,c+=a*d}},convertPolygonToPath:function(){var t,e=Math.floor(this.pt.v),r=2*Math.PI/e,i=this.or.v,s=this.os.v,a=2*Math.PI*i/(4*e),n=-Math.PI/2,o=3===this.data.d?-1:1;for(n+=this.r.v,t=this.v._length=0;t<e;t+=1){var h=i*Math.cos(n),p=i*Math.sin(n),l=0===h&&0===p?0:p/Math.sqrt(h*h+p*p),m=0===h&&0===p?0:-h/Math.sqrt(h*h+p*p);h+=+this.p.v[0],p+=+this.p.v[1],this.v.setTripleAt(h,p,h-l*a*s*o,p-m*a*s*o,h+l*a*s*o,p+m*a*s*o,t,!0),n+=r*o}this.paths.length=0,this.paths[0]=this.v}},extendPrototype([DynamicPropertyContainer],f),f);function f(t,e){this.v=shape_pool.newElement(),this.v.setPathData(!0,0),this.elem=t,this.comp=t.comp,this.data=e,this.frameId=-1,this.d=e.d,this.initDynamicPropertyContainer(t),1===e.sy?(this.ir=PropertyFactory.getProp(t,e.ir,0,0,this),this.is=PropertyFactory.getProp(t,e.is,0,.01,this),this.convertToPath=this.convertStarToPath):this.convertToPath=this.convertPolygonToPath,this.pt=PropertyFactory.getProp(t,e.pt,0,0,this),this.p=PropertyFactory.getProp(t,e.p,1,0,this),this.r=PropertyFactory.getProp(t,e.r,0,degToRads,this),this.or=PropertyFactory.getProp(t,e.or,0,0,this),this.os=PropertyFactory.getProp(t,e.os,0,.01,this),this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.localShapeCollection.addShape(this.v),this.paths=this.localShapeCollection,this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertToPath())}var c=(d.prototype={convertRectToPath:function(){var t=this.p.v[0],e=this.p.v[1],r=this.s.v[0]/2,i=this.s.v[1]/2,s=bm_min(r,i,this.r.v),a=s*(1-roundCorner);this.v._length=0,2===this.d||1===this.d?(this.v.setTripleAt(t+r,e-i+s,t+r,e-i+s,t+r,e-i+a,0,!0),this.v.setTripleAt(t+r,e+i-s,t+r,e+i-a,t+r,e+i-s,1,!0),0!==s?(this.v.setTripleAt(t+r-s,e+i,t+r-s,e+i,t+r-a,e+i,2,!0),this.v.setTripleAt(t-r+s,e+i,t-r+a,e+i,t-r+s,e+i,3,!0),this.v.setTripleAt(t-r,e+i-s,t-r,e+i-s,t-r,e+i-a,4,!0),this.v.setTripleAt(t-r,e-i+s,t-r,e-i+a,t-r,e-i+s,5,!0),this.v.setTripleAt(t-r+s,e-i,t-r+s,e-i,t-r+a,e-i,6,!0),this.v.setTripleAt(t+r-s,e-i,t+r-a,e-i,t+r-s,e-i,7,!0)):(this.v.setTripleAt(t-r,e+i,t-r+a,e+i,t-r,e+i,2),this.v.setTripleAt(t-r,e-i,t-r,e-i+a,t-r,e-i,3))):(this.v.setTripleAt(t+r,e-i+s,t+r,e-i+a,t+r,e-i+s,0,!0),0!==s?(this.v.setTripleAt(t+r-s,e-i,t+r-s,e-i,t+r-a,e-i,1,!0),this.v.setTripleAt(t-r+s,e-i,t-r+a,e-i,t-r+s,e-i,2,!0),this.v.setTripleAt(t-r,e-i+s,t-r,e-i+s,t-r,e-i+a,3,!0),this.v.setTripleAt(t-r,e+i-s,t-r,e+i-a,t-r,e+i-s,4,!0),this.v.setTripleAt(t-r+s,e+i,t-r+s,e+i,t-r+a,e+i,5,!0),this.v.setTripleAt(t+r-s,e+i,t+r-a,e+i,t+r-s,e+i,6,!0),this.v.setTripleAt(t+r,e+i-s,t+r,e+i-s,t+r,e+i-a,7,!0)):(this.v.setTripleAt(t-r,e-i,t-r+a,e-i,t-r,e-i,1,!0),this.v.setTripleAt(t-r,e+i,t-r,e+i-a,t-r,e+i,2,!0),this.v.setTripleAt(t+r,e+i,t+r-a,e+i,t+r,e+i,3,!0)))},getValue:function(t){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertRectToPath())},reset:a},extendPrototype([DynamicPropertyContainer],d),d);function d(t,e){this.v=shape_pool.newElement(),this.v.c=!0,this.localShapeCollection=shapeCollection_pool.newShapeCollection(),this.localShapeCollection.addShape(this.v),this.paths=this.localShapeCollection,this.elem=t,this.comp=t.comp,this.frameId=-1,this.d=e.d,this.initDynamicPropertyContainer(t),this.p=PropertyFactory.getProp(t,e.p,1,0,this),this.s=PropertyFactory.getProp(t,e.s,1,0,this),this.r=PropertyFactory.getProp(t,e.r,0,0,this),this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertRectToPath())}var u={getShapeProp:function(t,e,r){var i;return 3===r||4===r?i=(3===r?e.pt:e.ks).k.length?new o(t,e,r):new n(t,e,r):5===r?i=new c(t,e):6===r?i=new p(t,e):7===r&&(i=new m(t,e)),i.k&&t.addDynamicProperty(i),i},getConstructorFunction:function(){return n},getKeyframedConstructorFunction:function(){return o}};return u}(),ShapeModifiers=(Tr={},Ur={},Tr.registerModifier=function(t,e){Ur[t]||(Ur[t]=e)},Tr.getModifier=function(t,e,r){return new Ur[t](e,r)},Tr),Tr,Ur;function ShapeModifier(){}function TrimModifier(){}function RoundCornersModifier(){}function RepeaterModifier(){}function ShapeCollection(){this._length=0,this._maxLength=4,this.shapes=createSizedArray(this._maxLength)}function DashProperty(t,e,r,i){this.elem=t,this.frameId=-1,this.dataProps=createSizedArray(e.length),this.renderer=r,this.k=!1,this.dashStr="",this.dashArray=createTypedArray("float32",e.length?e.length-1:0),this.dashoffset=createTypedArray("float32",1),this.initDynamicPropertyContainer(i);var s,a,n=e.length||0;for(s=0;s<n;s+=1)a=PropertyFactory.getProp(t,e[s].v,0,0,this),this.k=a.k||this.k,this.dataProps[s]={n:e[s].n,p:a};this.k||this.getValue(!0),this._isAnimated=this.k}function GradientProperty(t,e,r){this.data=e,this.c=createTypedArray("uint8c",4*e.p);var i=e.k.k[0].s?e.k.k[0].s.length-4*e.p:e.k.k.length-4*e.p;this.o=createTypedArray("float32",i),this._cmdf=!1,this._omdf=!1,this._collapsable=this.checkCollapsable(),this._hasOpacity=i,this.initDynamicPropertyContainer(r),this.prop=PropertyFactory.getProp(t,e.k,1,null,this),this.k=this.prop.k,this.getValue(!0)}ShapeModifier.prototype.initModifierProperties=function(){},ShapeModifier.prototype.addShapeToModifier=function(){},ShapeModifier.prototype.addShape=function(t){if(!this.closed){var e={shape:t.sh,data:t,localShapeCollection:shapeCollection_pool.newShapeCollection()};this.shapes.push(e),this.addShapeToModifier(e),this._isAnimated&&t.setAsAnimated()}},ShapeModifier.prototype.init=function(t,e){this.shapes=[],this.elem=t,this.initDynamicPropertyContainer(t),this.initModifierProperties(t,e),this.frameId=initialDefaultFrame,this.closed=!1,this.k=!1,this.dynamicProperties.length?this.k=!0:this.getValue(!0)},ShapeModifier.prototype.processKeys=function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties())},extendPrototype([DynamicPropertyContainer],ShapeModifier),extendPrototype([ShapeModifier],TrimModifier),TrimModifier.prototype.initModifierProperties=function(t,e){this.s=PropertyFactory.getProp(t,e.s,0,.01,this),this.e=PropertyFactory.getProp(t,e.e,0,.01,this),this.o=PropertyFactory.getProp(t,e.o,0,0,this),this.sValue=0,this.eValue=0,this.getValue=this.processKeys,this.m=e.m,this._isAnimated=!!this.s.effectsSequence.length||!!this.e.effectsSequence.length||!!this.o.effectsSequence.length},TrimModifier.prototype.addShapeToModifier=function(t){t.pathsData=[]},TrimModifier.prototype.calculateShapeEdges=function(t,e,r,i,s){var a=[];e<=1?a.push({s:t,e:e}):1<=t?a.push({s:t-1,e:e-1}):(a.push({s:t,e:1}),a.push({s:0,e:e-1}));var n,o,h=[],p=a.length;for(n=0;n<p;n+=1){var l,m;if((o=a[n]).e*s<i||o.s*s>i+r);else l=o.s*s<=i?0:(o.s*s-i)/r,m=o.e*s>=i+r?1:(o.e*s-i)/r,h.push([l,m])}return h.length||h.push([0,0]),h},TrimModifier.prototype.releasePathsData=function(t){var e,r=t.length;for(e=0;e<r;e+=1)segments_length_pool.release(t[e]);return t.length=0,t},TrimModifier.prototype.processShapes=function(t){var e,r,i;if(this._mdf||t){var s=this.o.v%360/360;if(s<0&&(s+=1),e=(1<this.s.v?1:this.s.v<0?0:this.s.v)+s,(r=(1<this.e.v?1:this.e.v<0?0:this.e.v)+s)<e){var a=e;e=r,r=a}e=1e-4*Math.round(1e4*e),r=1e-4*Math.round(1e4*r),this.sValue=e,this.eValue=r}else e=this.sValue,r=this.eValue;var n,o,h,p,l,m,f=this.shapes.length,c=0;if(r===e)for(n=0;n<f;n+=1)this.shapes[n].localShapeCollection.releaseShapes(),this.shapes[n].shape._mdf=!0,this.shapes[n].shape.paths=this.shapes[n].localShapeCollection;else if(1===r&&0===e||0===r&&1===e){if(this._mdf)for(n=0;n<f;n+=1)this.shapes[n].pathsData.length=0,this.shapes[n].shape._mdf=!0}else{var d,u,y=[];for(n=0;n<f;n+=1)if((d=this.shapes[n]).shape._mdf||this._mdf||t||2===this.m){if(h=(i=d.shape.paths)._length,m=0,!d.shape._mdf&&d.pathsData.length)m=d.totalShapeLength;else{for(p=this.releasePathsData(d.pathsData),o=0;o<h;o+=1)l=bez.getSegmentsLength(i.shapes[o]),p.push(l),m+=l.totalLength;d.totalShapeLength=m,d.pathsData=p}c+=m,d.shape._mdf=!0}else d.shape.paths=d.localShapeCollection;var g,v=e,P=r,b=0;for(n=f-1;0<=n;n-=1)if((d=this.shapes[n]).shape._mdf){for((u=d.localShapeCollection).releaseShapes(),2===this.m&&1<f?(g=this.calculateShapeEdges(e,r,d.totalShapeLength,b,c),b+=d.totalShapeLength):g=[[v,P]],h=g.length,o=0;o<h;o+=1){v=g[o][0],P=g[o][1],y.length=0,P<=1?y.push({s:d.totalShapeLength*v,e:d.totalShapeLength*P}):1<=v?y.push({s:d.totalShapeLength*(v-1),e:d.totalShapeLength*(P-1)}):(y.push({s:d.totalShapeLength*v,e:d.totalShapeLength}),y.push({s:0,e:d.totalShapeLength*(P-1)}));var x=this.addShapes(d,y[0]);if(y[0].s!==y[0].e){if(1<y.length)if(d.shape.paths.shapes[d.shape.paths._length-1].c){var _=x.pop();this.addPaths(x,u),x=this.addShapes(d,y[1],_)}else this.addPaths(x,u),x=this.addShapes(d,y[1]);this.addPaths(x,u)}}d.shape.paths=u}}},TrimModifier.prototype.addPaths=function(t,e){var r,i=t.length;for(r=0;r<i;r+=1)e.addShape(t[r])},TrimModifier.prototype.addSegment=function(t,e,r,i,s,a,n){s.setXYAt(e[0],e[1],"o",a),s.setXYAt(r[0],r[1],"i",a+1),n&&s.setXYAt(t[0],t[1],"v",a),s.setXYAt(i[0],i[1],"v",a+1)},TrimModifier.prototype.addSegmentFromArray=function(t,e,r,i){e.setXYAt(t[1],t[5],"o",r),e.setXYAt(t[2],t[6],"i",r+1),i&&e.setXYAt(t[0],t[4],"v",r),e.setXYAt(t[3],t[7],"v",r+1)},TrimModifier.prototype.addShapes=function(t,e,r){var i,s,a,n,o,h,p,l,m=t.pathsData,f=t.shape.paths.shapes,c=t.shape.paths._length,d=0,u=[],y=!0;for(l=r?(o=r._length,r._length):(r=shape_pool.newElement(),o=0),u.push(r),i=0;i<c;i+=1){for(h=m[i].lengths,r.c=f[i].c,a=f[i].c?h.length:h.length+1,s=1;s<a;s+=1)if(d+(n=h[s-1]).addedLength<e.s)d+=n.addedLength,r.c=!1;else{if(d>e.e){r.c=!1;break}e.s<=d&&e.e>=d+n.addedLength?(this.addSegment(f[i].v[s-1],f[i].o[s-1],f[i].i[s],f[i].v[s],r,o,y),y=!1):(p=bez.getNewSegment(f[i].v[s-1],f[i].v[s],f[i].o[s-1],f[i].i[s],(e.s-d)/n.addedLength,(e.e-d)/n.addedLength,h[s-1]),this.addSegmentFromArray(p,r,o,y),y=!1,r.c=!1),d+=n.addedLength,o+=1}if(f[i].c&&h.length){if(n=h[s-1],d<=e.e){var g=h[s-1].addedLength;e.s<=d&&e.e>=d+g?(this.addSegment(f[i].v[s-1],f[i].o[s-1],f[i].i[0],f[i].v[0],r,o,y),y=!1):(p=bez.getNewSegment(f[i].v[s-1],f[i].v[0],f[i].o[s-1],f[i].i[0],(e.s-d)/g,(e.e-d)/g,h[s-1]),this.addSegmentFromArray(p,r,o,y),y=!1,r.c=!1)}else r.c=!1;d+=n.addedLength,o+=1}if(r._length&&(r.setXYAt(r.v[l][0],r.v[l][1],"i",l),r.setXYAt(r.v[r._length-1][0],r.v[r._length-1][1],"o",r._length-1)),d>e.e)break;i<c-1&&(r=shape_pool.newElement(),y=!0,u.push(r),o=0)}return u},ShapeModifiers.registerModifier("tm",TrimModifier),extendPrototype([ShapeModifier],RoundCornersModifier),RoundCornersModifier.prototype.initModifierProperties=function(t,e){this.getValue=this.processKeys,this.rd=PropertyFactory.getProp(t,e.r,0,null,this),this._isAnimated=!!this.rd.effectsSequence.length},RoundCornersModifier.prototype.processPath=function(t,e){var r=shape_pool.newElement();r.c=t.c;var i,s,a,n,o,h,p,l,m,f,c,d,u,y=t._length,g=0;for(i=0;i<y;i+=1)s=t.v[i],n=t.o[i],a=t.i[i],s[0]===n[0]&&s[1]===n[1]&&s[0]===a[0]&&s[1]===a[1]?0!==i&&i!==y-1||t.c?(o=0===i?t.v[y-1]:t.v[i-1],p=(h=Math.sqrt(Math.pow(s[0]-o[0],2)+Math.pow(s[1]-o[1],2)))?Math.min(h/2,e)/h:0,l=d=s[0]+(o[0]-s[0])*p,m=u=s[1]-(s[1]-o[1])*p,f=l-(l-s[0])*roundCorner,c=m-(m-s[1])*roundCorner,r.setTripleAt(l,m,f,c,d,u,g),g+=1,o=i===y-1?t.v[0]:t.v[i+1],p=(h=Math.sqrt(Math.pow(s[0]-o[0],2)+Math.pow(s[1]-o[1],2)))?Math.min(h/2,e)/h:0,l=f=s[0]+(o[0]-s[0])*p,m=c=s[1]+(o[1]-s[1])*p,d=l-(l-s[0])*roundCorner,u=m-(m-s[1])*roundCorner,r.setTripleAt(l,m,f,c,d,u,g)):r.setTripleAt(s[0],s[1],n[0],n[1],a[0],a[1],g):r.setTripleAt(t.v[i][0],t.v[i][1],t.o[i][0],t.o[i][1],t.i[i][0],t.i[i][1],g),g+=1;return r},RoundCornersModifier.prototype.processShapes=function(t){var e,r,i,s,a,n,o=this.shapes.length,h=this.rd.v;if(0!==h)for(r=0;r<o;r+=1){if((a=this.shapes[r]).shape.paths,n=a.localShapeCollection,a.shape._mdf||this._mdf||t)for(n.releaseShapes(),a.shape._mdf=!0,e=a.shape.paths.shapes,s=a.shape.paths._length,i=0;i<s;i+=1)n.addShape(this.processPath(e[i],h));a.shape.paths=a.localShapeCollection}this.dynamicProperties.length||(this._mdf=!1)},ShapeModifiers.registerModifier("rd",RoundCornersModifier),extendPrototype([ShapeModifier],RepeaterModifier),RepeaterModifier.prototype.initModifierProperties=function(t,e){this.getValue=this.processKeys,this.c=PropertyFactory.getProp(t,e.c,0,null,this),this.o=PropertyFactory.getProp(t,e.o,0,null,this),this.tr=TransformPropertyFactory.getTransformProperty(t,e.tr,this),this.so=PropertyFactory.getProp(t,e.tr.so,0,.01,this),this.eo=PropertyFactory.getProp(t,e.tr.eo,0,.01,this),this.data=e,this.dynamicProperties.length||this.getValue(!0),this._isAnimated=!!this.dynamicProperties.length,this.pMatrix=new Matrix,this.rMatrix=new Matrix,this.sMatrix=new Matrix,this.tMatrix=new Matrix,this.matrix=new Matrix},RepeaterModifier.prototype.applyTransforms=function(t,e,r,i,s,a){var n=a?-1:1,o=i.s.v[0]+(1-i.s.v[0])*(1-s),h=i.s.v[1]+(1-i.s.v[1])*(1-s);t.translate(i.p.v[0]*n*s,i.p.v[1]*n*s,i.p.v[2]),e.translate(-i.a.v[0],-i.a.v[1],i.a.v[2]),e.rotate(-i.r.v*n*s),e.translate(i.a.v[0],i.a.v[1],i.a.v[2]),r.translate(-i.a.v[0],-i.a.v[1],i.a.v[2]),r.scale(a?1/o:o,a?1/h:h),r.translate(i.a.v[0],i.a.v[1],i.a.v[2])},RepeaterModifier.prototype.init=function(t,e,r,i){this.elem=t,this.arr=e,this.pos=r,this.elemsData=i,this._currentCopies=0,this._elements=[],this._groups=[],this.frameId=-1,this.initDynamicPropertyContainer(t),this.initModifierProperties(t,e[r]);for(;0<r;)r-=1,this._elements.unshift(e[r]),1;this.dynamicProperties.length?this.k=!0:this.getValue(!0)},RepeaterModifier.prototype.resetElements=function(t){var e,r=t.length;for(e=0;e<r;e+=1)t[e]._processed=!1,"gr"===t[e].ty&&this.resetElements(t[e].it)},RepeaterModifier.prototype.cloneElements=function(t){t.length;var e=JSON.parse(JSON.stringify(t));return this.resetElements(e),e},RepeaterModifier.prototype.changeGroupRender=function(t,e){var r,i=t.length;for(r=0;r<i;r+=1)t[r]._render=e,"gr"===t[r].ty&&this.changeGroupRender(t[r].it,e)},RepeaterModifier.prototype.processShapes=function(t){var e,r,i,s,a;if(this._mdf||t){var n,o=Math.ceil(this.c.v);if(this._groups.length<o){for(;this._groups.length<o;){var h={it:this.cloneElements(this._elements),ty:"gr"};h.it.push({a:{a:0,ix:1,k:[0,0]},nm:"Transform",o:{a:0,ix:7,k:100},p:{a:0,ix:2,k:[0,0]},r:{a:1,ix:6,k:[{s:0,e:0,t:0},{s:0,e:0,t:1}]},s:{a:0,ix:3,k:[100,100]},sa:{a:0,ix:5,k:0},sk:{a:0,ix:4,k:0},ty:"tr"}),this.arr.splice(0,0,h),this._groups.splice(0,0,h),this._currentCopies+=1}this.elem.reloadShapes()}for(i=a=0;i<=this._groups.length-1;i+=1)n=a<o,this._groups[i]._render=n,this.changeGroupRender(this._groups[i].it,n),a+=1;this._currentCopies=o;var p=this.o.v,l=p%1,m=0<p?Math.floor(p):Math.ceil(p),f=(this.tr.v.props,this.pMatrix.props),c=this.rMatrix.props,d=this.sMatrix.props;this.pMatrix.reset(),this.rMatrix.reset(),this.sMatrix.reset(),this.tMatrix.reset(),this.matrix.reset();var u,y,g=0;if(0<p){for(;g<m;)this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!1),g+=1;l&&(this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,l,!1),g+=l)}else if(p<0){for(;m<g;)this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!0),g-=1;l&&(this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,-l,!0),g-=l)}for(i=1===this.data.m?0:this._currentCopies-1,s=1===this.data.m?1:-1,a=this._currentCopies;a;){if(y=(r=(e=this.elemsData[i].it)[e.length-1].transform.mProps.v.props).length,e[e.length-1].transform.mProps._mdf=!0,e[e.length-1].transform.op._mdf=!0,e[e.length-1].transform.op.v=this.so.v+(this.eo.v-this.so.v)*(i/(this._currentCopies-1)),0!==g){for((0!==i&&1===s||i!==this._currentCopies-1&&-1===s)&&this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!1),this.matrix.transform(c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8],c[9],c[10],c[11],c[12],c[13],c[14],c[15]),this.matrix.transform(d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8],d[9],d[10],d[11],d[12],d[13],d[14],d[15]),this.matrix.transform(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8],f[9],f[10],f[11],f[12],f[13],f[14],f[15]),u=0;u<y;u+=1)r[u]=this.matrix.props[u];this.matrix.reset()}else for(this.matrix.reset(),u=0;u<y;u+=1)r[u]=this.matrix.props[u];g+=1,a-=1,i+=s}}else for(a=this._currentCopies,i=0,s=1;a;)r=(e=this.elemsData[i].it)[e.length-1].transform.mProps.v.props,e[e.length-1].transform.mProps._mdf=!1,e[e.length-1].transform.op._mdf=!1,a-=1,i+=s},RepeaterModifier.prototype.addShape=function(){},ShapeModifiers.registerModifier("rp",RepeaterModifier),ShapeCollection.prototype.addShape=function(t){this._length===this._maxLength&&(this.shapes=this.shapes.concat(createSizedArray(this._maxLength)),this._maxLength*=2),this.shapes[this._length]=t,this._length+=1},ShapeCollection.prototype.releaseShapes=function(){var t;for(t=0;t<this._length;t+=1)shape_pool.release(this.shapes[t]);this._length=0},DashProperty.prototype.getValue=function(t){if((this.elem.globalData.frameId!==this.frameId||t)&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf=this._mdf||t,this._mdf)){var e=0,r=this.dataProps.length;for("svg"===this.renderer&&(this.dashStr=""),e=0;e<r;e+=1)"o"!=this.dataProps[e].n?"svg"===this.renderer?this.dashStr+=" "+this.dataProps[e].p.v:this.dashArray[e]=this.dataProps[e].p.v:this.dashoffset[0]=this.dataProps[e].p.v}},extendPrototype([DynamicPropertyContainer],DashProperty),GradientProperty.prototype.comparePoints=function(t,e){for(var r=0,i=this.o.length/2;r<i;){if(.01<Math.abs(t[4*r]-t[4*e+2*r]))return!1;r+=1}return!0},GradientProperty.prototype.checkCollapsable=function(){if(this.o.length/2!=this.c.length/4)return!1;if(this.data.k.k[0].s)for(var t=0,e=this.data.k.k.length;t<e;){if(!this.comparePoints(this.data.k.k[t].s,this.data.p))return!1;t+=1}else if(!this.comparePoints(this.data.k.k,this.data.p))return!1;return!0},GradientProperty.prototype.getValue=function(t){if(this.prop.getValue(),this._mdf=!1,this._cmdf=!1,this._omdf=!1,this.prop._mdf||t){var e,r,i,s=4*this.data.p;for(e=0;e<s;e+=1)r=e%4==0?100:255,i=Math.round(this.prop.v[e]*r),this.c[e]!==i&&(this.c[e]=i,this._cmdf=!t);if(this.o.length)for(s=this.prop.v.length,e=4*this.data.p;e<s;e+=1)r=e%2==0?100:1,i=e%2==0?Math.round(100*this.prop.v[e]):this.prop.v[e],this.o[e-4*this.data.p]!==i&&(this.o[e-4*this.data.p]=i,this._omdf=!t);this._mdf=!t}},extendPrototype([DynamicPropertyContainer],GradientProperty);var buildShapeString=function(t,e,r,i){if(0===e)return"";var s,a=t.o,n=t.i,o=t.v,h=" M"+i.applyToPointStringified(o[0][0],o[0][1]);for(s=1;s<e;s+=1)h+=" C"+i.applyToPointStringified(a[s-1][0],a[s-1][1])+" "+i.applyToPointStringified(n[s][0],n[s][1])+" "+i.applyToPointStringified(o[s][0],o[s][1]);return r&&e&&(h+=" C"+i.applyToPointStringified(a[s-1][0],a[s-1][1])+" "+i.applyToPointStringified(n[0][0],n[0][1])+" "+i.applyToPointStringified(o[0][0],o[0][1]),h+="z"),h},ImagePreloader=function(){},featureSupport=(Iv={maskType:!0},(/MSIE 10/i.test(navigator.userAgent)||/MSIE 9/i.test(navigator.userAgent)||/rv:11.0/i.test(navigator.userAgent)||/Edge\/\d./i.test(navigator.userAgent))&&(Iv.maskType=!1),Iv),Iv,filtersFactory=(Jv={},Jv.createFilter=function(t){var e=createNS("filter");return e.setAttribute("id",t),e.setAttribute("filterUnits","objectBoundingBox"),e.setAttribute("x","0%"),e.setAttribute("y","0%"),e.setAttribute("width","100%"),e.setAttribute("height","100%"),e},Jv.createAlphaToLuminanceFilter=function(){var t=createNS("feColorMatrix");return t.setAttribute("type","matrix"),t.setAttribute("color-interpolation-filters","sRGB"),t.setAttribute("values","0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1"),t},Jv),Jv,assetLoader={load:function(t,e,r){var i,s=new XMLHttpRequest;s.open("GET",t,!0);try{s.responseType="json"}catch(t){}s.send(),s.onreadystatechange=function(){if(4==s.readyState)if(200==s.status)i=Pv(s),e(i);else try{i=Pv(s),e(i)}catch(t){r&&r(t)}}}};function Pv(t){return t.response&&"object"==typeof t.response?t.response:t.response&&"string"==typeof t.response?JSON.parse(t.response):t.responseText?JSON.parse(t.responseText):void 0}var assetLoader=null;function TextAnimatorProperty(t,e,r){this._isFirstFrame=!0,this._hasMaskedPath=!1,this._frameId=-1,this._textData=t,this._renderType=e,this._elem=r,this._animatorsData=createSizedArray(this._textData.a.length),this._pathData={},this._moreOptions={alignment:{}},this.renderedLetters=[],this.lettersChangedFlag=!1,this.initDynamicPropertyContainer(r)}function TextAnimatorDataProperty(t,e,r){var i={propType:!1},s=PropertyFactory.getProp,a=e.a;this.a={r:a.r?s(t,a.r,0,degToRads,r):i,rx:a.rx?s(t,a.rx,0,degToRads,r):i,ry:a.ry?s(t,a.ry,0,degToRads,r):i,sk:a.sk?s(t,a.sk,0,degToRads,r):i,sa:a.sa?s(t,a.sa,0,degToRads,r):i,s:a.s?s(t,a.s,1,.01,r):i,a:a.a?s(t,a.a,1,0,r):i,o:a.o?s(t,a.o,0,.01,r):i,p:a.p?s(t,a.p,1,0,r):i,sw:a.sw?s(t,a.sw,0,0,r):i,sc:a.sc?s(t,a.sc,1,0,r):i,fc:a.fc?s(t,a.fc,1,0,r):i,fh:a.fh?s(t,a.fh,0,0,r):i,fs:a.fs?s(t,a.fs,0,.01,r):i,fb:a.fb?s(t,a.fb,0,.01,r):i,t:a.t?s(t,a.t,0,0,r):i},this.s=TextSelectorProp.getTextSelectorProp(t,e.s,r),this.s.t=e.s.t}function LetterProps(t,e,r,i,s,a){this.o=t,this.sw=e,this.sc=r,this.fc=i,this.m=s,this.p=a,this._mdf={o:!0,sw:!!e,sc:!!r,fc:!!i,m:!0,p:!0}}function TextProperty(t,e){this._frameId=initialDefaultFrame,this.pv="",this.v="",this.kf=!1,this._isFirstFrame=!0,this._mdf=!1,this.data=e,this.elem=t,this.comp=this.elem.comp,this.keysIndex=0,this.canResize=!1,this.minimumFontSize=1,this.effectsSequence=[],this.currentData={ascent:0,boxWidth:this.defaultBoxWidth,f:"",fStyle:"",fWeight:"",fc:"",j:"",justifyOffset:"",l:[],lh:0,lineWidths:[],ls:"",of:"",s:"",sc:"",sw:0,t:0,tr:0,sz:0,ps:null,fillColorAnim:!1,strokeColorAnim:!1,strokeWidthAnim:!1,yOffset:0,finalSize:0,finalText:[],finalLineHeight:0,__complete:!1},this.copyData(this.currentData,this.data.d.k[0].s),this.searchProperty()||this.completeTextData(this.currentData)}TextAnimatorProperty.prototype.searchProperties=function(){var t,e,r=this._textData.a.length,i=PropertyFactory.getProp;for(t=0;t<r;t+=1)e=this._textData.a[t],this._animatorsData[t]=new TextAnimatorDataProperty(this._elem,e,this);this._textData.p&&"m"in this._textData.p?(this._pathData={f:i(this._elem,this._textData.p.f,0,0,this),l:i(this._elem,this._textData.p.l,0,0,this),r:this._textData.p.r,m:this._elem.maskManager.getMaskProperty(this._textData.p.m)},this._hasMaskedPath=!0):this._hasMaskedPath=!1,this._moreOptions.alignment=i(this._elem,this._textData.m.a,1,0,this)},TextAnimatorProperty.prototype.getMeasures=function(t,e){if(this.lettersChangedFlag=e,this._mdf||this._isFirstFrame||e||this._hasMaskedPath&&this._pathData.m._mdf){this._isFirstFrame=!1;var r,i,s,a,n,o,h,p,l,m,f,c,d,u,y,g,v,P,b,x=this._moreOptions.alignment.v,_=this._animatorsData,S=this._textData,T=this.mHelper,A=this._renderType,C=this.renderedLetters.length,E=(this.data,t.l);if(this._hasMaskedPath){if(b=this._pathData.m,!this._pathData.n||this._pathData._mdf){var k,D=b.v;for(this._pathData.r&&(D=D.reverse()),n={tLength:0,segments:[]},a=D._length-1,s=g=0;s<a;s+=1)k=bez.buildBezierData(D.v[s],D.v[s+1],[D.o[s][0]-D.v[s][0],D.o[s][1]-D.v[s][1]],[D.i[s+1][0]-D.v[s+1][0],D.i[s+1][1]-D.v[s+1][1]]),n.tLength+=k.segmentLength,n.segments.push(k),g+=k.segmentLength;s=a,b.v.c&&(k=bez.buildBezierData(D.v[s],D.v[0],[D.o[s][0]-D.v[s][0],D.o[s][1]-D.v[s][1]],[D.i[0][0]-D.v[0][0],D.i[0][1]-D.v[0][1]]),n.tLength+=k.segmentLength,n.segments.push(k),g+=k.segmentLength),this._pathData.pi=n}if(n=this._pathData.pi,o=this._pathData.f.v,m=1,l=!(p=f=0),u=n.segments,o<0&&b.v.c)for(n.tLength<Math.abs(o)&&(o=-Math.abs(o)%n.tLength),m=(d=u[f=u.length-1].points).length-1;o<0;)o+=d[m].partialLength,(m-=1)<0&&(m=(d=u[f-=1].points).length-1);c=(d=u[f].points)[m-1],y=(h=d[m]).partialLength}a=E.length,i=r=0;var I,M,w,F,V=1.2*t.finalSize*.714,R=!0;w=_.length;var L,z,O,B,N,G,j,J,K,q,H,W,Y,X=-1,Q=o,$=f,U=m,Z=-1,tt="",et=this.defaultPropsArray;if(2===t.j||1===t.j){var rt=0,it=0,st=2===t.j?-.5:-1,at=0,nt=!0;for(s=0;s<a;s+=1)if(E[s].n){for(rt&&(rt+=it);at<s;)E[at].animatorJustifyOffset=rt,at+=1;nt=!(rt=0)}else{for(M=0;M<w;M+=1)(I=_[M].a).t.propType&&(nt&&2===t.j&&(it+=I.t.v*st),(L=_[M].s.getMult(E[s].anIndexes[M],S.a[M].s.totalChars)).length?rt+=I.t.v*L[0]*st:rt+=I.t.v*L*st);nt=!1}for(rt&&(rt+=it);at<s;)E[at].animatorJustifyOffset=rt,at+=1}for(s=0;s<a;s+=1){if(T.reset(),N=1,E[s].n)r=0,i+=t.yOffset,i+=R?1:0,o=Q,R=!1,0,this._hasMaskedPath&&(m=U,c=(d=u[f=$].points)[m-1],y=(h=d[m]).partialLength,p=0),Y=q=W=tt="",et=this.defaultPropsArray;else{if(this._hasMaskedPath){if(Z!==E[s].line){switch(t.j){case 1:o+=g-t.lineWidths[E[s].line];break;case 2:o+=(g-t.lineWidths[E[s].line])/2}Z=E[s].line}X!==E[s].ind&&(E[X]&&(o+=E[X].extra),o+=E[s].an/2,X=E[s].ind),o+=x[0]*E[s].an/200;var ot=0;for(M=0;M<w;M+=1)(I=_[M].a).p.propType&&((L=_[M].s.getMult(E[s].anIndexes[M],S.a[M].s.totalChars)).length?ot+=I.p.v[0]*L[0]:ot+=I.p.v[0]*L),I.a.propType&&((L=_[M].s.getMult(E[s].anIndexes[M],S.a[M].s.totalChars)).length?ot+=I.a.v[0]*L[0]:ot+=I.a.v[0]*L);for(l=!0;l;)o+ot<=p+y||!d?(v=(o+ot-p)/h.partialLength,O=c.point[0]+(h.point[0]-c.point[0])*v,B=c.point[1]+(h.point[1]-c.point[1])*v,T.translate(-x[0]*E[s].an/200,-x[1]*V/100),l=!1):d&&(p+=h.partialLength,(m+=1)>=d.length&&(m=0,d=u[f+=1]?u[f].points:b.v.c?u[f=m=0].points:(p-=h.partialLength,null)),d&&(c=h,y=(h=d[m]).partialLength));z=E[s].an/2-E[s].add,T.translate(-z,0,0)}else z=E[s].an/2-E[s].add,T.translate(-z,0,0),T.translate(-x[0]*E[s].an/200,-x[1]*V/100,0);for(E[s].l/2,M=0;M<w;M+=1)(I=_[M].a).t.propType&&(L=_[M].s.getMult(E[s].anIndexes[M],S.a[M].s.totalChars),0===r&&0===t.j||(this._hasMaskedPath?L.length?o+=I.t.v*L[0]:o+=I.t.v*L:L.length?r+=I.t.v*L[0]:r+=I.t.v*L));for(E[s].l/2,t.strokeWidthAnim&&(j=t.sw||0),t.strokeColorAnim&&(G=t.sc?[t.sc[0],t.sc[1],t.sc[2]]:[0,0,0]),t.fillColorAnim&&t.fc&&(J=[t.fc[0],t.fc[1],t.fc[2]]),M=0;M<w;M+=1)(I=_[M].a).a.propType&&((L=_[M].s.getMult(E[s].anIndexes[M],S.a[M].s.totalChars)).length?T.translate(-I.a.v[0]*L[0],-I.a.v[1]*L[1],I.a.v[2]*L[2]):T.translate(-I.a.v[0]*L,-I.a.v[1]*L,I.a.v[2]*L));for(M=0;M<w;M+=1)(I=_[M].a).s.propType&&((L=_[M].s.getMult(E[s].anIndexes[M],S.a[M].s.totalChars)).length?T.scale(1+(I.s.v[0]-1)*L[0],1+(I.s.v[1]-1)*L[1],1):T.scale(1+(I.s.v[0]-1)*L,1+(I.s.v[1]-1)*L,1));for(M=0;M<w;M+=1){if(I=_[M].a,L=_[M].s.getMult(E[s].anIndexes[M],S.a[M].s.totalChars),I.sk.propType&&(L.length?T.skewFromAxis(-I.sk.v*L[0],I.sa.v*L[1]):T.skewFromAxis(-I.sk.v*L,I.sa.v*L)),I.r.propType&&(L.length?T.rotateZ(-I.r.v*L[2]):T.rotateZ(-I.r.v*L)),I.ry.propType&&(L.length?T.rotateY(I.ry.v*L[1]):T.rotateY(I.ry.v*L)),I.rx.propType&&(L.length?T.rotateX(I.rx.v*L[0]):T.rotateX(I.rx.v*L)),I.o.propType&&(L.length?N+=(I.o.v*L[0]-N)*L[0]:N+=(I.o.v*L-N)*L),t.strokeWidthAnim&&I.sw.propType&&(L.length?j+=I.sw.v*L[0]:j+=I.sw.v*L),t.strokeColorAnim&&I.sc.propType)for(K=0;K<3;K+=1)L.length?G[K]=G[K]+(I.sc.v[K]-G[K])*L[0]:G[K]=G[K]+(I.sc.v[K]-G[K])*L;if(t.fillColorAnim&&t.fc){if(I.fc.propType)for(K=0;K<3;K+=1)L.length?J[K]=J[K]+(I.fc.v[K]-J[K])*L[0]:J[K]=J[K]+(I.fc.v[K]-J[K])*L;I.fh.propType&&(J=L.length?addHueToRGB(J,I.fh.v*L[0]):addHueToRGB(J,I.fh.v*L)),I.fs.propType&&(J=L.length?addSaturationToRGB(J,I.fs.v*L[0]):addSaturationToRGB(J,I.fs.v*L)),I.fb.propType&&(J=L.length?addBrightnessToRGB(J,I.fb.v*L[0]):addBrightnessToRGB(J,I.fb.v*L))}}for(M=0;M<w;M+=1)(I=_[M].a).p.propType&&(L=_[M].s.getMult(E[s].anIndexes[M],S.a[M].s.totalChars),this._hasMaskedPath?L.length?T.translate(0,I.p.v[1]*L[0],-I.p.v[2]*L[1]):T.translate(0,I.p.v[1]*L,-I.p.v[2]*L):L.length?T.translate(I.p.v[0]*L[0],I.p.v[1]*L[1],-I.p.v[2]*L[2]):T.translate(I.p.v[0]*L,I.p.v[1]*L,-I.p.v[2]*L));if(t.strokeWidthAnim&&(q=j<0?0:j),t.strokeColorAnim&&(H="rgb("+Math.round(255*G[0])+","+Math.round(255*G[1])+","+Math.round(255*G[2])+")"),t.fillColorAnim&&t.fc&&(W="rgb("+Math.round(255*J[0])+","+Math.round(255*J[1])+","+Math.round(255*J[2])+")"),this._hasMaskedPath){if(T.translate(0,-t.ls),T.translate(0,x[1]*V/100+i,0),S.p.p){P=(h.point[1]-c.point[1])/(h.point[0]-c.point[0]);var ht=180*Math.atan(P)/Math.PI;h.point[0]<c.point[0]&&(ht+=180),T.rotate(-ht*Math.PI/180)}T.translate(O,B,0),o-=x[0]*E[s].an/200,E[s+1]&&X!==E[s+1].ind&&(o+=E[s].an/2,o+=t.tr/1e3*t.finalSize)}else{switch(T.translate(r,i,0),t.ps&&T.translate(t.ps[0],t.ps[1]+t.ascent,0),t.j){case 1:T.translate(E[s].animatorJustifyOffset+t.justifyOffset+(t.boxWidth-t.lineWidths[E[s].line]),0,0);break;case 2:T.translate(E[s].animatorJustifyOffset+t.justifyOffset+(t.boxWidth-t.lineWidths[E[s].line])/2,0,0)}T.translate(0,-t.ls),T.translate(z,0,0),T.translate(x[0]*E[s].an/200,x[1]*V/100,0),r+=E[s].l+t.tr/1e3*t.finalSize}"html"===A?tt=T.toCSS():"svg"===A?tt=T.to2dCSS():et=[T.props[0],T.props[1],T.props[2],T.props[3],T.props[4],T.props[5],T.props[6],T.props[7],T.props[8],T.props[9],T.props[10],T.props[11],T.props[12],T.props[13],T.props[14],T.props[15]],Y=N}C<=s?(F=new LetterProps(Y,q,H,W,tt,et),this.renderedLetters.push(F),C+=1,this.lettersChangedFlag=!0):(F=this.renderedLetters[s],this.lettersChangedFlag=F.update(Y,q,H,W,tt,et)||this.lettersChangedFlag)}}},TextAnimatorProperty.prototype.getValue=function(){this._elem.globalData.frameId!==this._frameId&&(this._frameId=this._elem.globalData.frameId,this.iterateDynamicProperties())},TextAnimatorProperty.prototype.mHelper=new Matrix,TextAnimatorProperty.prototype.defaultPropsArray=[],extendPrototype([DynamicPropertyContainer],TextAnimatorProperty),LetterProps.prototype.update=function(t,e,r,i,s,a){this._mdf.o=!1,this._mdf.sw=!1,this._mdf.sc=!1,this._mdf.fc=!1,this._mdf.m=!1;var n=this._mdf.p=!1;return this.o!==t&&(this.o=t,n=this._mdf.o=!0),this.sw!==e&&(this.sw=e,n=this._mdf.sw=!0),this.sc!==r&&(this.sc=r,n=this._mdf.sc=!0),this.fc!==i&&(this.fc=i,n=this._mdf.fc=!0),this.m!==s&&(this.m=s,n=this._mdf.m=!0),!a.length||this.p[0]===a[0]&&this.p[1]===a[1]&&this.p[4]===a[4]&&this.p[5]===a[5]&&this.p[12]===a[12]&&this.p[13]===a[13]||(this.p=a,n=this._mdf.p=!0),n},TextProperty.prototype.defaultBoxWidth=[0,0],TextProperty.prototype.copyData=function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r]);return t},TextProperty.prototype.setCurrentData=function(t){t.__complete||this.completeTextData(t),this.currentData=t,this.currentData.boxWidth=this.currentData.boxWidth||this.defaultBoxWidth,this._mdf=!0},TextProperty.prototype.searchProperty=function(){return this.searchKeyframes()},TextProperty.prototype.searchKeyframes=function(){return this.kf=1<this.data.d.k.length,this.kf&&this.addEffect(this.getKeyframeValue.bind(this)),this.kf},TextProperty.prototype.addEffect=function(t){this.effectsSequence.push(t),this.elem.addDynamicProperty(this)},TextProperty.prototype.getValue=function(t){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length||t){this.currentData.t=this.data.d.k[this.keysIndex].s.t;var e=this.currentData,r=this.keysIndex;if(this.lock)this.setCurrentData(this.currentData);else{this.lock=!0,this._mdf=!1;var i,s=this.effectsSequence.length,a=t||this.data.d.k[this.keysIndex].s;for(i=0;i<s;i+=1)a=r!==this.keysIndex?this.effectsSequence[i](a,a.t):this.effectsSequence[i](this.currentData,a.t);e!==a&&this.setCurrentData(a),this.pv=this.v=this.currentData,this.lock=!1,this.frameId=this.elem.globalData.frameId}}},TextProperty.prototype.getKeyframeValue=function(){for(var t=this.data.d.k,e=this.elem.comp.renderedFrame,r=0,i=t.length;r<=i-1&&(t[r].s,!(r===i-1||t[r+1].t>e));)r+=1;return this.keysIndex!==r&&(this.keysIndex=r),this.data.d.k[this.keysIndex].s},TextProperty.prototype.buildFinalText=function(t){for(var e=FontManager.getCombinedCharacterCodes(),r=[],i=0,s=t.length;i<s;)-1!==e.indexOf(t.charCodeAt(i))?r[r.length-1]+=t.charAt(i):r.push(t.charAt(i)),i+=1;return r},TextProperty.prototype.completeTextData=function(t){t.__complete=!0;var e,r,i,s,a,n,o,h=this.elem.globalData.fontManager,p=this.data,l=[],m=0,f=p.m.g,c=0,d=0,u=0,y=[],g=0,v=0,P=h.getFontByName(t.f),b=0,x=P.fStyle?P.fStyle.split(" "):[],_="normal",S="normal";for(r=x.length,e=0;e<r;e+=1)switch(x[e].toLowerCase()){case"italic":S="italic";break;case"bold":_="700";break;case"black":_="900";break;case"medium":_="500";break;case"regular":case"normal":_="400";break;case"light":case"thin":_="200"}t.fWeight=P.fWeight||_,t.fStyle=S,r=t.t.length,t.finalSize=t.s,t.finalText=this.buildFinalText(t.t),t.finalLineHeight=t.lh;var T,A=t.tr/1e3*t.finalSize;if(t.sz)for(var C,E,k=!0,D=t.sz[0],I=t.sz[1];k;){g=C=0,r=(E=this.buildFinalText(t.t)).length,A=t.tr/1e3*t.finalSize;var M=-1;for(e=0;e<r;e+=1)T=E[e].charCodeAt(0),i=!1," "===E[e]?M=e:13!==T&&3!==T||(i=!(g=0),C+=t.finalLineHeight||1.2*t.finalSize),D<g+(b=h.chars?(o=h.getCharData(E[e],P.fStyle,P.fFamily),i?0:o.w*t.finalSize/100):h.measureText(E[e],t.f,t.finalSize))&&" "!==E[e]?(-1===M?r+=1:e=M,C+=t.finalLineHeight||1.2*t.finalSize,E.splice(e,M===e?1:0,"\r"),M=-1,g=0):(g+=b,g+=A);C+=P.ascent*t.finalSize/100,this.canResize&&t.finalSize>this.minimumFontSize&&I<C?(t.finalSize-=1,t.finalLineHeight=t.finalSize*t.lh/t.s):(t.finalText=E,r=t.finalText.length,k=!1)}g=-A;var w,F=b=0;for(e=0;e<r;e+=1)if(i=!1,T=(w=t.finalText[e]).charCodeAt(0)," "===w?s="\xa0":13===T||3===T?(F=0,y.push(g),v=v<g?g:v,g=-2*A,i=!(s=""),u+=1):s=t.finalText[e],b=h.chars?(o=h.getCharData(w,P.fStyle,h.getFontByName(t.f).fFamily),i?0:o.w*t.finalSize/100):h.measureText(s,t.f,t.finalSize)," "===w?F+=b+A:(g+=b+A+F,F=0),l.push({l:b,an:b,add:c,n:i,anIndexes:[],val:s,line:u,animatorJustifyOffset:0}),2==f){if(c+=b,""===s||"\xa0"===s||e===r-1){for(""!==s&&"\xa0"!==s||(c-=b);d<=e;)l[d].an=c,l[d].ind=m,l[d].extra=b,d+=1;m+=1,c=0}}else if(3==f){if(c+=b,""===s||e===r-1){for(""===s&&(c-=b);d<=e;)l[d].an=c,l[d].ind=m,l[d].extra=b,d+=1;c=0,m+=1}}else l[m].ind=m,l[m].extra=0,m+=1;if(t.l=l,v=v<g?g:v,y.push(g),t.sz)t.boxWidth=t.sz[0],t.justifyOffset=0;else switch(t.boxWidth=v,t.j){case 1:t.justifyOffset=-t.boxWidth;break;case 2:t.justifyOffset=-t.boxWidth/2;break;default:t.justifyOffset=0}t.lineWidths=y;var V,R,L=p.a;n=L.length;var z,O,B=[];for(a=0;a<n;a+=1){for((V=L[a]).a.sc&&(t.strokeColorAnim=!0),V.a.sw&&(t.strokeWidthAnim=!0),(V.a.fc||V.a.fh||V.a.fs||V.a.fb)&&(t.fillColorAnim=!0),O=0,z=V.s.b,e=0;e<r;e+=1)(R=l[e]).anIndexes[a]=O,(1==z&&""!==R.val||2==z&&""!==R.val&&"\xa0"!==R.val||3==z&&(R.n||"\xa0"==R.val||e==r-1)||4==z&&(R.n||e==r-1))&&(1===V.s.rn&&B.push(O),O+=1);p.a[a].s.totalChars=O;var N,G=-1;if(1===V.s.rn)for(e=0;e<r;e+=1)G!=(R=l[e]).anIndexes[a]&&(G=R.anIndexes[a],N=B.splice(Math.floor(Math.random()*B.length),1)[0]),R.anIndexes[a]=N}t.yOffset=t.finalLineHeight||1.2*t.finalSize,t.ls=t.ls||0,t.ascent=P.ascent*t.finalSize/100},TextProperty.prototype.updateDocumentData=function(t,e){e=void 0===e?this.keysIndex:e;var r=this.copyData({},this.data.d.k[e].s);r=this.copyData(r,t),this.data.d.k[e].s=r,this.recalculate(e),this.elem.addDynamicProperty(this)},TextProperty.prototype.recalculate=function(t){var e=this.data.d.k[t].s;e.__complete=!1,this.keysIndex=0,this._isFirstFrame=!0,this.getValue(e)},TextProperty.prototype.canResizeFont=function(t){this.canResize=t,this.recalculate(this.keysIndex),this.elem.addDynamicProperty(this)},TextProperty.prototype.setMinimumFontSize=function(t){this.minimumFontSize=Math.floor(t)||1,this.recalculate(this.keysIndex),this.elem.addDynamicProperty(this)};var TextSelectorProp=(cz=Math.max,dz=Math.min,ez=Math.floor,fz.prototype={getMult:function(t){this._currentTextLength!==this.elem.textProperty.currentData.l.length&&this.getValue();var e=BezierFactory.getBezierEasing(this.ne.v/100,0,1-this.xe.v/100,1).get,r=0,i=this.finalS,s=this.finalE,a=this.data.sh;if(2==a)r=e(r=s===i?s<=t?1:0:cz(0,dz(.5/(s-i)+(t-i)/(s-i),1)));else if(3==a)r=e(r=s===i?s<=t?0:1:1-cz(0,dz(.5/(s-i)+(t-i)/(s-i),1)));else if(4==a)s===i?r=0:(r=cz(0,dz(.5/(s-i)+(t-i)/(s-i),1)))<.5?r*=2:r=1-2*(r-.5),r=e(r);else if(5==a){if(s===i)r=0;else{var n=s-i,o=-n/2+(t=dz(cz(0,t+.5-i),s-i)),h=n/2;r=Math.sqrt(1-o*o/(h*h))}r=e(r)}else r=6==a?e(r=s===i?0:(t=dz(cz(0,t+.5-i),s-i),(1+Math.cos(Math.PI+2*Math.PI*t/(s-i)))/2)):(t>=ez(i)&&(r=t-i<0?1-(i-t):cz(0,dz(s-t,1))),e(r));return r*this.a.v},getValue:function(t){this.iterateDynamicProperties(),this._mdf=t||this._mdf,this._currentTextLength=this.elem.textProperty.currentData.l.length||0,t&&2===this.data.r&&(this.e.v=this._currentTextLength);var e=2===this.data.r?1:100/this.data.totalChars,r=this.o.v/e,i=this.s.v/e+r,s=this.e.v/e+r;if(s<i){var a=i;i=s,s=a}this.finalS=i,this.finalE=s}},extendPrototype([DynamicPropertyContainer],fz),{getTextSelectorProp:function(t,e,r){return new fz(t,e,r)}}),cz,dz,ez;function fz(t,e){this._currentTextLength=-1,this.k=!1,this.data=e,this.elem=t,this.comp=t.comp,this.finalS=0,this.finalE=0,this.initDynamicPropertyContainer(t),this.s=PropertyFactory.getProp(t,e.s||{k:0},0,0,this),this.e="e"in e?PropertyFactory.getProp(t,e.e,0,0,this):{v:100},this.o=PropertyFactory.getProp(t,e.o||{k:0},0,0,this),this.xe=PropertyFactory.getProp(t,e.xe||{k:0},0,0,this),this.ne=PropertyFactory.getProp(t,e.ne||{k:0},0,0,this),this.a=PropertyFactory.getProp(t,e.a,0,.01,this),this.dynamicProperties.length||this.getValue()}var pool_factory=function(t,e,r,i){var s=0,a=t,n=createSizedArray(a);function o(){return s?n[s-=1]:e()}return{newElement:o,release:function(t){s===a&&(n=pooling.double(n),a*=2),r&&r(t),n[s]=t,s+=1}}},pooling={double:function(t){return t.concat(createSizedArray(t.length))}},point_pool=pool_factory(8,function(){return createTypedArray("float32",2)}),shape_pool=(Vz=pool_factory(4,function(){return new ShapePath},function(t){var e,r=t._length;for(e=0;e<r;e+=1)point_pool.release(t.v[e]),point_pool.release(t.i[e]),point_pool.release(t.o[e]),t.v[e]=null,t.i[e]=null,t.o[e]=null;t._length=0,t.c=!1}),Vz.clone=function(t){var e,r=Vz.newElement(),i=void 0===t._length?t.v.length:t._length;for(r.setLength(i),r.c=t.c,e=0;e<i;e+=1)r.setTripleAt(t.v[e][0],t.v[e][1],t.o[e][0],t.o[e][1],t.i[e][0],t.i[e][1],e);return r},Vz),Vz,shapeCollection_pool=(cA={newShapeCollection:function(){var t;t=dA?fA[dA-=1]:new ShapeCollection;return t},release:function(t){var e,r=t._length;for(e=0;e<r;e+=1)shape_pool.release(t.shapes[e]);t._length=0,dA===eA&&(fA=pooling.double(fA),eA*=2);fA[dA]=t,dA+=1}},dA=0,eA=4,fA=createSizedArray(eA),cA),cA,dA,eA,fA,segments_length_pool=pool_factory(8,function(){return{lengths:[],totalLength:0}},function(t){var e,r=t.lengths.length;for(e=0;e<r;e+=1)bezier_length_pool.release(t.lengths[e]);t.lengths.length=0}),bezier_length_pool=pool_factory(8,function(){return{addedLength:0,percents:createTypedArray("float32",defaultCurveSegments),lengths:createTypedArray("float32",defaultCurveSegments)}});function BaseRenderer(){}function SVGRenderer(t,e){this.animationItem=t,this.layers=null,this.renderedFrame=-1,this.svgElement=createNS("svg");var r="";if(e&&e.title){var i=createNS("title"),s=createElementID();i.setAttribute("id",s),i.textContent=e.title,this.svgElement.appendChild(i),r+=s}if(e&&e.description){var a=createNS("desc"),n=createElementID();a.setAttribute("id",n),a.textContent=e.description,this.svgElement.appendChild(a),r+=" "+n}r&&this.svgElement.setAttribute("aria-labelledby",r);var o=createNS("defs");this.svgElement.appendChild(o);var h=createNS("g");this.svgElement.appendChild(h),this.layerElement=h,this.renderConfig={preserveAspectRatio:e&&e.preserveAspectRatio||"xMidYMid meet",imagePreserveAspectRatio:e&&e.imagePreserveAspectRatio||"xMidYMid slice",progressiveLoad:e&&e.progressiveLoad||!1,hideOnTransparent:!e||!1!==e.hideOnTransparent,viewBoxOnly:e&&e.viewBoxOnly||!1,viewBoxSize:e&&e.viewBoxSize||!1,className:e&&e.className||""},this.globalData={_mdf:!1,frameNum:-1,defs:o,renderConfig:this.renderConfig},this.elements=[],this.pendingElements=[],this.destroyed=!1,this.rendererType="svg"}function CanvasRenderer(t,e){this.animationItem=t,this.renderConfig={clearCanvas:!e||void 0===e.clearCanvas||e.clearCanvas,context:e&&e.context||null,progressiveLoad:e&&e.progressiveLoad||!1,preserveAspectRatio:e&&e.preserveAspectRatio||"xMidYMid meet",imagePreserveAspectRatio:e&&e.imagePreserveAspectRatio||"xMidYMid slice",className:e&&e.className||""},this.renderConfig.dpr=e&&e.dpr||1,this.animationItem.wrapper&&(this.renderConfig.dpr=e&&e.dpr||window.devicePixelRatio||1),this.renderedFrame=-1,this.globalData={frameNum:-1,_mdf:!1,renderConfig:this.renderConfig,currentGlobalAlpha:-1},this.contextData=new CVContextData,this.elements=[],this.pendingElements=[],this.transformMat=new Matrix,this.completeLayers=!1,this.rendererType="canvas"}function MaskElement(t,e,r){this.data=t,this.element=e,this.globalData=r,this.storedData=[],this.masksProperties=this.data.masksProperties||[],this.maskElement=null;var i,s=this.globalData.defs,a=this.masksProperties?this.masksProperties.length:0;this.viewData=createSizedArray(a),this.solidPath="";var n,o,h,p,l,m,f,c=this.masksProperties,d=0,u=[],y=createElementID(),g="clipPath",v="clip-path";for(i=0;i<a;i++)if(("a"!==c[i].mode&&"n"!==c[i].mode||c[i].inv||100!==c[i].o.k)&&(v=g="mask"),"s"!=c[i].mode&&"i"!=c[i].mode||0!==d?p=null:((p=createNS("rect")).setAttribute("fill","#ffffff"),p.setAttribute("width",this.element.comp.data.w||0),p.setAttribute("height",this.element.comp.data.h||0),u.push(p)),n=createNS("path"),"n"!=c[i].mode){var P;if(d+=1,n.setAttribute("fill","s"===c[i].mode?"#000000":"#ffffff"),n.setAttribute("clip-rule","nonzero"),0!==c[i].x.k?(v=g="mask",f=PropertyFactory.getProp(this.element,c[i].x,0,null,this.element),P=createElementID(),(l=createNS("filter")).setAttribute("id",P),(m=createNS("feMorphology")).setAttribute("operator","erode"),m.setAttribute("in","SourceGraphic"),m.setAttribute("radius","0"),l.appendChild(m),s.appendChild(l),n.setAttribute("stroke","s"===c[i].mode?"#000000":"#ffffff")):f=m=null,this.storedData[i]={elem:n,x:f,expan:m,lastPath:"",lastOperator:"",filterId:P,lastRadius:0},"i"==c[i].mode){h=u.length;var b=createNS("g");for(o=0;o<h;o+=1)b.appendChild(u[o]);var x=createNS("mask");x.setAttribute("mask-type","alpha"),x.setAttribute("id",y+"_"+d),x.appendChild(n),s.appendChild(x),b.setAttribute("mask","url("+locationHref+"#"+y+"_"+d+")"),u.length=0,u.push(b)}else u.push(n);c[i].inv&&!this.solidPath&&(this.solidPath=this.createLayerSolidPath()),this.viewData[i]={elem:n,lastPath:"",op:PropertyFactory.getProp(this.element,c[i].o,0,.01,this.element),prop:ShapePropertyFactory.getShapeProp(this.element,c[i],3),invRect:p},this.viewData[i].prop.k||this.drawPath(c[i],this.viewData[i].prop.v,this.viewData[i])}else this.viewData[i]={op:PropertyFactory.getProp(this.element,c[i].o,0,.01,this.element),prop:ShapePropertyFactory.getShapeProp(this.element,c[i],3),elem:n,lastPath:""},s.appendChild(n);for(this.maskElement=createNS(g),a=u.length,i=0;i<a;i+=1)this.maskElement.appendChild(u[i]);0<d&&(this.maskElement.setAttribute("id",y),this.element.maskedElement.setAttribute(v,"url("+locationHref+"#"+y+")"),s.appendChild(this.maskElement)),this.viewData.length&&this.element.addRenderableComponent(this)}function HierarchyElement(){}function FrameElement(){}function TransformElement(){}function RenderableElement(){}function RenderableDOMElement(){}function ProcessedElement(t,e){this.elem=t,this.pos=e}function SVGShapeData(t,e,r){this.caches=[],this.styles=[],this.transformers=t,this.lStr="",this.sh=r,this.lvl=e,this._isAnimated=!!r.k;for(var i=0,s=t.length;i<s;){if(t[i].mProps.dynamicProperties.length){this._isAnimated=!0;break}i+=1}}function ShapeGroupData(){this.it=[],this.prevViewData=[],this.gr=createNS("g")}function ShapeTransformManager(){this.sequences={},this.sequenceList=[],this.transform_key_count=0}function CVShapeData(t,e,r,i){this.styledShapes=[],this.tr=[0,0,0,0,0,0];var s=4;"rc"==e.ty?s=5:"el"==e.ty?s=6:"sr"==e.ty&&(s=7),this.sh=ShapePropertyFactory.getShapeProp(t,e,s,t);var a,n,o=r.length;for(a=0;a<o;a+=1)r[a].closed||(n={transforms:i.addTransformSequence(r[a].transforms),trNodes:[]},this.styledShapes.push(n),r[a].elements.push(n))}function BaseElement(){}function NullElement(t,e,r){this.initFrame(),this.initBaseData(t,e,r),this.initFrame(),this.initTransform(t,e,r),this.initHierarchy()}function SVGBaseElement(){}function IShapeElement(){}function ITextElement(){}function ICompElement(){}function IImageElement(t,e,r){this.assetData=e.getAssetData(t.refId),this.initElement(t,e,r),this.sourceRect={top:0,left:0,width:this.assetData.w,height:this.assetData.h}}function ISolidElement(t,e,r){this.initElement(t,e,r)}function SVGShapeElement(t,e,r){this.shapes=[],this.shapesData=t.shapes,this.stylesList=[],this.shapeModifiers=[],this.itemsData=[],this.processedElements=[],this.animatedContents=[],this.initElement(t,e,r),this.prevViewData=[]}function CVContextData(){this.saved=[],this.cArrPos=0,this.cTr=new Matrix,this.cO=1;var t;for(this.savedOp=createTypedArray("float32",15),t=0;t<15;t+=1)this.saved[t]=createTypedArray("float32",16);this._length=15}function CVBaseElement(){}function CVCompElement(t,e,r){this.completeLayers=!1,this.layers=t.layers,this.pendingElements=[],this.elements=createSizedArray(this.layers.length),this.initElement(t,e,r),this.tm=t.tm?PropertyFactory.getProp(this,t.tm,0,e.frameRate,this):{_placeholder:!0}}function CVMaskElement(t,e){this.data=t,this.element=e,this.masksProperties=this.data.masksProperties||[],this.viewData=createSizedArray(this.masksProperties.length);var r,i=this.masksProperties.length,s=!1;for(r=0;r<i;r++)"n"!==this.masksProperties[r].mode&&(s=!0),this.viewData[r]=ShapePropertyFactory.getShapeProp(this.element,this.masksProperties[r],3);(this.hasMasks=s)&&this.element.addRenderableComponent(this)}function CVShapeElement(t,e,r){this.shapes=[],this.shapesData=t.shapes,this.stylesList=[],this.itemsData=[],this.prevViewData=[],this.shapeModifiers=[],this.processedElements=[],this.transformsManager=new ShapeTransformManager,this.initElement(t,e,r)}function CVSolidElement(t,e,r){this.initElement(t,e,r)}function CVEffects(){}BaseRenderer.prototype.checkLayers=function(t){var e,r,i=this.layers.length;for(this.completeLayers=!0,e=i-1;0<=e;e--)this.elements[e]||(r=this.layers[e]).ip-r.st<=t-this.layers[e].st&&r.op-r.st>t-this.layers[e].st&&this.buildItem(e),this.completeLayers=!!this.elements[e]&&this.completeLayers;this.checkPendingElements()},BaseRenderer.prototype.createItem=function(t){switch(t.ty){case 2:return this.createImage(t);case 0:return this.createComp(t);case 1:return this.createSolid(t);case 3:return this.createNull(t);case 4:return this.createShape(t);case 5:return this.createText(t);case 13:return this.createCamera(t)}return this.createNull(t)},BaseRenderer.prototype.createCamera=function(){throw new Error("You're using a 3d camera. Try the html renderer.")},BaseRenderer.prototype.buildAllItems=function(){var t,e=this.layers.length;for(t=0;t<e;t+=1)this.buildItem(t);this.checkPendingElements()},BaseRenderer.prototype.includeLayers=function(t){this.completeLayers=!1;var e,r,i=t.length,s=this.layers.length;for(e=0;e<i;e+=1)for(r=0;r<s;){if(this.layers[r].id==t[e].id){this.layers[r]=t[e];break}r+=1}},BaseRenderer.prototype.setProjectInterface=function(t){this.globalData.projectInterface=t},BaseRenderer.prototype.initItems=function(){this.globalData.progressiveLoad||this.buildAllItems()},BaseRenderer.prototype.buildElementParenting=function(t,e,r){for(var i=this.elements,s=this.layers,a=0,n=s.length;a<n;)s[a].ind==e&&(i[a]&&!0!==i[a]?(r.push(i[a]),i[a].setAsParent(),void 0!==s[a].parent?this.buildElementParenting(t,s[a].parent,r):t.setHierarchy(r)):(this.buildItem(a),this.addPendingElement(t))),a+=1},BaseRenderer.prototype.addPendingElement=function(t){this.pendingElements.push(t)},BaseRenderer.prototype.searchExtraCompositions=function(t){var e,r=t.length;for(e=0;e<r;e+=1)if(t[e].xt){var i=this.createComp(t[e]);i.initExpressions(),this.globalData.projectInterface.registerComposition(i)}},BaseRenderer.prototype.setupGlobalData=function(t,e){this.globalData.fontManager=new FontManager,this.globalData.fontManager.addChars(t.chars),this.globalData.fontManager.addFonts(t.fonts,e),this.globalData.getAssetData=this.animationItem.getAssetData.bind(this.animationItem),this.globalData.getAssetsPath=this.animationItem.getAssetsPath.bind(this.animationItem),this.globalData.imageLoader=this.animationItem.imagePreloader,this.globalData.frameId=0,this.globalData.frameRate=t.fr,this.globalData.nm=t.nm,this.globalData.compSize={w:t.w,h:t.h}},extendPrototype([BaseRenderer],SVGRenderer),SVGRenderer.prototype.createNull=function(t){return new NullElement(t,this.globalData,this)},SVGRenderer.prototype.createShape=function(t){return new SVGShapeElement(t,this.globalData,this)},SVGRenderer.prototype.createText=function(t){return new SVGTextElement(t,this.globalData,this)},SVGRenderer.prototype.createImage=function(t){return new IImageElement(t,this.globalData,this)},SVGRenderer.prototype.createComp=function(t){return new SVGCompElement(t,this.globalData,this)},SVGRenderer.prototype.createSolid=function(t){return new ISolidElement(t,this.globalData,this)},SVGRenderer.prototype.configAnimation=function(t){this.svgElement.setAttribute("xmlns","http://www.w3.org/2000/svg"),this.renderConfig.viewBoxSize?this.svgElement.setAttribute("viewBox",this.renderConfig.viewBoxSize):this.svgElement.setAttribute("viewBox","0 0 "+t.w+" "+t.h),this.renderConfig.viewBoxOnly||(this.svgElement.setAttribute("width",t.w),this.svgElement.setAttribute("height",t.h),this.svgElement.style.width="100%",this.svgElement.style.height="100%",this.svgElement.style.transform="translate3d(0,0,0)"),this.renderConfig.className&&this.svgElement.setAttribute("class",this.renderConfig.className),this.svgElement.setAttribute("preserveAspectRatio",this.renderConfig.preserveAspectRatio),this.animationItem.wrapper.appendChild(this.svgElement);var e=this.globalData.defs;this.setupGlobalData(t,e),this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.data=t;var r=createNS("clipPath"),i=createNS("rect");i.setAttribute("width",t.w),i.setAttribute("height",t.h),i.setAttribute("x",0),i.setAttribute("y",0);var s=createElementID();r.setAttribute("id",s),r.appendChild(i),this.layerElement.setAttribute("clip-path","url("+locationHref+"#"+s+")"),e.appendChild(r),this.layers=t.layers,this.elements=createSizedArray(t.layers.length)},SVGRenderer.prototype.destroy=function(){this.animationItem.wrapper.innerHTML="",this.layerElement=null,this.globalData.defs=null;var t,e=this.layers?this.layers.length:0;for(t=0;t<e;t++)this.elements[t]&&this.elements[t].destroy();this.elements.length=0,this.destroyed=!0,this.animationItem=null},SVGRenderer.prototype.updateContainerSize=function(){},SVGRenderer.prototype.buildItem=function(t){var e=this.elements;if(!e[t]&&99!=this.layers[t].ty){e[t]=!0;var r=this.createItem(this.layers[t]);e[t]=r,expressionsPlugin&&(0===this.layers[t].ty&&this.globalData.projectInterface.registerComposition(r),r.initExpressions()),this.appendElementInPos(r,t),this.layers[t].tt&&(this.elements[t-1]&&!0!==this.elements[t-1]?r.setMatte(e[t-1].layerId):(this.buildItem(t-1),this.addPendingElement(r)))}},SVGRenderer.prototype.checkPendingElements=function(){for(;this.pendingElements.length;){var t=this.pendingElements.pop();if(t.checkParenting(),t.data.tt)for(var e=0,r=this.elements.length;e<r;){if(this.elements[e]===t){t.setMatte(this.elements[e-1].layerId);break}e+=1}}},SVGRenderer.prototype.renderFrame=function(t){if(this.renderedFrame!==t&&!this.destroyed){null===t?t=this.renderedFrame:this.renderedFrame=t,this.globalData.frameNum=t,this.globalData.frameId+=1,this.globalData.projectInterface.currentFrame=t,this.globalData._mdf=!1;var e,r=this.layers.length;for(this.completeLayers||this.checkLayers(t),e=r-1;0<=e;e--)(this.completeLayers||this.elements[e])&&this.elements[e].prepareFrame(t-this.layers[e].st);if(this.globalData._mdf)for(e=0;e<r;e+=1)(this.completeLayers||this.elements[e])&&this.elements[e].renderFrame()}},SVGRenderer.prototype.appendElementInPos=function(t,e){var r=t.getBaseElement();if(r){for(var i,s=0;s<e;)this.elements[s]&&!0!==this.elements[s]&&this.elements[s].getBaseElement()&&(i=this.elements[s].getBaseElement()),s+=1;i?this.layerElement.insertBefore(r,i):this.layerElement.appendChild(r)}},SVGRenderer.prototype.hide=function(){this.layerElement.style.display="none"},SVGRenderer.prototype.show=function(){this.layerElement.style.display="block"},extendPrototype([BaseRenderer],CanvasRenderer),CanvasRenderer.prototype.createShape=function(t){return new CVShapeElement(t,this.globalData,this)},CanvasRenderer.prototype.createText=function(t){return new CVTextElement(t,this.globalData,this)},CanvasRenderer.prototype.createImage=function(t){return new CVImageElement(t,this.globalData,this)},CanvasRenderer.prototype.createComp=function(t){return new CVCompElement(t,this.globalData,this)},CanvasRenderer.prototype.createSolid=function(t){return new CVSolidElement(t,this.globalData,this)},CanvasRenderer.prototype.createNull=SVGRenderer.prototype.createNull,CanvasRenderer.prototype.ctxTransform=function(t){if(1!==t[0]||0!==t[1]||0!==t[4]||1!==t[5]||0!==t[12]||0!==t[13])if(this.renderConfig.clearCanvas){this.transformMat.cloneFromProps(t);var e=this.contextData.cTr.props;this.transformMat.transform(e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15]),this.contextData.cTr.cloneFromProps(this.transformMat.props);var r=this.contextData.cTr.props;this.canvasContext.setTransform(r[0],r[1],r[4],r[5],r[12],r[13])}else this.canvasContext.transform(t[0],t[1],t[4],t[5],t[12],t[13])},CanvasRenderer.prototype.ctxOpacity=function(t){if(!this.renderConfig.clearCanvas)return this.canvasContext.globalAlpha*=t<0?0:t,void(this.globalData.currentGlobalAlpha=this.contextData.cO);this.contextData.cO*=t<0?0:t,this.globalData.currentGlobalAlpha!==this.contextData.cO&&(this.canvasContext.globalAlpha=this.contextData.cO,this.globalData.currentGlobalAlpha=this.contextData.cO)},CanvasRenderer.prototype.reset=function(){this.renderConfig.clearCanvas?this.contextData.reset():this.canvasContext.restore()},CanvasRenderer.prototype.save=function(t){if(this.renderConfig.clearCanvas){t&&this.canvasContext.save();var e=this.contextData.cTr.props;this.contextData._length<=this.contextData.cArrPos&&this.contextData.duplicate();var r,i=this.contextData.saved[this.contextData.cArrPos];for(r=0;r<16;r+=1)i[r]=e[r];this.contextData.savedOp[this.contextData.cArrPos]=this.contextData.cO,this.contextData.cArrPos+=1}else this.canvasContext.save()},CanvasRenderer.prototype.restore=function(t){if(this.renderConfig.clearCanvas){t&&(this.canvasContext.restore(),this.globalData.blendMode="source-over"),this.contextData.cArrPos-=1;var e,r=this.contextData.saved[this.contextData.cArrPos],i=this.contextData.cTr.props;for(e=0;e<16;e+=1)i[e]=r[e];this.canvasContext.setTransform(r[0],r[1],r[4],r[5],r[12],r[13]),r=this.contextData.savedOp[this.contextData.cArrPos],this.contextData.cO=r,this.globalData.currentGlobalAlpha!==r&&(this.canvasContext.globalAlpha=r,this.globalData.currentGlobalAlpha=r)}else this.canvasContext.restore()},CanvasRenderer.prototype.configAnimation=function(t){this.animationItem.wrapper?(this.animationItem.container=createTag("canvas"),this.animationItem.container.style.width="100%",this.animationItem.container.style.height="100%",this.animationItem.container.style.transformOrigin=this.animationItem.container.style.mozTransformOrigin=this.animationItem.container.style.webkitTransformOrigin=this.animationItem.container.style["-webkit-transform"]="0px 0px 0px",this.animationItem.wrapper.appendChild(this.animationItem.container),this.canvasContext=this.animationItem.container.getContext("2d"),this.renderConfig.className&&this.animationItem.container.setAttribute("class",this.renderConfig.className)):this.canvasContext=this.renderConfig.context,this.data=t,this.layers=t.layers,this.transformCanvas={w:t.w,h:t.h,sx:0,sy:0,tx:0,ty:0},this.setupGlobalData(t,document.body),this.globalData.canvasContext=this.canvasContext,(this.globalData.renderer=this).globalData.isDashed=!1,this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.globalData.transformCanvas=this.transformCanvas,this.elements=createSizedArray(t.layers.length),this.updateContainerSize()},CanvasRenderer.prototype.updateContainerSize=function(){var t,e,r,i;if(this.reset(),this.animationItem.wrapper&&this.animationItem.container?(t=this.animationItem.wrapper.offsetWidth,e=this.animationItem.wrapper.offsetHeight,this.animationItem.container.setAttribute("width",t*this.renderConfig.dpr),this.animationItem.container.setAttribute("height",e*this.renderConfig.dpr)):(t=this.canvasContext.canvas.width*this.renderConfig.dpr,e=this.canvasContext.canvas.height*this.renderConfig.dpr),-1!==this.renderConfig.preserveAspectRatio.indexOf("meet")||-1!==this.renderConfig.preserveAspectRatio.indexOf("slice")){var s=this.renderConfig.preserveAspectRatio.split(" "),a=s[1]||"meet",n=s[0]||"xMidYMid",o=n.substr(0,4),h=n.substr(4);(r=t/e)<(i=this.transformCanvas.w/this.transformCanvas.h)&&"meet"===a||i<r&&"slice"===a?(this.transformCanvas.sx=t/(this.transformCanvas.w/this.renderConfig.dpr),this.transformCanvas.sy=t/(this.transformCanvas.w/this.renderConfig.dpr)):(this.transformCanvas.sx=e/(this.transformCanvas.h/this.renderConfig.dpr),this.transformCanvas.sy=e/(this.transformCanvas.h/this.renderConfig.dpr)),this.transformCanvas.tx="xMid"===o&&(i<r&&"meet"===a||r<i&&"slice"===a)?(t-this.transformCanvas.w*(e/this.transformCanvas.h))/2*this.renderConfig.dpr:"xMax"===o&&(i<r&&"meet"===a||r<i&&"slice"===a)?(t-this.transformCanvas.w*(e/this.transformCanvas.h))*this.renderConfig.dpr:0,this.transformCanvas.ty="YMid"===h&&(r<i&&"meet"===a||i<r&&"slice"===a)?(e-this.transformCanvas.h*(t/this.transformCanvas.w))/2*this.renderConfig.dpr:"YMax"===h&&(r<i&&"meet"===a||i<r&&"slice"===a)?(e-this.transformCanvas.h*(t/this.transformCanvas.w))*this.renderConfig.dpr:0}else"none"==this.renderConfig.preserveAspectRatio?(this.transformCanvas.sx=t/(this.transformCanvas.w/this.renderConfig.dpr),this.transformCanvas.sy=e/(this.transformCanvas.h/this.renderConfig.dpr)):(this.transformCanvas.sx=this.renderConfig.dpr,this.transformCanvas.sy=this.renderConfig.dpr),this.transformCanvas.tx=0,this.transformCanvas.ty=0;this.transformCanvas.props=[this.transformCanvas.sx,0,0,0,0,this.transformCanvas.sy,0,0,0,0,1,0,this.transformCanvas.tx,this.transformCanvas.ty,0,1],this.ctxTransform(this.transformCanvas.props),this.canvasContext.beginPath(),this.canvasContext.rect(0,0,this.transformCanvas.w,this.transformCanvas.h),this.canvasContext.closePath(),this.canvasContext.clip(),this.renderFrame(this.renderedFrame,!0)},CanvasRenderer.prototype.destroy=function(){var t;for(this.renderConfig.clearCanvas&&(this.animationItem.wrapper.innerHTML=""),t=(this.layers?this.layers.length:0)-1;0<=t;t-=1)this.elements[t]&&this.elements[t].destroy();this.elements.length=0,this.globalData.canvasContext=null,this.animationItem.container=null,this.destroyed=!0},CanvasRenderer.prototype.renderFrame=function(t,e){if((this.renderedFrame!==t||!0!==this.renderConfig.clearCanvas||e)&&!this.destroyed&&-1!==t){this.renderedFrame=t,this.globalData.frameNum=t-this.animationItem._isFirstFrame,this.globalData.frameId+=1,this.globalData._mdf=!this.renderConfig.clearCanvas||e,this.globalData.projectInterface.currentFrame=t;var r,i=this.layers.length;for(this.completeLayers||this.checkLayers(t),r=0;r<i;r++)(this.completeLayers||this.elements[r])&&this.elements[r].prepareFrame(t-this.layers[r].st);if(this.globalData._mdf){for(!0===this.renderConfig.clearCanvas?this.canvasContext.clearRect(0,0,this.transformCanvas.w,this.transformCanvas.h):this.save(),r=i-1;0<=r;r-=1)(this.completeLayers||this.elements[r])&&this.elements[r].renderFrame();!0!==this.renderConfig.clearCanvas&&this.restore()}}},CanvasRenderer.prototype.buildItem=function(t){var e=this.elements;if(!e[t]&&99!=this.layers[t].ty){var r=this.createItem(this.layers[t],this,this.globalData);(e[t]=r).initExpressions()}},CanvasRenderer.prototype.checkPendingElements=function(){for(;this.pendingElements.length;){this.pendingElements.pop().checkParenting()}},CanvasRenderer.prototype.hide=function(){this.animationItem.container.style.display="none"},CanvasRenderer.prototype.show=function(){this.animationItem.container.style.display="block"},CanvasRenderer.prototype.configAnimation=function(t){this.animationItem.wrapper?(this.animationItem.container=createTag("canvas"),this.animationItem.container.style.width="100%",this.animationItem.container.style.height="100%",this.animationItem.container.style.transformOrigin=this.animationItem.container.style.mozTransformOrigin=this.animationItem.container.style.webkitTransformOrigin=this.animationItem.container.style["-webkit-transform"]="0px 0px 0px",this.animationItem.wrapper.appendChild(this.animationItem.container),this.canvasContext=this.animationItem.container.getContext("2d"),this.renderConfig.className&&this.animationItem.container.setAttribute("class",this.renderConfig.className)):this.canvasContext=this.renderConfig.context,this.data=t,this.layers=t.layers,this.transformCanvas={w:t.w,h:t.h,sx:0,sy:0,tx:0,ty:0},this.globalData.frameId=0,this.globalData.frameRate=t.fr,this.globalData.nm=t.nm,this.globalData.compSize={w:t.w,h:t.h},this.globalData.canvasContext=this.canvasContext,(this.globalData.renderer=this).globalData.isDashed=!1,this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.globalData.transformCanvas=this.transformCanvas,this.elements=createSizedArray(t.layers.length),this.updateContainerSize()},MaskElement.prototype.getMaskProperty=function(t){return this.viewData[t].prop},MaskElement.prototype.renderFrame=function(t){var e,r=this.element.finalTransform.mat,i=this.masksProperties.length;for(e=0;e<i;e++)if((this.viewData[e].prop._mdf||t)&&this.drawPath(this.masksProperties[e],this.viewData[e].prop.v,this.viewData[e]),(this.viewData[e].op._mdf||t)&&this.viewData[e].elem.setAttribute("fill-opacity",this.viewData[e].op.v),"n"!==this.masksProperties[e].mode&&(this.viewData[e].invRect&&(this.element.finalTransform.mProp._mdf||t)&&(this.viewData[e].invRect.setAttribute("x",-r.props[12]),this.viewData[e].invRect.setAttribute("y",-r.props[13])),this.storedData[e].x&&(this.storedData[e].x._mdf||t))){var s=this.storedData[e].expan;this.storedData[e].x.v<0?("erode"!==this.storedData[e].lastOperator&&(this.storedData[e].lastOperator="erode",this.storedData[e].elem.setAttribute("filter","url("+locationHref+"#"+this.storedData[e].filterId+")")),s.setAttribute("radius",-this.storedData[e].x.v)):("dilate"!==this.storedData[e].lastOperator&&(this.storedData[e].lastOperator="dilate",this.storedData[e].elem.setAttribute("filter",null)),this.storedData[e].elem.setAttribute("stroke-width",2*this.storedData[e].x.v))}},MaskElement.prototype.getMaskelement=function(){return this.maskElement},MaskElement.prototype.createLayerSolidPath=function(){var t="M0,0 ";return t+=" h"+this.globalData.compSize.w,t+=" v"+this.globalData.compSize.h,t+=" h-"+this.globalData.compSize.w,t+=" v-"+this.globalData.compSize.h+" "},MaskElement.prototype.drawPath=function(t,e,r){var i,s,a=" M"+e.v[0][0]+","+e.v[0][1];for(s=e._length,i=1;i<s;i+=1)a+=" C"+e.o[i-1][0]+","+e.o[i-1][1]+" "+e.i[i][0]+","+e.i[i][1]+" "+e.v[i][0]+","+e.v[i][1];if(e.c&&1<s&&(a+=" C"+e.o[i-1][0]+","+e.o[i-1][1]+" "+e.i[0][0]+","+e.i[0][1]+" "+e.v[0][0]+","+e.v[0][1]),r.lastPath!==a){var n="";r.elem&&(e.c&&(n=t.inv?this.solidPath+a:a),r.elem.setAttribute("d",n)),r.lastPath=a}},MaskElement.prototype.destroy=function(){this.element=null,this.globalData=null,this.maskElement=null,this.data=null,this.masksProperties=null},HierarchyElement.prototype={initHierarchy:function(){this.hierarchy=[],this._isParent=!1,this.checkParenting()},setHierarchy:function(t){this.hierarchy=t},setAsParent:function(){this._isParent=!0},checkParenting:function(){void 0!==this.data.parent&&this.comp.buildElementParenting(this,this.data.parent,[])}},FrameElement.prototype={initFrame:function(){this._isFirstFrame=!1,this.dynamicProperties=[],this._mdf=!1},prepareProperties:function(t,e){var r,i=this.dynamicProperties.length;for(r=0;r<i;r+=1)(e||this._isParent&&"transform"===this.dynamicProperties[r].propType)&&(this.dynamicProperties[r].getValue(),this.dynamicProperties[r]._mdf&&(this.globalData._mdf=!0,this._mdf=!0))},addDynamicProperty:function(t){-1===this.dynamicProperties.indexOf(t)&&this.dynamicProperties.push(t)}},TransformElement.prototype={initTransform:function(){this.finalTransform={mProp:this.data.ks?TransformPropertyFactory.getTransformProperty(this,this.data.ks,this):{o:0},_matMdf:!1,_opMdf:!1,mat:new Matrix},this.data.ao&&(this.finalTransform.mProp.autoOriented=!0),this.data.ty},renderTransform:function(){if(this.finalTransform._opMdf=this.finalTransform.mProp.o._mdf||this._isFirstFrame,this.finalTransform._matMdf=this.finalTransform.mProp._mdf||this._isFirstFrame,this.hierarchy){var t,e=this.finalTransform.mat,r=0,i=this.hierarchy.length;if(!this.finalTransform._matMdf)for(;r<i;){if(this.hierarchy[r].finalTransform.mProp._mdf){this.finalTransform._matMdf=!0;break}r+=1}if(this.finalTransform._matMdf)for(t=this.finalTransform.mProp.v.props,e.cloneFromProps(t),r=0;r<i;r+=1)t=this.hierarchy[r].finalTransform.mProp.v.props,e.transform(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15])}},globalToLocal:function(t){var e=[];e.push(this.finalTransform);for(var r=!0,i=this.comp;r;)i.finalTransform?(i.data.hasMask&&e.splice(0,0,i.finalTransform),i=i.comp):r=!1;var s,a,n=e.length;for(s=0;s<n;s+=1)a=e[s].mat.applyToPointArray(0,0,0),t=[t[0]-a[0],t[1]-a[1],0];return t},mHelper:new Matrix},RenderableElement.prototype={initRenderable:function(){this.isInRange=!1,this.hidden=!1,this.isTransparent=!1,this.renderableComponents=[]},addRenderableComponent:function(t){-1===this.renderableComponents.indexOf(t)&&this.renderableComponents.push(t)},removeRenderableComponent:function(t){-1!==this.renderableComponents.indexOf(t)&&this.renderableComponents.splice(this.renderableComponents.indexOf(t),1)},prepareRenderableFrame:function(t){this.checkLayerLimits(t)},checkTransparency:function(){this.finalTransform.mProp.o.v<=0?!this.isTransparent&&this.globalData.renderConfig.hideOnTransparent&&(this.isTransparent=!0,this.hide()):this.isTransparent&&(this.isTransparent=!1,this.show())},checkLayerLimits:function(t){this.data.ip-this.data.st<=t&&this.data.op-this.data.st>t?!0!==this.isInRange&&(this.globalData._mdf=!0,this._mdf=!0,this.isInRange=!0,this.show()):!1!==this.isInRange&&(this.globalData._mdf=!0,this.isInRange=!1,this.hide())},renderRenderable:function(){var t,e=this.renderableComponents.length;for(t=0;t<e;t+=1)this.renderableComponents[t].renderFrame(this._isFirstFrame)},sourceRectAtTime:function(){return{top:0,left:0,width:100,height:100}},getLayerSize:function(){return 5===this.data.ty?{w:this.data.textData.width,h:this.data.textData.height}:{w:this.data.width,h:this.data.height}}},extendPrototype([RenderableElement,createProxyFunction({initElement:function(t,e,r){this.initFrame(),this.initBaseData(t,e,r),this.initTransform(t,e,r),this.initHierarchy(),this.initRenderable(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),this.createContent(),this.hide()},hide:function(){this.hidden||this.isInRange&&!this.isTransparent||((this.baseElement||this.layerElement).style.display="none",this.hidden=!0)},show:function(){this.isInRange&&!this.isTransparent&&(this.data.hd||((this.baseElement||this.layerElement).style.display="block"),this.hidden=!1,this._isFirstFrame=!0)},renderFrame:function(){this.data.hd||this.hidden||(this.renderTransform(),this.renderRenderable(),this.renderElement(),this.renderInnerContent(),this._isFirstFrame&&(this._isFirstFrame=!1))},renderInnerContent:function(){},prepareFrame:function(t){this._mdf=!1,this.prepareRenderableFrame(t),this.prepareProperties(t,this.isInRange),this.checkTransparency()},destroy:function(){this.innerElem=null,this.destroyBaseElement()}})],RenderableDOMElement),SVGShapeData.prototype.setAsAnimated=function(){this._isAnimated=!0},ShapeTransformManager.prototype={addTransformSequence:function(t){var e,r=t.length,i="_";for(e=0;e<r;e+=1)i+=t[e].transform.key+"_";var s=this.sequences[i];return s||(s={transforms:[].concat(t),finalTransform:new Matrix,_mdf:!1},this.sequences[i]=s,this.sequenceList.push(s)),s},processSequence:function(t,e){for(var r,i=0,s=t.transforms.length,a=e;i<s&&!e;){if(t.transforms[i].transform.mProps._mdf){a=!0;break}i+=1}if(a)for(t.finalTransform.reset(),i=s-1;0<=i;i-=1)r=t.transforms[i].transform.mProps.v.props,t.finalTransform.transform(r[0],r[1],r[2],r[3],r[4],r[5],r[6],r[7],r[8],r[9],r[10],r[11],r[12],r[13],r[14],r[15]);t._mdf=a},processSequences:function(t){var e,r=this.sequenceList.length;for(e=0;e<r;e+=1)this.processSequence(this.sequenceList[e],t)},getNewKey:function(){return"_"+this.transform_key_count++}},CVShapeData.prototype.setAsAnimated=SVGShapeData.prototype.setAsAnimated,BaseElement.prototype={checkMasks:function(){if(!this.data.hasMask)return!1;for(var t=0,e=this.data.masksProperties.length;t<e;){if("n"!==this.data.masksProperties[t].mode&&!1!==this.data.masksProperties[t].cl)return!0;t+=1}return!1},initExpressions:function(){this.layerInterface=LayerExpressionInterface(this),this.data.hasMask&&this.maskManager&&this.layerInterface.registerMaskInterface(this.maskManager);var t=EffectsExpressionInterface.createEffectsInterface(this,this.layerInterface);this.layerInterface.registerEffectsInterface(t),0===this.data.ty||this.data.xt?this.compInterface=CompExpressionInterface(this):4===this.data.ty?(this.layerInterface.shapeInterface=ShapeExpressionInterface(this.shapesData,this.itemsData,this.layerInterface),this.layerInterface.content=this.layerInterface.shapeInterface):5===this.data.ty&&(this.layerInterface.textInterface=TextExpressionInterface(this),this.layerInterface.text=this.layerInterface.textInterface)},setBlendMode:function(){var t=getBlendMode(this.data.bm);(this.baseElement||this.layerElement).style["mix-blend-mode"]=t},initBaseData:function(t,e,r){this.globalData=e,this.comp=r,this.data=t,this.layerId=createElementID(),this.data.sr||(this.data.sr=1),this.effectsManager=new EffectsManager(this.data,this,this.dynamicProperties)},getType:function(){return this.type},sourceRectAtTime:function(){}},NullElement.prototype.prepareFrame=function(t){this.prepareProperties(t,!0)},NullElement.prototype.renderFrame=function(){},NullElement.prototype.getBaseElement=function(){return null},NullElement.prototype.destroy=function(){},NullElement.prototype.sourceRectAtTime=function(){},NullElement.prototype.hide=function(){},extendPrototype([BaseElement,TransformElement,HierarchyElement,FrameElement],NullElement),SVGBaseElement.prototype={initRendererElement:function(){this.layerElement=createNS("g")},createContainerElements:function(){this.matteElement=createNS("g"),this.transformedElement=this.layerElement,this.maskedElement=this.layerElement,this._sizeChanged=!1;var t,e,r,i=null;if(this.data.td){if(3==this.data.td||1==this.data.td){var s=createNS("mask");s.setAttribute("id",this.layerId),s.setAttribute("mask-type",3==this.data.td?"luminance":"alpha"),s.appendChild(this.layerElement),i=s,this.globalData.defs.appendChild(s),featureSupport.maskType||1!=this.data.td||(s.setAttribute("mask-type","luminance"),t=createElementID(),e=filtersFactory.createFilter(t),this.globalData.defs.appendChild(e),e.appendChild(filtersFactory.createAlphaToLuminanceFilter()),(r=createNS("g")).appendChild(this.layerElement),i=r,s.appendChild(r),r.setAttribute("filter","url("+locationHref+"#"+t+")"))}else if(2==this.data.td){var a=createNS("mask");a.setAttribute("id",this.layerId),a.setAttribute("mask-type","alpha");var n=createNS("g");a.appendChild(n),t=createElementID(),e=filtersFactory.createFilter(t);var o=createNS("feComponentTransfer");o.setAttribute("in","SourceGraphic"),e.appendChild(o);var h=createNS("feFuncA");h.setAttribute("type","table"),h.setAttribute("tableValues","1.0 0.0"),o.appendChild(h),this.globalData.defs.appendChild(e);var p=createNS("rect");p.setAttribute("width",this.comp.data.w),p.setAttribute("height",this.comp.data.h),p.setAttribute("x","0"),p.setAttribute("y","0"),p.setAttribute("fill","#ffffff"),p.setAttribute("opacity","0"),n.setAttribute("filter","url("+locationHref+"#"+t+")"),n.appendChild(p),n.appendChild(this.layerElement),i=n,featureSupport.maskType||(a.setAttribute("mask-type","luminance"),e.appendChild(filtersFactory.createAlphaToLuminanceFilter()),r=createNS("g"),n.appendChild(p),r.appendChild(this.layerElement),i=r,n.appendChild(r)),this.globalData.defs.appendChild(a)}}else this.data.tt?(this.matteElement.appendChild(this.layerElement),i=this.matteElement,this.baseElement=this.matteElement):this.baseElement=this.layerElement;if(this.data.ln&&this.layerElement.setAttribute("id",this.data.ln),this.data.cl&&this.layerElement.setAttribute("class",this.data.cl),0===this.data.ty&&!this.data.hd){var l=createNS("clipPath"),m=createNS("path");m.setAttribute("d","M0,0 L"+this.data.w+",0 L"+this.data.w+","+this.data.h+" L0,"+this.data.h+"z");var f=createElementID();if(l.setAttribute("id",f),l.appendChild(m),this.globalData.defs.appendChild(l),this.checkMasks()){var c=createNS("g");c.setAttribute("clip-path","url("+locationHref+"#"+f+")"),c.appendChild(this.layerElement),this.transformedElement=c,i?i.appendChild(this.transformedElement):this.baseElement=this.transformedElement}else this.layerElement.setAttribute("clip-path","url("+locationHref+"#"+f+")")}0!==this.data.bm&&this.setBlendMode()},renderElement:function(){this.finalTransform._matMdf&&this.transformedElement.setAttribute("transform",this.finalTransform.mat.to2dCSS()),this.finalTransform._opMdf&&this.transformedElement.setAttribute("opacity",this.finalTransform.mProp.o.v)},destroyBaseElement:function(){this.layerElement=null,this.matteElement=null,this.maskManager.destroy()},getBaseElement:function(){return this.data.hd?null:this.baseElement},createRenderableComponents:function(){this.maskManager=new MaskElement(this.data,this,this.globalData),this.renderableEffectsManager=new SVGEffects(this)},setMatte:function(t){this.matteElement&&this.matteElement.setAttribute("mask","url("+locationHref+"#"+t+")")}},IShapeElement.prototype={addShapeToModifiers:function(t){var e,r=this.shapeModifiers.length;for(e=0;e<r;e+=1)this.shapeModifiers[e].addShape(t)},isShapeInAnimatedModifiers:function(t){for(var e=this.shapeModifiers.length;0<e;)if(this.shapeModifiers[0].isAnimatedWithShape(t))return!0;return!1},renderModifiers:function(){if(this.shapeModifiers.length){var t,e=this.shapes.length;for(t=0;t<e;t+=1)this.shapes[t].sh.reset();for(t=(e=this.shapeModifiers.length)-1;0<=t;t-=1)this.shapeModifiers[t].processShapes(this._isFirstFrame)}},lcEnum:{1:"butt",2:"round",3:"square"},ljEnum:{1:"miter",2:"round",3:"bevel"},searchProcessedElement:function(t){for(var e=this.processedElements,r=0,i=e.length;r<i;){if(e[r].elem===t)return e[r].pos;r+=1}return 0},addProcessedElement:function(t,e){for(var r=this.processedElements,i=r.length;i;)if(r[i-=1].elem===t)return void(r[i].pos=e);r.push(new ProcessedElement(t,e))},prepareFrame:function(t){this.prepareRenderableFrame(t),this.prepareProperties(t,this.isInRange)}},ITextElement.prototype.initElement=function(t,e,r){this.lettersChangedFlag=!0,this.initFrame(),this.initBaseData(t,e,r),this.textProperty=new TextProperty(this,t.t,this.dynamicProperties),this.textAnimator=new TextAnimatorProperty(t.t,this.renderType,this),this.initTransform(t,e,r),this.initHierarchy(),this.initRenderable(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),this.createContent(),this.hide(),this.textAnimator.searchProperties(this.dynamicProperties)},ITextElement.prototype.prepareFrame=function(t){this._mdf=!1,this.prepareRenderableFrame(t),this.prepareProperties(t,this.isInRange),(this.textProperty._mdf||this.textProperty._isFirstFrame)&&(this.buildNewText(),this.textProperty._isFirstFrame=!1,this.textProperty._mdf=!1)},ITextElement.prototype.createPathShape=function(t,e){var r,i,s=e.length,a="";for(r=0;r<s;r+=1)i=e[r].ks.k,a+=buildShapeString(i,i.i.length,!0,t);return a},ITextElement.prototype.updateDocumentData=function(t,e){this.textProperty.updateDocumentData(t,e)},ITextElement.prototype.canResizeFont=function(t){this.textProperty.canResizeFont(t)},ITextElement.prototype.setMinimumFontSize=function(t){this.textProperty.setMinimumFontSize(t)},ITextElement.prototype.applyTextPropertiesToMatrix=function(t,e,r,i,s){switch(t.ps&&e.translate(t.ps[0],t.ps[1]+t.ascent,0),e.translate(0,-t.ls,0),t.j){case 1:e.translate(t.justifyOffset+(t.boxWidth-t.lineWidths[r]),0,0);break;case 2:e.translate(t.justifyOffset+(t.boxWidth-t.lineWidths[r])/2,0,0)}e.translate(i,s,0)},ITextElement.prototype.buildColor=function(t){return"rgb("+Math.round(255*t[0])+","+Math.round(255*t[1])+","+Math.round(255*t[2])+")"},ITextElement.prototype.emptyProp=new LetterProps,ITextElement.prototype.destroy=function(){},extendPrototype([BaseElement,TransformElement,HierarchyElement,FrameElement,RenderableDOMElement],ICompElement),ICompElement.prototype.initElement=function(t,e,r){this.initFrame(),this.initBaseData(t,e,r),this.initTransform(t,e,r),this.initRenderable(),this.initHierarchy(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),!this.data.xt&&e.progressiveLoad||this.buildAllItems(),this.hide()},ICompElement.prototype.prepareFrame=function(t){if(this._mdf=!1,this.prepareRenderableFrame(t),this.prepareProperties(t,this.isInRange),this.isInRange||this.data.xt){if(this.tm._placeholder)this.renderedFrame=t/this.data.sr;else{var e=this.tm.v;e===this.data.op&&(e=this.data.op-1),this.renderedFrame=e}var r,i=this.elements.length;for(this.completeLayers||this.checkLayers(this.renderedFrame),r=i-1;0<=r;r-=1)(this.completeLayers||this.elements[r])&&(this.elements[r].prepareFrame(this.renderedFrame-this.layers[r].st),this.elements[r]._mdf&&(this._mdf=!0))}},ICompElement.prototype.renderInnerContent=function(){var t,e=this.layers.length;for(t=0;t<e;t+=1)(this.completeLayers||this.elements[t])&&this.elements[t].renderFrame()},ICompElement.prototype.setElements=function(t){this.elements=t},ICompElement.prototype.getElements=function(){return this.elements},ICompElement.prototype.destroyElements=function(){var t,e=this.layers.length;for(t=0;t<e;t+=1)this.elements[t]&&this.elements[t].destroy()},ICompElement.prototype.destroy=function(){this.destroyElements(),this.destroyBaseElement()},extendPrototype([BaseElement,TransformElement,SVGBaseElement,HierarchyElement,FrameElement,RenderableDOMElement],IImageElement),IImageElement.prototype.createContent=function(){var t=this.globalData.getAssetsPath(this.assetData);this.innerElem=createNS("image"),this.innerElem.setAttribute("width",this.assetData.w+"px"),this.innerElem.setAttribute("height",this.assetData.h+"px"),this.innerElem.setAttribute("preserveAspectRatio",this.assetData.pr||this.globalData.renderConfig.imagePreserveAspectRatio),this.innerElem.setAttributeNS("http://www.w3.org/1999/xlink","href",t),this.layerElement.appendChild(this.innerElem)},IImageElement.prototype.sourceRectAtTime=function(){return this.sourceRect},extendPrototype([IImageElement],ISolidElement),ISolidElement.prototype.createContent=function(){var t=createNS("rect");t.setAttribute("width",this.data.sw),t.setAttribute("height",this.data.sh),t.setAttribute("fill",this.data.sc),this.layerElement.appendChild(t)},extendPrototype([BaseElement,TransformElement,SVGBaseElement,IShapeElement,HierarchyElement,FrameElement,RenderableDOMElement],SVGShapeElement),SVGShapeElement.prototype.initSecondaryElement=function(){},SVGShapeElement.prototype.identityMatrix=new Matrix,SVGShapeElement.prototype.buildExpressionInterface=function(){},SVGShapeElement.prototype.createContent=function(){this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement,0,[],!0),this.filterUniqueShapes()},SVGShapeElement.prototype.filterUniqueShapes=function(){var t,e,r,i,s=this.shapes.length,a=this.stylesList.length,n=[],o=!1;for(r=0;r<a;r+=1){for(i=this.stylesList[r],o=!1,t=n.length=0;t<s;t+=1)-1!==(e=this.shapes[t]).styles.indexOf(i)&&(n.push(e),o=e._isAnimated||o);1<n.length&&o&&this.setShapesAsAnimated(n)}},SVGShapeElement.prototype.setShapesAsAnimated=function(t){var e,r=t.length;for(e=0;e<r;e+=1)t[e].setAsAnimated()},SVGShapeElement.prototype.createStyleElement=function(t,e){var r,i=new SVGStyleData(t,e),s=i.pElem;if("st"===t.ty)r=new SVGStrokeStyleData(this,t,i);else if("fl"===t.ty)r=new SVGFillStyleData(this,t,i);else if("gf"===t.ty||"gs"===t.ty){r=new("gf"===t.ty?SVGGradientFillStyleData:SVGGradientStrokeStyleData)(this,t,i),this.globalData.defs.appendChild(r.gf),r.maskId&&(this.globalData.defs.appendChild(r.ms),this.globalData.defs.appendChild(r.of),s.setAttribute("mask","url("+locationHref+"#"+r.maskId+")"))}return"st"!==t.ty&&"gs"!==t.ty||(s.setAttribute("stroke-linecap",this.lcEnum[t.lc]||"round"),s.setAttribute("stroke-linejoin",this.ljEnum[t.lj]||"round"),s.setAttribute("fill-opacity","0"),1===t.lj&&s.setAttribute("stroke-miterlimit",t.ml)),2===t.r&&s.setAttribute("fill-rule","evenodd"),t.ln&&s.setAttribute("id",t.ln),t.cl&&s.setAttribute("class",t.cl),t.bm&&(s.style["mix-blend-mode"]=getBlendMode(t.bm)),this.stylesList.push(i),this.addToAnimatedContents(t,r),r},SVGShapeElement.prototype.createGroupElement=function(t){var e=new ShapeGroupData;return t.ln&&e.gr.setAttribute("id",t.ln),t.cl&&e.gr.setAttribute("class",t.cl),t.bm&&(e.gr.style["mix-blend-mode"]=getBlendMode(t.bm)),e},SVGShapeElement.prototype.createTransformElement=function(t,e){var r=TransformPropertyFactory.getTransformProperty(this,t,this),i=new SVGTransformData(r,r.o,e);return this.addToAnimatedContents(t,i),i},SVGShapeElement.prototype.createShapeElement=function(t,e,r){var i=4;"rc"===t.ty?i=5:"el"===t.ty?i=6:"sr"===t.ty&&(i=7);var s=new SVGShapeData(e,r,ShapePropertyFactory.getShapeProp(this,t,i,this));return this.shapes.push(s),this.addShapeToModifiers(s),this.addToAnimatedContents(t,s),s},SVGShapeElement.prototype.addToAnimatedContents=function(t,e){for(var r=0,i=this.animatedContents.length;r<i;){if(this.animatedContents[r].element===e)return;r+=1}this.animatedContents.push({fn:SVGElementsRenderer.createRenderFunction(t),element:e,data:t})},SVGShapeElement.prototype.setElementStyles=function(t){var e,r=t.styles,i=this.stylesList.length;for(e=0;e<i;e+=1)this.stylesList[e].closed||r.push(this.stylesList[e])},SVGShapeElement.prototype.reloadShapes=function(){this._isFirstFrame=!0;var t,e=this.itemsData.length;for(t=0;t<e;t+=1)this.prevViewData[t]=this.itemsData[t];for(this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement,0,[],!0),this.filterUniqueShapes(),e=this.dynamicProperties.length,t=0;t<e;t+=1)this.dynamicProperties[t].getValue();this.renderModifiers()},SVGShapeElement.prototype.searchShapes=function(t,e,r,i,s,a,n){var o,h,p,l,m,f,c=[].concat(a),d=t.length-1,u=[],y=[];for(o=d;0<=o;o-=1){if((f=this.searchProcessedElement(t[o]))?e[o]=r[f-1]:t[o]._render=n,"fl"==t[o].ty||"st"==t[o].ty||"gf"==t[o].ty||"gs"==t[o].ty)f?e[o].style.closed=!1:e[o]=this.createStyleElement(t[o],s),t[o]._render&&i.appendChild(e[o].style.pElem),u.push(e[o].style);else if("gr"==t[o].ty){if(f)for(p=e[o].it.length,h=0;h<p;h+=1)e[o].prevViewData[h]=e[o].it[h];else e[o]=this.createGroupElement(t[o]);this.searchShapes(t[o].it,e[o].it,e[o].prevViewData,e[o].gr,s+1,c,n),t[o]._render&&i.appendChild(e[o].gr)}else"tr"==t[o].ty?(f||(e[o]=this.createTransformElement(t[o],i)),l=e[o].transform,c.push(l)):"sh"==t[o].ty||"rc"==t[o].ty||"el"==t[o].ty||"sr"==t[o].ty?(f||(e[o]=this.createShapeElement(t[o],c,s)),this.setElementStyles(e[o])):"tm"==t[o].ty||"rd"==t[o].ty||"ms"==t[o].ty?(f?(m=e[o]).closed=!1:((m=ShapeModifiers.getModifier(t[o].ty)).init(this,t[o]),e[o]=m,this.shapeModifiers.push(m)),y.push(m)):"rp"==t[o].ty&&(f?(m=e[o]).closed=!0:(m=ShapeModifiers.getModifier(t[o].ty),(e[o]=m).init(this,t,o,e),this.shapeModifiers.push(m),n=!1),y.push(m));this.addProcessedElement(t[o],o+1)}for(d=u.length,o=0;o<d;o+=1)u[o].closed=!0;for(d=y.length,o=0;o<d;o+=1)y[o].closed=!0},SVGShapeElement.prototype.renderInnerContent=function(){this.renderModifiers();var t,e=this.stylesList.length;for(t=0;t<e;t+=1)this.stylesList[t].reset();for(this.renderShape(),t=0;t<e;t+=1)(this.stylesList[t]._mdf||this._isFirstFrame)&&(this.stylesList[t].msElem&&(this.stylesList[t].msElem.setAttribute("d",this.stylesList[t].d),this.stylesList[t].d="M0 0"+this.stylesList[t].d),this.stylesList[t].pElem.setAttribute("d",this.stylesList[t].d||"M0 0"))},SVGShapeElement.prototype.renderShape=function(){var t,e,r=this.animatedContents.length;for(t=0;t<r;t+=1)e=this.animatedContents[t],(this._isFirstFrame||e.element._isAnimated)&&!0!==e.data&&e.fn(e.data,e.element,this._isFirstFrame)},SVGShapeElement.prototype.destroy=function(){this.destroyBaseElement(),this.shapesData=null,this.itemsData=null},CVContextData.prototype.duplicate=function(){var t=2*this._length,e=this.savedOp;this.savedOp=createTypedArray("float32",t),this.savedOp.set(e);var r=0;for(r=this._length;r<t;r+=1)this.saved[r]=createTypedArray("float32",16);this._length=t},CVContextData.prototype.reset=function(){this.cArrPos=0,this.cTr.reset(),this.cO=1},CVBaseElement.prototype={createElements:function(){},initRendererElement:function(){},createContainerElements:function(){this.canvasContext=this.globalData.canvasContext,this.renderableEffectsManager=new CVEffects(this)},createContent:function(){},setBlendMode:function(){var t=this.globalData;if(t.blendMode!==this.data.bm){t.blendMode=this.data.bm;var e=getBlendMode(this.data.bm);t.canvasContext.globalCompositeOperation=e}},createRenderableComponents:function(){this.maskManager=new CVMaskElement(this.data,this)},hideElement:function(){this.hidden||this.isInRange&&!this.isTransparent||(this.hidden=!0)},showElement:function(){this.isInRange&&!this.isTransparent&&(this.hidden=!1,this._isFirstFrame=!0,this.maskManager._isFirstFrame=!0)},renderFrame:function(){this.hidden||this.data.hd||(this.renderTransform(),this.renderRenderable(),this.setBlendMode(),this.globalData.renderer.save(),this.globalData.renderer.ctxTransform(this.finalTransform.mat.props),this.globalData.renderer.ctxOpacity(this.finalTransform.mProp.o.v),this.renderInnerContent(),this.globalData.renderer.restore(),this.maskManager.hasMasks&&this.globalData.renderer.restore(!0),this._isFirstFrame&&(this._isFirstFrame=!1))},destroy:function(){this.canvasContext=null,this.data=null,this.globalData=null,this.maskManager.destroy()},mHelper:new Matrix},CVBaseElement.prototype.hide=CVBaseElement.prototype.hideElement,CVBaseElement.prototype.show=CVBaseElement.prototype.showElement,extendPrototype([CanvasRenderer,ICompElement,CVBaseElement],CVCompElement),CVCompElement.prototype.renderInnerContent=function(){var t;for(t=this.layers.length-1;0<=t;t-=1)(this.completeLayers||this.elements[t])&&this.elements[t].renderFrame()},CVCompElement.prototype.destroy=function(){var t;for(t=this.layers.length-1;0<=t;t-=1)this.elements[t]&&this.elements[t].destroy();this.layers=null,this.elements=null},CVMaskElement.prototype.renderFrame=function(){if(this.hasMasks){var t,e,r,i,s=this.element.finalTransform.mat,a=this.element.canvasContext,n=this.masksProperties.length;for(a.beginPath(),t=0;t<n;t++)if("n"!==this.masksProperties[t].mode){this.masksProperties[t].inv&&(a.moveTo(0,0),a.lineTo(this.element.globalData.compSize.w,0),a.lineTo(this.element.globalData.compSize.w,this.element.globalData.compSize.h),a.lineTo(0,this.element.globalData.compSize.h),a.lineTo(0,0)),i=this.viewData[t].v,e=s.applyToPointArray(i.v[0][0],i.v[0][1],0),a.moveTo(e[0],e[1]);var o,h=i._length;for(o=1;o<h;o++)r=s.applyToTriplePoints(i.o[o-1],i.i[o],i.v[o]),a.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5]);r=s.applyToTriplePoints(i.o[o-1],i.i[0],i.v[0]),a.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5])}this.element.globalData.renderer.save(!0),a.clip()}},CVMaskElement.prototype.getMaskProperty=MaskElement.prototype.getMaskProperty,CVMaskElement.prototype.destroy=function(){this.element=null},extendPrototype([BaseElement,TransformElement,CVBaseElement,IShapeElement,HierarchyElement,FrameElement,RenderableElement],CVShapeElement),CVShapeElement.prototype.initElement=RenderableDOMElement.prototype.initElement,CVShapeElement.prototype.transformHelper={opacity:1,_opMdf:!1},CVShapeElement.prototype.dashResetter=[],CVShapeElement.prototype.createContent=function(){this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,!0,[])},CVShapeElement.prototype.createStyleElement=function(t,e){var r={data:t,type:t.ty,preTransforms:this.transformsManager.addTransformSequence(e),transforms:[],elements:[],closed:!0===t.hd},i={};if("fl"==t.ty||"st"==t.ty?(i.c=PropertyFactory.getProp(this,t.c,1,255,this),i.c.k||(r.co="rgb("+bm_floor(i.c.v[0])+","+bm_floor(i.c.v[1])+","+bm_floor(i.c.v[2])+")")):"gf"!==t.ty&&"gs"!==t.ty||(i.s=PropertyFactory.getProp(this,t.s,1,null,this),i.e=PropertyFactory.getProp(this,t.e,1,null,this),i.h=PropertyFactory.getProp(this,t.h||{k:0},0,.01,this),i.a=PropertyFactory.getProp(this,t.a||{k:0},0,degToRads,this),i.g=new GradientProperty(this,t.g,this)),i.o=PropertyFactory.getProp(this,t.o,0,.01,this),"st"==t.ty||"gs"==t.ty){if(r.lc=this.lcEnum[t.lc]||"round",r.lj=this.ljEnum[t.lj]||"round",1==t.lj&&(r.ml=t.ml),i.w=PropertyFactory.getProp(this,t.w,0,null,this),i.w.k||(r.wi=i.w.v),t.d){var s=new DashProperty(this,t.d,"canvas",this);i.d=s,i.d.k||(r.da=i.d.dashArray,r.do=i.d.dashoffset[0])}}else r.r=2===t.r?"evenodd":"nonzero";return this.stylesList.push(r),i.style=r,i},CVShapeElement.prototype.createGroupElement=function(t){return{it:[],prevViewData:[]}},CVShapeElement.prototype.createTransformElement=function(t){return{transform:{opacity:1,_opMdf:!1,key:this.transformsManager.getNewKey(),op:PropertyFactory.getProp(this,t.o,0,.01,this),mProps:TransformPropertyFactory.getTransformProperty(this,t,this)}}},CVShapeElement.prototype.createShapeElement=function(t){var e=new CVShapeData(this,t,this.stylesList,this.transformsManager);return this.shapes.push(e),this.addShapeToModifiers(e),e},CVShapeElement.prototype.reloadShapes=function(){this._isFirstFrame=!0;var t,e=this.itemsData.length;for(t=0;t<e;t+=1)this.prevViewData[t]=this.itemsData[t];for(this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,!0,[]),e=this.dynamicProperties.length,t=0;t<e;t+=1)this.dynamicProperties[t].getValue();this.renderModifiers(),this.transformsManager.processSequences(this._isFirstFrame)},CVShapeElement.prototype.addTransformToStyleList=function(t){var e,r=this.stylesList.length;for(e=0;e<r;e+=1)this.stylesList[e].closed||this.stylesList[e].transforms.push(t)},CVShapeElement.prototype.removeTransformFromStyleList=function(){var t,e=this.stylesList.length;for(t=0;t<e;t+=1)this.stylesList[t].closed||this.stylesList[t].transforms.pop()},CVShapeElement.prototype.closeStyles=function(t){var e,r=t.length;for(e=0;e<r;e+=1)t[e].closed=!0},CVShapeElement.prototype.searchShapes=function(t,e,r,i,s){var a,n,o,h,p,l,m=t.length-1,f=[],c=[],d=[].concat(s);for(a=m;0<=a;a-=1){if((h=this.searchProcessedElement(t[a]))?e[a]=r[h-1]:t[a]._shouldRender=i,"fl"==t[a].ty||"st"==t[a].ty||"gf"==t[a].ty||"gs"==t[a].ty)h?e[a].style.closed=!1:e[a]=this.createStyleElement(t[a],d),f.push(e[a].style);else if("gr"==t[a].ty){if(h)for(o=e[a].it.length,n=0;n<o;n+=1)e[a].prevViewData[n]=e[a].it[n];else e[a]=this.createGroupElement(t[a]);this.searchShapes(t[a].it,e[a].it,e[a].prevViewData,i,d)}else"tr"==t[a].ty?(h||(l=this.createTransformElement(t[a]),e[a]=l),d.push(e[a]),this.addTransformToStyleList(e[a])):"sh"==t[a].ty||"rc"==t[a].ty||"el"==t[a].ty||"sr"==t[a].ty?h||(e[a]=this.createShapeElement(t[a])):"tm"==t[a].ty||"rd"==t[a].ty?(h?(p=e[a]).closed=!1:((p=ShapeModifiers.getModifier(t[a].ty)).init(this,t[a]),e[a]=p,this.shapeModifiers.push(p)),c.push(p)):"rp"==t[a].ty&&(h?(p=e[a]).closed=!0:(p=ShapeModifiers.getModifier(t[a].ty),(e[a]=p).init(this,t,a,e),this.shapeModifiers.push(p),i=!1),c.push(p));this.addProcessedElement(t[a],a+1)}for(this.removeTransformFromStyleList(),this.closeStyles(f),m=c.length,a=0;a<m;a+=1)c[a].closed=!0},CVShapeElement.prototype.renderInnerContent=function(){this.transformHelper.opacity=1,this.transformHelper._opMdf=!1,this.renderModifiers(),this.transformsManager.processSequences(this._isFirstFrame),this.renderShape(this.transformHelper,this.shapesData,this.itemsData,!0)},CVShapeElement.prototype.renderShapeTransform=function(t,e){(t._opMdf||e.op._mdf||this._isFirstFrame)&&(e.opacity=t.opacity,e.opacity*=e.op.v,e._opMdf=!0)},CVShapeElement.prototype.drawLayer=function(){var t,e,r,i,s,a,n,o,h,p=this.stylesList.length,l=this.globalData.renderer,m=this.globalData.canvasContext;for(t=0;t<p;t+=1)if(("st"!==(o=(h=this.stylesList[t]).type)&&"gs"!==o||0!==h.wi)&&h.data._shouldRender&&0!==h.coOp&&0!==this.globalData.currentGlobalAlpha){for(l.save(),a=h.elements,"st"===o||"gs"===o?(m.strokeStyle="st"===o?h.co:h.grd,m.lineWidth=h.wi,m.lineCap=h.lc,m.lineJoin=h.lj,m.miterLimit=h.ml||0):m.fillStyle="fl"===o?h.co:h.grd,l.ctxOpacity(h.coOp),"st"!==o&&"gs"!==o&&m.beginPath(),l.ctxTransform(h.preTransforms.finalTransform.props),r=a.length,e=0;e<r;e+=1){for("st"!==o&&"gs"!==o||(m.beginPath(),h.da&&(m.setLineDash(h.da),m.lineDashOffset=h.do)),s=(n=a[e].trNodes).length,i=0;i<s;i+=1)"m"==n[i].t?m.moveTo(n[i].p[0],n[i].p[1]):"c"==n[i].t?m.bezierCurveTo(n[i].pts[0],n[i].pts[1],n[i].pts[2],n[i].pts[3],n[i].pts[4],n[i].pts[5]):m.closePath();"st"!==o&&"gs"!==o||(m.stroke(),h.da&&m.setLineDash(this.dashResetter))}"st"!==o&&"gs"!==o&&m.fill(h.r),l.restore()}},CVShapeElement.prototype.renderShape=function(t,e,r,i){var s,a;for(a=t,s=e.length-1;0<=s;s-=1)"tr"==e[s].ty?(a=r[s].transform,this.renderShapeTransform(t,a)):"sh"==e[s].ty||"el"==e[s].ty||"rc"==e[s].ty||"sr"==e[s].ty?this.renderPath(e[s],r[s]):"fl"==e[s].ty?this.renderFill(e[s],r[s],a):"st"==e[s].ty?this.renderStroke(e[s],r[s],a):"gf"==e[s].ty||"gs"==e[s].ty?this.renderGradientFill(e[s],r[s],a):"gr"==e[s].ty?this.renderShape(a,e[s].it,r[s].it):e[s].ty;i&&this.drawLayer()},CVShapeElement.prototype.renderStyledShape=function(t,e){if(this._isFirstFrame||e._mdf||t.transforms._mdf){var r,i,s,a=t.trNodes,n=e.paths,o=n._length;a.length=0;var h=t.transforms.finalTransform;for(s=0;s<o;s+=1){var p=n.shapes[s];if(p&&p.v){for(i=p._length,r=1;r<i;r+=1)1===r&&a.push({t:"m",p:h.applyToPointArray(p.v[0][0],p.v[0][1],0)}),a.push({t:"c",pts:h.applyToTriplePoints(p.o[r-1],p.i[r],p.v[r])});1===i&&a.push({t:"m",p:h.applyToPointArray(p.v[0][0],p.v[0][1],0)}),p.c&&i&&(a.push({t:"c",pts:h.applyToTriplePoints(p.o[r-1],p.i[0],p.v[0])}),a.push({t:"z"}))}}t.trNodes=a}},CVShapeElement.prototype.renderPath=function(t,e){if(!0!==t.hd&&t._shouldRender){var r,i=e.styledShapes.length;for(r=0;r<i;r+=1)this.renderStyledShape(e.styledShapes[r],e.sh)}},CVShapeElement.prototype.renderFill=function(t,e,r){var i=e.style;(e.c._mdf||this._isFirstFrame)&&(i.co="rgb("+bm_floor(e.c.v[0])+","+bm_floor(e.c.v[1])+","+bm_floor(e.c.v[2])+")"),(e.o._mdf||r._opMdf||this._isFirstFrame)&&(i.coOp=e.o.v*r.opacity)},CVShapeElement.prototype.renderGradientFill=function(t,e,r){var i=e.style;if(!i.grd||e.g._mdf||e.s._mdf||e.e._mdf||1!==t.t&&(e.h._mdf||e.a._mdf)){var s=this.globalData.canvasContext,a=e.s.v,n=e.e.v;if(1===t.t)f=s.createLinearGradient(a[0],a[1],n[0],n[1]);else var o=Math.sqrt(Math.pow(a[0]-n[0],2)+Math.pow(a[1]-n[1],2)),h=Math.atan2(n[1]-a[1],n[0]-a[0]),p=o*(1<=e.h.v?.99:e.h.v<=-1?-.99:e.h.v),l=Math.cos(h+e.a.v)*p+a[0],m=Math.sin(h+e.a.v)*p+a[1],f=s.createRadialGradient(l,m,0,a[0],a[1],o);var c,d=t.g.p,u=e.g.c,y=1;for(c=0;c<d;c+=1)e.g._hasOpacity&&e.g._collapsable&&(y=e.g.o[2*c+1]),f.addColorStop(u[4*c]/100,"rgba("+u[4*c+1]+","+u[4*c+2]+","+u[4*c+3]+","+y+")");i.grd=f}i.coOp=e.o.v*r.opacity},CVShapeElement.prototype.renderStroke=function(t,e,r){var i=e.style,s=e.d;s&&(s._mdf||this._isFirstFrame)&&(i.da=s.dashArray,i.do=s.dashoffset[0]),(e.c._mdf||this._isFirstFrame)&&(i.co="rgb("+bm_floor(e.c.v[0])+","+bm_floor(e.c.v[1])+","+bm_floor(e.c.v[2])+")"),(e.o._mdf||r._opMdf||this._isFirstFrame)&&(i.coOp=e.o.v*r.opacity),(e.w._mdf||this._isFirstFrame)&&(i.wi=e.w.v)},CVShapeElement.prototype.destroy=function(){this.shapesData=null,this.globalData=null,this.canvasContext=null,this.stylesList.length=0,this.itemsData.length=0},extendPrototype([BaseElement,TransformElement,CVBaseElement,HierarchyElement,FrameElement,RenderableElement],CVSolidElement),CVSolidElement.prototype.initElement=SVGShapeElement.prototype.initElement,CVSolidElement.prototype.prepareFrame=IImageElement.prototype.prepareFrame,CVSolidElement.prototype.renderInnerContent=function(){var t=this.canvasContext;t.fillStyle=this.data.sc,t.fillRect(0,0,this.data.sw,this.data.sh)},CVEffects.prototype.renderFrame=function(){};var animationManager=(tJ={},uJ=[],vJ=0,wJ=0,xJ=0,yJ=!0,zJ=!1,tJ.registerAnimation=BJ,tJ.loadAnimation=function(t){var e=new AnimationItem;return FJ(e,null),e.setParams(t),e},tJ.setSpeed=function(t,e){var r;for(r=0;r<wJ;r+=1)uJ[r].animation.setSpeed(t,e)},tJ.setDirection=function(t,e){var r;for(r=0;r<wJ;r+=1)uJ[r].animation.setDirection(t,e)},tJ.play=function(t){var e;for(e=0;e<wJ;e+=1)uJ[e].animation.play(t)},tJ.pause=function(t){var e;for(e=0;e<wJ;e+=1)uJ[e].animation.pause(t)},tJ.stop=function(t){var e;for(e=0;e<wJ;e+=1)uJ[e].animation.stop(t)},tJ.togglePause=function(t){var e;for(e=0;e<wJ;e+=1)uJ[e].animation.togglePause(t)},tJ.searchAnimations=function(t,e,r){var i,s=[].concat([].slice.call(document.getElementsByClassName("lottie")),[].slice.call(document.getElementsByClassName("bodymovin"))),a=s.length;for(i=0;i<a;i+=1)r&&s[i].setAttribute("data-bm-type",r),BJ(s[i],t);if(e&&0===a){r||(r="svg");var n=document.getElementsByTagName("body")[0];n.innerHTML="";var o=createTag("div");o.style.width="100%",o.style.height="100%",o.setAttribute("data-bm-type",r),n.appendChild(o),BJ(o,t)}},tJ.resize=function(){var t;for(t=0;t<wJ;t+=1)uJ[t].animation.resize()},tJ.goToAndStop=function(t,e,r){var i;for(i=0;i<wJ;i+=1)uJ[i].animation.goToAndStop(t,e,r)},tJ.destroy=function(t){var e;for(e=wJ-1;0<=e;e-=1)uJ[e].animation.destroy(t)},tJ.freeze=function(){zJ=!0},tJ.unfreeze=function(){zJ=!1,TJ()},tJ.getRegisteredAnimations=function(){var t,e=uJ.length,r=[];for(t=0;t<e;t+=1)r.push(uJ[t].animation);return r},tJ),tJ,uJ,vJ,wJ,xJ,yJ,zJ,PK,QK,RK,SK,TK,UK,VK;function AJ(t){for(var e=0,r=t.target;e<wJ;)uJ[e].animation===r&&(uJ.splice(e,1),e-=1,wJ-=1,r.isPaused||EJ()),e+=1}function BJ(t,e){if(!t)return null;for(var r=0;r<wJ;){if(uJ[r].elem==t&&null!==uJ[r].elem)return uJ[r].animation;r+=1}var i=new AnimationItem;return FJ(i,t),i.setData(t,e),i}function DJ(){xJ+=1,TJ()}function EJ(){xJ-=1}function FJ(t,e){t.addEventListener("destroy",AJ),t.addEventListener("_active",DJ),t.addEventListener("_idle",EJ),uJ.push({elem:e,animation:t}),wJ+=1}function KJ(t){var e,r=t-vJ;for(e=0;e<wJ;e+=1)uJ[e].animation.advanceTime(r);vJ=t,xJ&&!zJ?window.requestAnimationFrame(KJ):yJ=!0}function LJ(t){vJ=t,window.requestAnimationFrame(KJ)}function TJ(){!zJ&&xJ&&yJ&&(window.requestAnimationFrame(LJ),yJ=!1)}function WK(t){for(var e=0,r=t.target;e<SK;)QK[e].animation===r&&(QK.splice(e,1),e-=1,SK-=1,r.isPaused||$K()),e+=1}function ZK(){TK+=1,nL()}function $K(){TK-=1}function _K(t,e){t.addEventListener("destroy",WK),t.addEventListener("_active",ZK),t.addEventListener("_idle",$K),QK.push({elem:e,animation:t}),SK+=1}function eL(t){var e,r=t-RK;for(e=0;e<SK;e+=1)QK[e].animation.advanceTime(r);RK=t,TK&&!VK?requestAnimationFrame(eL):UK=!0}function fL(t){RK=t,requestAnimationFrame(eL)}function nL(){!VK&&TK&&UK&&(requestAnimationFrame(fL),UK=!1)}PK={},QK=[],RK=0,SK=0,TK=0,UK=!0,VK=!1,PK.registerAnimation=function(t,e){if(!t)return null;for(var r=0;r<SK;){if(QK[r].elem==t&&null!==QK[r].elem)return QK[r].animation;r+=1}var i=new AnimationItem;return _K(i,t),i.setData(t,e),i},PK.loadAnimation=function(t){var e=new AnimationItem;return _K(e,null),e.setParams(t),e},PK.setSpeed=function(t,e){var r;for(r=0;r<SK;r+=1)QK[r].animation.setSpeed(t,e)},PK.setDirection=function(t,e){var r;for(r=0;r<SK;r+=1)QK[r].animation.setDirection(t,e)},PK.play=function(t){var e;for(e=0;e<SK;e+=1)QK[e].animation.play(t)},PK.pause=function(t){var e;for(e=0;e<SK;e+=1)QK[e].animation.pause(t)},PK.stop=function(t){var e;for(e=0;e<SK;e+=1)QK[e].animation.stop(t)},PK.togglePause=function(t){var e;for(e=0;e<SK;e+=1)QK[e].animation.togglePause(t)},PK.searchAnimations=function(t,e,r){throw new Error("Cannot access DOM from worker thread")},PK.resize=function(){var t;for(t=0;t<SK;t+=1)QK[t].animation.resize()},PK.goToAndStop=function(t,e,r){var i;for(i=0;i<SK;i+=1)QK[i].animation.goToAndStop(t,e,r)},PK.destroy=function(t){var e;for(e=SK-1;0<=e;e-=1)QK[e].animation.destroy(t)},PK.freeze=function(){VK=!0},PK.unfreeze=function(){VK=!1,nL()},PK.getRegisteredAnimations=function(){var t,e=QK.length,r=[];for(t=0;t<e;t+=1)r.push(QK[t].animation);return r},animationManager=PK;var AnimationItem=function(){this._cbs=[],this.name="",this.path="",this.isLoaded=!1,this.currentFrame=0,this.currentRawFrame=0,this.totalFrames=0,this.frameRate=0,this.frameMult=0,this.playSpeed=1,this.playDirection=1,this.playCount=0,this.animationData={},this.assets=[],this.isPaused=!0,this.autoplay=!1,this.loop=!0,this.renderer=null,this.animationID=createElementID(),this.assetsPath="",this.timeCompleted=0,this.segmentPos=0,this.subframeEnabled=subframeEnabled,this.segments=[],this._idle=!0,this._completedLoop=!1,this.projectInterface=ProjectInterface(),this.imagePreloader=new ImagePreloader};extendPrototype([BaseEvent],AnimationItem),AnimationItem.prototype.setParams=function(t){t.context&&(this.context=t.context),(t.wrapper||t.container)&&(this.wrapper=t.wrapper||t.container);var e=t.animType?t.animType:t.renderer?t.renderer:"svg";switch(e){case"canvas":this.renderer=new CanvasRenderer(this,t.rendererSettings);break;case"svg":this.renderer=new SVGRenderer(this,t.rendererSettings);break;default:this.renderer=new HybridRenderer(this,t.rendererSettings)}this.renderer.setProjectInterface(this.projectInterface),this.animType=e,""===t.loop||null===t.loop||(!1===t.loop?this.loop=!1:!0===t.loop?this.loop=!0:this.loop=parseInt(t.loop)),this.autoplay=!("autoplay"in t)||t.autoplay,this.name=t.name?t.name:"",this.autoloadSegments=!t.hasOwnProperty("autoloadSegments")||t.autoloadSegments,this.assetsPath=t.assetsPath,t.animationData?this.configAnimation(t.animationData):t.path&&("json"!=t.path.substr(-4)&&("/"!=t.path.substr(-1,1)&&(t.path+="/"),t.path+="data.json"),-1!=t.path.lastIndexOf("\\")?this.path=t.path.substr(0,t.path.lastIndexOf("\\")+1):this.path=t.path.substr(0,t.path.lastIndexOf("/")+1),this.fileName=t.path.substr(t.path.lastIndexOf("/")+1),this.fileName=this.fileName.substr(0,this.fileName.lastIndexOf(".json")),assetLoader.load(t.path,this.configAnimation.bind(this),function(){this.trigger("data_failed")}.bind(this)))},AnimationItem.prototype.setData=function(t,e){var r={wrapper:t,animationData:e?"object"==typeof e?e:JSON.parse(e):null},i=t.attributes;r.path=i.getNamedItem("data-animation-path")?i.getNamedItem("data-animation-path").value:i.getNamedItem("data-bm-path")?i.getNamedItem("data-bm-path").value:i.getNamedItem("bm-path")?i.getNamedItem("bm-path").value:"",r.animType=i.getNamedItem("data-anim-type")?i.getNamedItem("data-anim-type").value:i.getNamedItem("data-bm-type")?i.getNamedItem("data-bm-type").value:i.getNamedItem("bm-type")?i.getNamedItem("bm-type").value:i.getNamedItem("data-bm-renderer")?i.getNamedItem("data-bm-renderer").value:i.getNamedItem("bm-renderer")?i.getNamedItem("bm-renderer").value:"canvas";var s=i.getNamedItem("data-anim-loop")?i.getNamedItem("data-anim-loop").value:i.getNamedItem("data-bm-loop")?i.getNamedItem("data-bm-loop").value:i.getNamedItem("bm-loop")?i.getNamedItem("bm-loop").value:"";""===s||(r.loop="false"!==s&&("true"===s||parseInt(s)));var a=i.getNamedItem("data-anim-autoplay")?i.getNamedItem("data-anim-autoplay").value:i.getNamedItem("data-bm-autoplay")?i.getNamedItem("data-bm-autoplay").value:!i.getNamedItem("bm-autoplay")||i.getNamedItem("bm-autoplay").value;r.autoplay="false"!==a,r.name=i.getNamedItem("data-name")?i.getNamedItem("data-name").value:i.getNamedItem("data-bm-name")?i.getNamedItem("data-bm-name").value:i.getNamedItem("bm-name")?i.getNamedItem("bm-name").value:"","false"===(i.getNamedItem("data-anim-prerender")?i.getNamedItem("data-anim-prerender").value:i.getNamedItem("data-bm-prerender")?i.getNamedItem("data-bm-prerender").value:i.getNamedItem("bm-prerender")?i.getNamedItem("bm-prerender").value:"")&&(r.prerender=!1),this.setParams(r)},AnimationItem.prototype.includeLayers=function(t){t.op>this.animationData.op&&(this.animationData.op=t.op,this.totalFrames=Math.floor(t.op-this.animationData.ip));var e,r,i=this.animationData.layers,s=i.length,a=t.layers,n=a.length;for(r=0;r<n;r+=1)for(e=0;e<s;){if(i[e].id==a[r].id){i[e]=a[r];break}e+=1}if((t.chars||t.fonts)&&(this.renderer.globalData.fontManager.addChars(t.chars),this.renderer.globalData.fontManager.addFonts(t.fonts,this.renderer.globalData.defs)),t.assets)for(s=t.assets.length,e=0;e<s;e+=1)this.animationData.assets.push(t.assets[e]);this.animationData.__complete=!1,dataManager.completeData(this.animationData,this.renderer.globalData.fontManager),this.renderer.includeLayers(t.layers),expressionsPlugin&&expressionsPlugin.initExpressions(this),this.loadNextSegment()},AnimationItem.prototype.loadNextSegment=function(){var t=this.animationData.segments;if(!t||0===t.length||!this.autoloadSegments)return this.trigger("data_ready"),void(this.timeCompleted=this.totalFrames);var e=t.shift();this.timeCompleted=e.time*this.frameRate;var r=this.path+this.fileName+"_"+this.segmentPos+".json";this.segmentPos+=1,assetLoader.load(r,this.includeLayers.bind(this),function(){this.trigger("data_failed")}.bind(this))},AnimationItem.prototype.loadSegments=function(){this.animationData.segments||(this.timeCompleted=this.totalFrames),this.loadNextSegment()},AnimationItem.prototype.imagesLoaded=function(){this.trigger("loaded_images"),this.checkLoaded()},AnimationItem.prototype.preloadImages=function(){this.imagePreloader.setAssetsPath(this.assetsPath),this.imagePreloader.setPath(this.path),this.imagePreloader.loadAssets(this.animationData.assets,this.imagesLoaded.bind(this))},AnimationItem.prototype.configAnimation=function(t){this.renderer&&(this.animationData=t,this.totalFrames=Math.floor(this.animationData.op-this.animationData.ip),this.renderer.configAnimation(t),t.assets||(t.assets=[]),this.renderer.searchExtraCompositions(t.assets),this.assets=this.animationData.assets,this.frameRate=this.animationData.fr,this.firstFrame=Math.round(this.animationData.ip),this.frameMult=this.animationData.fr/1e3,this.trigger("config_ready"),this.preloadImages(),this.loadSegments(),this.updaFrameModifier(),this.waitForFontsLoaded())},AnimationItem.prototype.waitForFontsLoaded=function(){this.renderer&&(this.renderer.globalData.fontManager.loaded()?this.checkLoaded():setTimeout(this.waitForFontsLoaded.bind(this),20))},AnimationItem.prototype.checkLoaded=function(){this.isLoaded||!this.renderer.globalData.fontManager.loaded()||!this.imagePreloader.loaded()&&"canvas"===this.renderer.rendererType||(this.isLoaded=!0,dataManager.completeData(this.animationData,this.renderer.globalData.fontManager),expressionsPlugin&&expressionsPlugin.initExpressions(this),this.renderer.initItems(),setTimeout(function(){this.trigger("DOMLoaded")}.bind(this),0),this.gotoFrame(),this.autoplay&&this.play())},AnimationItem.prototype.resize=function(){this.renderer.updateContainerSize()},AnimationItem.prototype.setSubframe=function(t){this.subframeEnabled=!!t},AnimationItem.prototype.gotoFrame=function(){this.currentFrame=this.subframeEnabled?this.currentRawFrame:~~this.currentRawFrame,this.timeCompleted!==this.totalFrames&&this.currentFrame>this.timeCompleted&&(this.currentFrame=this.timeCompleted),this.trigger("enterFrame"),this.renderFrame()},AnimationItem.prototype.renderFrame=function(){!1!==this.isLoaded&&this.renderer.renderFrame(this.currentFrame+this.firstFrame)},AnimationItem.prototype.play=function(t){t&&this.name!=t||!0===this.isPaused&&(this.isPaused=!1,this._idle&&(this._idle=!1,this.trigger("_active")))},AnimationItem.prototype.pause=function(t){t&&this.name!=t||!1===this.isPaused&&(this.isPaused=!0,this._idle=!0,this.trigger("_idle"))},AnimationItem.prototype.togglePause=function(t){t&&this.name!=t||(!0===this.isPaused?this.play():this.pause())},AnimationItem.prototype.stop=function(t){t&&this.name!=t||(this.pause(),this.playCount=0,this._completedLoop=!1,this.setCurrentRawFrameValue(0))},AnimationItem.prototype.goToAndStop=function(t,e,r){r&&this.name!=r||(e?this.setCurrentRawFrameValue(t):this.setCurrentRawFrameValue(t*this.frameModifier),this.pause())},AnimationItem.prototype.goToAndPlay=function(t,e,r){this.goToAndStop(t,e,r),this.play()},AnimationItem.prototype.advanceTime=function(t){if(!0!==this.isPaused&&!1!==this.isLoaded){var e=this.currentRawFrame+t*this.frameModifier,r=!1;e>=this.totalFrames-1&&0<this.frameModifier?this.loop&&this.playCount!==this.loop?e>=this.totalFrames?(this.playCount+=1,this.checkSegments(e%this.totalFrames)||(this.setCurrentRawFrameValue(e%this.totalFrames),this._completedLoop=!0,this.trigger("loopComplete"))):this.setCurrentRawFrameValue(e):this.checkSegments(e>this.totalFrames?e%this.totalFrames:0)||(r=!0,e=this.totalFrames-1):e<0?this.checkSegments(e%this.totalFrames)||(!this.loop||this.playCount--<=0&&!0!==this.loop?(r=!0,e=0):(this.setCurrentRawFrameValue(this.totalFrames+e%this.totalFrames),this._completedLoop?this.trigger("loopComplete"):this._completedLoop=!0)):this.setCurrentRawFrameValue(e),r&&(this.setCurrentRawFrameValue(e),this.pause(),this.trigger("complete"))}},AnimationItem.prototype.adjustSegment=function(t,e){this.playCount=0,t[1]<t[0]?(0<this.frameModifier&&(this.playSpeed<0?this.setSpeed(-this.playSpeed):this.setDirection(-1)),this.timeCompleted=this.totalFrames=t[0]-t[1],this.firstFrame=t[1],this.setCurrentRawFrameValue(this.totalFrames-.001-e)):t[1]>t[0]&&(this.frameModifier<0&&(this.playSpeed<0?this.setSpeed(-this.playSpeed):this.setDirection(1)),this.timeCompleted=this.totalFrames=t[1]-t[0],this.firstFrame=t[0],this.setCurrentRawFrameValue(.001+e)),this.trigger("segmentStart")},AnimationItem.prototype.setSegment=function(t,e){var r=-1;this.isPaused&&(this.currentRawFrame+this.firstFrame<t?r=t:this.currentRawFrame+this.firstFrame>e&&(r=e-t)),this.firstFrame=t,this.timeCompleted=this.totalFrames=e-t,-1!==r&&this.goToAndStop(r,!0)},AnimationItem.prototype.playSegments=function(t,e){if(e&&(this.segments.length=0),"object"==typeof t[0]){var r,i=t.length;for(r=0;r<i;r+=1)this.segments.push(t[r])}else this.segments.push(t);this.segments.length&&e&&this.adjustSegment(this.segments.shift(),0),this.isPaused&&this.play()},AnimationItem.prototype.resetSegments=function(t){this.segments.length=0,this.segments.push([this.animationData.ip,this.animationData.op]),t&&this.checkSegments(0)},AnimationItem.prototype.checkSegments=function(t){return!!this.segments.length&&(this.adjustSegment(this.segments.shift(),t),!0)},AnimationItem.prototype.destroy=function(t){t&&this.name!=t||!this.renderer||(this.renderer.destroy(),this.imagePreloader.destroy(),this.trigger("destroy"),this._cbs=null,this.onEnterFrame=this.onLoopComplete=this.onComplete=this.onSegmentStart=this.onDestroy=null,this.renderer=null)},AnimationItem.prototype.setCurrentRawFrameValue=function(t){this.currentRawFrame=t,this.gotoFrame()},AnimationItem.prototype.setSpeed=function(t){this.playSpeed=t,this.updaFrameModifier()},AnimationItem.prototype.setDirection=function(t){this.playDirection=t<0?-1:1,this.updaFrameModifier()},AnimationItem.prototype.updaFrameModifier=function(){this.frameModifier=this.frameMult*this.playSpeed*this.playDirection},AnimationItem.prototype.getPath=function(){return this.path},AnimationItem.prototype.getAssetsPath=function(t){var e="";if(t.e)e=t.p;else if(this.assetsPath){var r=t.p;-1!==r.indexOf("images/")&&(r=r.split("/")[1]),e=this.assetsPath+r}else e=this.path,e+=t.u?t.u:"",e+=t.p;return e},AnimationItem.prototype.getAssetData=function(t){for(var e=0,r=this.assets.length;e<r;){if(t==this.assets[e].id)return this.assets[e];e+=1}},AnimationItem.prototype.hide=function(){this.renderer.hide()},AnimationItem.prototype.show=function(){this.renderer.show()},AnimationItem.prototype.getDuration=function(t){return t?this.totalFrames:this.totalFrames/this.frameRate},AnimationItem.prototype.trigger=function(t){if(this._cbs&&this._cbs[t])switch(t){case"enterFrame":this.triggerEvent(t,new BMEnterFrameEvent(t,this.currentFrame,this.totalFrames,this.frameModifier));break;case"loopComplete":this.triggerEvent(t,new BMCompleteLoopEvent(t,this.loop,this.playCount,this.frameMult));break;case"complete":this.triggerEvent(t,new BMCompleteEvent(t,this.frameMult));break;case"segmentStart":this.triggerEvent(t,new BMSegmentStartEvent(t,this.firstFrame,this.totalFrames));break;case"destroy":this.triggerEvent(t,new BMDestroyEvent(t,this));break;default:this.triggerEvent(t)}"enterFrame"===t&&this.onEnterFrame&&this.onEnterFrame.call(this,new BMEnterFrameEvent(t,this.currentFrame,this.totalFrames,this.frameMult)),"loopComplete"===t&&this.onLoopComplete&&this.onLoopComplete.call(this,new BMCompleteLoopEvent(t,this.loop,this.playCount,this.frameMult)),"complete"===t&&this.onComplete&&this.onComplete.call(this,new BMCompleteEvent(t,this.frameMult)),"segmentStart"===t&&this.onSegmentStart&&this.onSegmentStart.call(this,new BMSegmentStartEvent(t,this.firstFrame,this.totalFrames)),"destroy"===t&&this.onDestroy&&this.onDestroy.call(this,new BMDestroyEvent(t,this))},AnimationItem.prototype.setParams=function(t){t.context&&(this.context=t.context);var e=t.animType?t.animType:t.renderer?t.renderer:"svg";switch(e){case"canvas":this.renderer=new CanvasRenderer(this,t.rendererSettings);break;default:throw new Error("Only canvas renderer is supported when using worker.")}if(this.renderer.setProjectInterface(this.projectInterface),this.animType=e,""===t.loop||null===t.loop||(!1===t.loop?this.loop=!1:!0===t.loop?this.loop=!0:this.loop=parseInt(t.loop)),this.autoplay=!("autoplay"in t)||t.autoplay,this.name=t.name?t.name:"",this.autoloadSegments=!t.hasOwnProperty("autoloadSegments")||t.autoloadSegments,this.assetsPath=null,t.animationData)this.configAnimation(t.animationData);else if(t.path)throw new Error("Canvas worker renderer cannot load animation from url")},AnimationItem.prototype.setData=function(t,e){throw new Error("Cannot set data on wrapper for canvas worker renderer")},AnimationItem.prototype.includeLayers=function(t){t.op>this.animationData.op&&(this.animationData.op=t.op,this.totalFrames=Math.floor(t.op-this.animationData.ip));var e,r,i=this.animationData.layers,s=i.length,a=t.layers,n=a.length;for(r=0;r<n;r+=1)for(e=0;e<s;){if(i[e].id==a[r].id){i[e]=a[r];break}e+=1}this.animationData.__complete=!1,dataManager.completeData(this.animationData,this.renderer.globalData.fontManager),this.renderer.includeLayers(t.layers),expressionsPlugin&&expressionsPlugin.initExpressions(this),this.loadNextSegment()},AnimationItem.prototype.loadNextSegment=function(){var t=this.animationData.segments;if(t&&0!==t.length&&this.autoloadSegments)throw new Error("Cannot load multiple segments in worker.");this.timeCompleted=this.totalFrames},AnimationItem.prototype.loadSegments=function(){this.animationData.segments||(this.timeCompleted=this.totalFrames),this.loadNextSegment()},AnimationItem.prototype.imagesLoaded=null,AnimationItem.prototype.preloadImages=null,AnimationItem.prototype.configAnimation=function(t){this.renderer&&(this.animationData=t,this.totalFrames=Math.floor(this.animationData.op-this.animationData.ip),this.renderer.configAnimation(t),t.assets||(t.assets=[]),this.renderer.searchExtraCompositions(t.assets),this.assets=this.animationData.assets,this.frameRate=this.animationData.fr,this.firstFrame=Math.round(this.animationData.ip),this.frameMult=this.animationData.fr/1e3,this.loadSegments(),this.updaFrameModifier(),this.checkLoaded())},AnimationItem.prototype.waitForFontsLoaded=null,AnimationItem.prototype.checkLoaded=function(){this.isLoaded||(this.isLoaded=!0,dataManager.completeData(this.animationData,null),expressionsPlugin&&expressionsPlugin.initExpressions(this),this.renderer.initItems(),this.gotoFrame())},AnimationItem.prototype.destroy=function(t){t&&this.name!=t||!this.renderer||(this.renderer.destroy(),this._cbs=null,this.onEnterFrame=this.onLoopComplete=this.onComplete=this.onSegmentStart=this.onDestroy=null,this.renderer=null)},AnimationItem.prototype.getPath=null;var Expressions=(xN={},xN.initExpressions=function(t){var e=0,r=[];t.renderer.compInterface=CompExpressionInterface(t.renderer),t.renderer.globalData.projectInterface.registerComposition(t.renderer),t.renderer.globalData.pushExpression=function(){e+=1},t.renderer.globalData.popExpression=function(){0==(e-=1)&&function(){var t,e=r.length;for(t=0;t<e;t+=1)r[t].release();r.length=0}()},t.renderer.globalData.registerExpressionProperty=function(t){-1===r.indexOf(t)&&r.push(t)}},xN),xN;expressionsPlugin=Expressions;var ExpressionManager=function(){var ob={},Math=BMMath,window=null,document=null;function $bm_isInstanceOfArray(t){return t.constructor===Array||t.constructor===Float32Array}function isNumerable(t,e){return"number"===t||"boolean"===t||"string"===t||e instanceof Number}function $bm_neg(t){var e=typeof t;if("number"==e||"boolean"==e||t instanceof Number)return-t;if($bm_isInstanceOfArray(t)){var r,i=t.length,s=[];for(r=0;r<i;r+=1)s[r]=-t[r];return s}return t.propType?t.v:void 0}var easeInBez=BezierFactory.getBezierEasing(.333,0,.833,.833,"easeIn").get,easeOutBez=BezierFactory.getBezierEasing(.167,.167,.667,1,"easeOut").get,easeInOutBez=BezierFactory.getBezierEasing(.33,0,.667,1,"easeInOut").get;function sum(t,e){var r=typeof t,i=typeof e;if("string"==r||"string"==i)return t+e;if(isNumerable(r,t)&&isNumerable(i,e))return t+e;if($bm_isInstanceOfArray(t)&&isNumerable(i,e))return(t=t.slice(0))[0]=t[0]+e,t;if(isNumerable(r,t)&&$bm_isInstanceOfArray(e))return(e=e.slice(0))[0]=t+e[0],e;if($bm_isInstanceOfArray(t)&&$bm_isInstanceOfArray(e)){for(var s=0,a=t.length,n=e.length,o=[];s<a||s<n;)("number"==typeof t[s]||t[s]instanceof Number)&&("number"==typeof e[s]||e[s]instanceof Number)?o[s]=t[s]+e[s]:o[s]=void 0===e[s]?t[s]:t[s]||e[s],s+=1;return o}return 0}var add=sum;function sub(t,e){var r=typeof t,i=typeof e;if(isNumerable(r,t)&&isNumerable(i,e))return"string"==r&&(t=parseInt(t)),"string"==i&&(e=parseInt(e)),t-e;if($bm_isInstanceOfArray(t)&&isNumerable(i,e))return(t=t.slice(0))[0]=t[0]-e,t;if(isNumerable(r,t)&&$bm_isInstanceOfArray(e))return(e=e.slice(0))[0]=t-e[0],e;if($bm_isInstanceOfArray(t)&&$bm_isInstanceOfArray(e)){for(var s=0,a=t.length,n=e.length,o=[];s<a||s<n;)("number"==typeof t[s]||t[s]instanceof Number)&&("number"==typeof e[s]||e[s]instanceof Number)?o[s]=t[s]-e[s]:o[s]=void 0===e[s]?t[s]:t[s]||e[s],s+=1;return o}return 0}function mul(t,e){var r,i,s,a=typeof t,n=typeof e;if(isNumerable(a,t)&&isNumerable(n,e))return t*e;if($bm_isInstanceOfArray(t)&&isNumerable(n,e)){for(s=t.length,r=createTypedArray("float32",s),i=0;i<s;i+=1)r[i]=t[i]*e;return r}if(isNumerable(a,t)&&$bm_isInstanceOfArray(e)){for(s=e.length,r=createTypedArray("float32",s),i=0;i<s;i+=1)r[i]=t*e[i];return r}return 0}function div(t,e){var r,i,s,a=typeof t,n=typeof e;if(isNumerable(a,t)&&isNumerable(n,e))return t/e;if($bm_isInstanceOfArray(t)&&isNumerable(n,e)){for(s=t.length,r=createTypedArray("float32",s),i=0;i<s;i+=1)r[i]=t[i]/e;return r}if(isNumerable(a,t)&&$bm_isInstanceOfArray(e)){for(s=e.length,r=createTypedArray("float32",s),i=0;i<s;i+=1)r[i]=t/e[i];return r}return 0}function mod(t,e){return"string"==typeof t&&(t=parseInt(t)),"string"==typeof e&&(e=parseInt(e)),t%e}var $bm_sum=sum,$bm_sub=sub,$bm_mul=mul,$bm_div=div,$bm_mod=mod;function clamp(t,e,r){if(r<e){var i=r;r=e,e=i}return Math.min(Math.max(t,e),r)}function radiansToDegrees(t){return t/degToRads}var radians_to_degrees=radiansToDegrees;function degreesToRadians(t){return t*degToRads}var degrees_to_radians=radiansToDegrees,helperLengthArray=[0,0,0,0,0,0];function length(t,e){if("number"==typeof t||t instanceof Number)return e=e||0,Math.abs(t-e);e||(e=helperLengthArray);var r,i=Math.min(t.length,e.length),s=0;for(r=0;r<i;r+=1)s+=Math.pow(e[r]-t[r],2);return Math.sqrt(s)}function normalize(t){return div(t,length(t))}function rgbToHsl(t){var e,r,i=t[0],s=t[1],a=t[2],n=Math.max(i,s,a),o=Math.min(i,s,a),h=(n+o)/2;if(n==o)e=r=0;else{var p=n-o;switch(r=.5<h?p/(2-n-o):p/(n+o),n){case i:e=(s-a)/p+(s<a?6:0);break;case s:e=(a-i)/p+2;break;case a:e=(i-s)/p+4}e/=6}return[e,r,h,t[3]]}function hue2rgb(t,e,r){return r<0&&(r+=1),1<r&&(r-=1),r<1/6?t+6*(e-t)*r:r<.5?e:r<2/3?t+(e-t)*(2/3-r)*6:t}function hslToRgb(t){var e,r,i,s=t[0],a=t[1],n=t[2];if(0===a)e=r=i=n;else{var o=n<.5?n*(1+a):n+a-n*a,h=2*n-o;e=hue2rgb(h,o,s+1/3),r=hue2rgb(h,o,s),i=hue2rgb(h,o,s-1/3)}return[e,r,i,t[3]]}function linear(t,e,r,i,s){if(void 0!==i&&void 0!==s||(i=e,s=r,e=0,r=1),r<e){var a=r;r=e,e=a}if(t<=e)return i;if(r<=t)return s;var n=r===e?0:(t-e)/(r-e);if(!i.length)return i+(s-i)*n;var o,h=i.length,p=createTypedArray("float32",h);for(o=0;o<h;o+=1)p[o]=i[o]+(s[o]-i[o])*n;return p}function random(t,e){if(void 0===e&&(void 0===t?(t=0,e=1):(e=t,t=void 0)),e.length){var r,i=e.length;t||(t=createTypedArray("float32",i));var s=createTypedArray("float32",i),a=BMMath.random();for(r=0;r<i;r+=1)s[r]=t[r]+a*(e[r]-t[r]);return s}return void 0===t&&(t=0),t+BMMath.random()*(e-t)}function createPath(t,e,r,i){var s,a=t.length,n=shape_pool.newElement();n.setPathData(!!i,a);var o,h,p=[0,0];for(s=0;s<a;s+=1)o=e&&e[s]?e[s]:p,h=r&&r[s]?r[s]:p,n.setTripleAt(t[s][0],t[s][1],h[0]+t[s][0],h[1]+t[s][1],o[0]+t[s][0],o[1]+t[s][1],s,!0);return n}function initiateExpression(elem,data,property){var val=data.x,needsVelocity=/velocity(?![\w\d])/.test(val),_needsRandom=-1!==val.indexOf("random"),elemType=elem.data.ty,transform,$bm_transform,content,effect,thisProperty=property;thisProperty.valueAtTime=thisProperty.getValueAtTime,Object.defineProperty(thisProperty,"value",{get:function(){return thisProperty.v}}),elem.comp.frameDuration=1/elem.comp.globalData.frameRate,elem.comp.displayStartTime=0;var inPoint=elem.data.ip/elem.comp.globalData.frameRate,outPoint=elem.data.op/elem.comp.globalData.frameRate,width=elem.data.sw?elem.data.sw:0,height=elem.data.sh?elem.data.sh:0,name=elem.data.nm,loopIn,loop_in,loopOut,loop_out,smooth,toWorld,fromWorld,fromComp,toComp,fromCompToSurface,position,rotation,anchorPoint,scale,thisLayer,thisComp,mask,valueAtTime,velocityAtTime,__expression_functions=[],scoped_bm_rt;if(data.xf){var i,len=data.xf.length;for(i=0;i<len;i+=1)__expression_functions[i]=eval("(function(){ return "+data.xf[i]+"}())")}var expression_function=eval("[function _expression_function(){"+val+";scoped_bm_rt=$bm_rt}]")[0],numKeys=property.kf?data.k.length:0,active=!this.data||!0!==this.data.hd,wiggle=function(t,e){var r,i,s=this.pv.length?this.pv.length:1,a=createTypedArray("float32",s);var n=Math.floor(5*time);for(i=r=0;r<n;){for(i=0;i<s;i+=1)a[i]+=-e+2*e*BMMath.random();r+=1}var o=5*time,h=o-Math.floor(o),p=createTypedArray("float32",s);if(1<s){for(i=0;i<s;i+=1)p[i]=this.pv[i]+a[i]+(-e+2*e*BMMath.random())*h;return p}return this.pv+a[0]+(-e+2*e*BMMath.random())*h}.bind(this);function loopInDuration(t,e){return loopIn(t,e,!0)}function loopOutDuration(t,e){return loopOut(t,e,!0)}thisProperty.loopIn&&(loopIn=thisProperty.loopIn.bind(thisProperty),loop_in=loopIn),thisProperty.loopOut&&(loopOut=thisProperty.loopOut.bind(thisProperty),loop_out=loopOut),thisProperty.smooth&&(smooth=thisProperty.smooth.bind(thisProperty)),this.getValueAtTime&&(valueAtTime=this.getValueAtTime.bind(this)),this.getVelocityAtTime&&(velocityAtTime=this.getVelocityAtTime.bind(this));var comp=elem.comp.globalData.projectInterface.bind(elem.comp.globalData.projectInterface),time,velocity,value,text,textIndex,textTotal,selectorValue;function lookAt(t,e){var r=[e[0]-t[0],e[1]-t[1],e[2]-t[2]],i=Math.atan2(r[0],Math.sqrt(r[1]*r[1]+r[2]*r[2]))/degToRads;return[-Math.atan2(r[1],r[2])/degToRads,i,0]}function easeOut(t,e,r,i,s){return applyEase(easeOutBez,t,e,r,i,s)}function easeIn(t,e,r,i,s){return applyEase(easeInBez,t,e,r,i,s)}function ease(t,e,r,i,s){return applyEase(easeInOutBez,t,e,r,i,s)}function applyEase(t,e,r,i,s,a){void 0===s?(s=r,a=i):e=(e-r)/(i-r);var n=t(e=1<e?1:e<0?0:e);if($bm_isInstanceOfArray(s)){var o,h=s.length,p=createTypedArray("float32",h);for(o=0;o<h;o+=1)p[o]=(a[o]-s[o])*n+s[o];return p}return(a-s)*n+s}function nearestKey(t){var e,r,i,s=data.k.length;if(data.k.length&&"number"!=typeof data.k[0])if(r=-1,(t*=elem.comp.globalData.frameRate)<data.k[0].t)r=1,i=data.k[0].t;else{for(e=0;e<s-1;e+=1){if(t===data.k[e].t){r=e+1,i=data.k[e].t;break}if(t>data.k[e].t&&t<data.k[e+1].t){i=t-data.k[e].t>data.k[e+1].t-t?(r=e+2,data.k[e+1].t):(r=e+1,data.k[e].t);break}}-1===r&&(r=e+1,i=data.k[e].t)}else i=r=0;var a={};return a.index=r,a.time=i/elem.comp.globalData.frameRate,a}function key(t){var e,r,i,s;if(!data.k.length||"number"==typeof data.k[0])throw new Error("The property has no keyframe at index "+t);for(t-=1,e={time:data.k[t].t/elem.comp.globalData.frameRate,value:[]},i=(s=t!==data.k.length-1||data.k[t].h?data.k[t].s:data.k[t].s||0===data.k[t].s?data.k[t-1].s:data.k[t].e).length,r=0;r<i;r+=1)e[r]=s[r],e.value[r]=s[r];return e}function framesToTime(t,e){return e||(e=elem.comp.globalData.frameRate),t/e}function timeToFrames(t,e){return t||0===t||(t=time),e||(e=elem.comp.globalData.frameRate),t*e}function seedRandom(t){BMMath.seedrandom(randSeed+t)}function sourceRectAtTime(){return elem.sourceRectAtTime()}function substring(t,e){return"string"==typeof value?void 0===e?value.substring(t):value.substring(t,e):""}function substr(t,e){return"string"==typeof value?void 0===e?value.substr(t):value.substr(t,e):""}var index=elem.data.ind,hasParent=!(!elem.hierarchy||!elem.hierarchy.length),parent,randSeed=Math.floor(1e6*Math.random()),globalData=elem.globalData;function executeExpression(t){return value=t,_needsRandom&&seedRandom(randSeed),this.frameExpressionId===elem.globalData.frameId&&"textSelector"!==this.propType?value:("textSelector"===this.propType&&(textIndex=this.textIndex,textTotal=this.textTotal,selectorValue=this.selectorValue),thisLayer||(text=elem.layerInterface.text,thisLayer=elem.layerInterface,thisComp=elem.comp.compInterface,toWorld=thisLayer.toWorld.bind(thisLayer),fromWorld=thisLayer.fromWorld.bind(thisLayer),fromComp=thisLayer.fromComp.bind(thisLayer),toComp=thisLayer.toComp.bind(thisLayer),mask=thisLayer.mask?thisLayer.mask.bind(thisLayer):null,fromCompToSurface=fromComp),transform||(transform=elem.layerInterface("ADBE Transform Group"),($bm_transform=transform)&&(anchorPoint=transform.anchorPoint)),4!==elemType||content||(content=thisLayer("ADBE Root Vectors Group")),effect||(effect=thisLayer(4)),(hasParent=!(!elem.hierarchy||!elem.hierarchy.length))&&!parent&&(parent=elem.hierarchy[0].layerInterface),time=this.comp.renderedFrame/this.comp.globalData.frameRate,needsVelocity&&(velocity=velocityAtTime(time)),expression_function(),this.frameExpressionId=elem.globalData.frameId,"shape"===scoped_bm_rt.propType&&(scoped_bm_rt=scoped_bm_rt.v),scoped_bm_rt)}return executeExpression}return ob.initiateExpression=initiateExpression,ob}(),expressionHelpers={searchExpressions:function(t,e,r){e.x&&(r.k=!0,r.x=!0,r.initiateExpression=ExpressionManager.initiateExpression,r.effectsSequence.push(r.initiateExpression(t,e,r).bind(r)))},getSpeedAtTime:function(t){var e=this.getValueAtTime(t),r=this.getValueAtTime(t+-.01),i=0;if(e.length){var s;for(s=0;s<e.length;s+=1)i+=Math.pow(r[s]-e[s],2);i=100*Math.sqrt(i)}else i=0;return i},getVelocityAtTime:function(t){if(void 0!==this.vel)return this.vel;var e,r,i=this.getValueAtTime(t),s=this.getValueAtTime(t+-.001);if(i.length)for(e=createTypedArray("float32",i.length),r=0;r<i.length;r+=1)e[r]=(s[r]-i[r])/-.001;else e=(s-i)/-.001;return e},getValueAtTime:function(t){return t*=this.elem.globalData.frameRate,(t-=this.offsetTime)!==this._cachingAtTime.lastFrame&&(this._cachingAtTime.lastIndex=this._cachingAtTime.lastFrame<t?this._cachingAtTime.lastIndex:0,this._cachingAtTime.value=this.interpolateValue(t,this._cachingAtTime),this._cachingAtTime.lastFrame=t),this._cachingAtTime.value},getStaticValueAtTime:function(){return this.pv},setGroupProperty:function(t){this.propertyGroup=t}};!function(){function o(t,e,r){if(!this.k||!this.keyframes)return this.pv;t=t?t.toLowerCase():"";var i,s,a,n,o,h=this.comp.renderedFrame,p=this.keyframes,l=p[p.length-1].t;if(h<=l)return this.pv;if(r?s=l-(i=e?Math.abs(l-elem.comp.globalData.frameRate*e):Math.max(0,l-this.elem.data.ip)):((!e||e>p.length-1)&&(e=p.length-1),i=l-(s=p[p.length-1-e].t)),"pingpong"===t){if(Math.floor((h-s)/i)%2!=0)return this.getValueAtTime((i-(h-s)%i+s)/this.comp.globalData.frameRate,0)}else{if("offset"===t){var m=this.getValueAtTime(s/this.comp.globalData.frameRate,0),f=this.getValueAtTime(l/this.comp.globalData.frameRate,0),c=this.getValueAtTime(((h-s)%i+s)/this.comp.globalData.frameRate,0),d=Math.floor((h-s)/i);if(this.pv.length){for(n=(o=new Array(m.length)).length,a=0;a<n;a+=1)o[a]=(f[a]-m[a])*d+c[a];return o}return(f-m)*d+c}if("continue"===t){var u=this.getValueAtTime(l/this.comp.globalData.frameRate,0),y=this.getValueAtTime((l-.001)/this.comp.globalData.frameRate,0);if(this.pv.length){for(n=(o=new Array(u.length)).length,a=0;a<n;a+=1)o[a]=u[a]+(u[a]-y[a])*((h-l)/this.comp.globalData.frameRate)/5e-4;return o}return u+(h-l)/.001*(u-y)}}return this.getValueAtTime(((h-s)%i+s)/this.comp.globalData.frameRate,0)}function h(t,e,r){if(!this.k)return this.pv;t=t?t.toLowerCase():"";var i,s,a,n,o,h=this.comp.renderedFrame,p=this.keyframes,l=p[0].t;if(l<=h)return this.pv;if(r?s=l+(i=e?Math.abs(elem.comp.globalData.frameRate*e):Math.max(0,this.elem.data.op-l)):((!e||e>p.length-1)&&(e=p.length-1),i=(s=p[e].t)-l),"pingpong"===t){if(Math.floor((l-h)/i)%2==0)return this.getValueAtTime(((l-h)%i+l)/this.comp.globalData.frameRate,0)}else{if("offset"===t){var m=this.getValueAtTime(l/this.comp.globalData.frameRate,0),f=this.getValueAtTime(s/this.comp.globalData.frameRate,0),c=this.getValueAtTime((i-(l-h)%i+l)/this.comp.globalData.frameRate,0),d=Math.floor((l-h)/i)+1;if(this.pv.length){for(n=(o=new Array(m.length)).length,a=0;a<n;a+=1)o[a]=c[a]-(f[a]-m[a])*d;return o}return c-(f-m)*d}if("continue"===t){var u=this.getValueAtTime(l/this.comp.globalData.frameRate,0),y=this.getValueAtTime((l+.001)/this.comp.globalData.frameRate,0);if(this.pv.length){for(n=(o=new Array(u.length)).length,a=0;a<n;a+=1)o[a]=u[a]+(u[a]-y[a])*(l-h)/.001;return o}return u+(u-y)*(l-h)/.001}}return this.getValueAtTime((i-(l-h)%i+l)/this.comp.globalData.frameRate,0)}function p(t,e){if(!this.k)return this.pv;if(t=.5*(t||.4),(e=Math.floor(e||5))<=1)return this.pv;var r,i,s=this.comp.renderedFrame/this.comp.globalData.frameRate,a=s-t,n=1<e?(s+t-a)/(e-1):1,o=0,h=0;for(r=this.pv.length?createTypedArray("float32",this.pv.length):0;o<e;){if(i=this.getValueAtTime(a+o*n),this.pv.length)for(h=0;h<this.pv.length;h+=1)r[h]+=i[h];else r+=i;o+=1}if(this.pv.length)for(h=0;h<this.pv.length;h+=1)r[h]/=e;else r/=e;return r}var s=TransformPropertyFactory.getTransformProperty;TransformPropertyFactory.getTransformProperty=function(t,e,r){var i=s(t,e,r);return i.dynamicProperties.length?i.getValueAtTime=function(t){console.warn("Transform at time not supported")}.bind(i):i.getValueAtTime=function(t){}.bind(i),i.setGroupProperty=expressionHelpers.setGroupProperty,i};var l=PropertyFactory.getProp;PropertyFactory.getProp=function(t,e,r,i,s){var a=l(t,e,r,i,s);a.kf?a.getValueAtTime=expressionHelpers.getValueAtTime.bind(a):a.getValueAtTime=expressionHelpers.getStaticValueAtTime.bind(a),a.setGroupProperty=expressionHelpers.setGroupProperty,a.loopOut=o,a.loopIn=h,a.smooth=p,a.getVelocityAtTime=expressionHelpers.getVelocityAtTime.bind(a),a.getSpeedAtTime=expressionHelpers.getSpeedAtTime.bind(a),a.numKeys=1===e.a?e.k.length:0,a.propertyIndex=e.ix;var n=0;return 0!==r&&(n=createTypedArray("float32",1===e.a?e.k[0].s.length:e.k.length)),a._cachingAtTime={lastFrame:initialDefaultFrame,lastIndex:0,value:n},expressionHelpers.searchExpressions(t,e,a),a.k&&s.addDynamicProperty(a),a};var t=ShapePropertyFactory.getConstructorFunction(),e=ShapePropertyFactory.getKeyframedConstructorFunction();function r(){}r.prototype={vertices:function(t,e){this.k&&this.getValue();var r=this.v;void 0!==e&&(r=this.getValueAtTime(e,0));var i,s=r._length,a=r[t],n=r.v,o=createSizedArray(s);for(i=0;i<s;i+=1)o[i]="i"===t||"o"===t?[a[i][0]-n[i][0],a[i][1]-n[i][1]]:[a[i][0],a[i][1]];return o},points:function(t){return this.vertices("v",t)},inTangents:function(t){return this.vertices("i",t)},outTangents:function(t){return this.vertices("o",t)},isClosed:function(){return this.v.c},pointOnPath:function(t,e){var r=this.v;void 0!==e&&(r=this.getValueAtTime(e,0)),this._segmentsLength||(this._segmentsLength=bez.getSegmentsLength(r));for(var i,s=this._segmentsLength,a=s.lengths,n=s.totalLength*t,o=0,h=a.length,p=0;o<h;){if(p+a[o].addedLength>n){var l=o,m=r.c&&o===h-1?0:o+1,f=(n-p)/a[o].addedLength;i=bez.getPointInSegment(r.v[l],r.v[m],r.o[l],r.i[m],f,a[o]);break}p+=a[o].addedLength,o+=1}return i||(i=r.c?[r.v[0][0],r.v[0][1]]:[r.v[r._length-1][0],r.v[r._length-1][1]]),i},vectorOnPath:function(t,e,r){t=1==t?this.v.c?0:.999:t;var i=this.pointOnPath(t,e),s=this.pointOnPath(t+.001,e),a=s[0]-i[0],n=s[1]-i[1],o=Math.sqrt(Math.pow(a,2)+Math.pow(n,2));return"tangent"===r?[a/o,n/o]:[-n/o,a/o]},tangentOnPath:function(t,e){return this.vectorOnPath(t,e,"tangent")},normalOnPath:function(t,e){return this.vectorOnPath(t,e,"normal")},setGroupProperty:expressionHelpers.setGroupProperty,getValueAtTime:expressionHelpers.getStaticValueAtTime},extendPrototype([r],t),extendPrototype([r],e),e.prototype.getValueAtTime=function(t){return this._cachingAtTime||(this._cachingAtTime={shapeValue:shape_pool.clone(this.pv),lastIndex:0,lastTime:initialDefaultFrame}),t*=this.elem.globalData.frameRate,(t-=this.offsetTime)!==this._cachingAtTime.lastTime&&(this._cachingAtTime.lastIndex=this._cachingAtTime.lastTime<t?this._caching.lastIndex:0,this._cachingAtTime.lastTime=t,this.interpolateShape(t,this._cachingAtTime.shapeValue,this._cachingAtTime)),this._cachingAtTime.shapeValue},e.prototype.initiateExpression=ExpressionManager.initiateExpression;var n=ShapePropertyFactory.getShapeProp;ShapePropertyFactory.getShapeProp=function(t,e,r,i,s){var a=n(t,e,r,i,s);return a.propertyIndex=e.ix,a.lock=!1,3===r?expressionHelpers.searchExpressions(t,e.pt,a):4===r&&expressionHelpers.searchExpressions(t,e.ks,a),a.k&&t.addDynamicProperty(a),a}}(),TextProperty.prototype.getExpressionValue=function(t,e){var r=this.calculateExpression(e);if(t.t===r)return t;var i={};return this.copyData(i,t),i.t=r.toString(),i.__complete=!1,i},TextProperty.prototype.searchProperty=function(){var t=this.searchKeyframes(),e=this.searchExpressions();return this.kf=t||e,this.kf},TextProperty.prototype.searchExpressions=function(){if(this.data.d.x)return this.calculateExpression=ExpressionManager.initiateExpression.bind(this)(this.elem,this.data.d,this),this.addEffect(this.getExpressionValue.bind(this)),!0};var ShapeExpressionInterface=function(t,e,r){var i;function s(t){if("number"==typeof t)return i[t-1];for(var e=0,r=i.length;e<r;){if(i[e]._name===t)return i[e];e+=1}}return s.propertyGroup=r,i=DT(t,e,s),s.numProperties=i.length,s};function DT(t,e,r){var i,s=[],a=t?t.length:0;for(i=0;i<a;i+=1)"gr"==t[i].ty?s.push(FT(t[i],e[i],r)):"fl"==t[i].ty?s.push(GT(t[i],e[i],r)):"st"==t[i].ty?s.push(HT(t[i],e[i],r)):"tm"==t[i].ty?s.push(IT(t[i],e[i],r)):"tr"==t[i].ty||("el"==t[i].ty?s.push(KT(t[i],e[i],r)):"sr"==t[i].ty?s.push(LT(t[i],e[i],r)):"sh"==t[i].ty?s.push(PT(t[i],e[i],r)):"rc"==t[i].ty?s.push(MT(t[i],e[i],r)):"rd"==t[i].ty?s.push(NT(t[i],e[i],r)):"rp"==t[i].ty&&s.push(OT(t[i],e[i],r)));return s}function FT(t,e,r){var i=function(t){switch(t){case"ADBE Vectors Group":case"Contents":case 2:return i.content;default:return i.transform}};i.propertyGroup=function(t){return 1===t?i:r(t-1)};var s=function(t,e,r){function i(t){for(var e=0,r=s.length;e<r;){if(s[e]._name===t||s[e].mn===t||s[e].propertyIndex===t||s[e].ix===t||s[e].ind===t)return s[e];e+=1}if("number"==typeof t)return s[t-1]}var s;return i.propertyGroup=function(t){return 1===t?i:r(t-1)},s=DT(t.it,e.it,i.propertyGroup),i.numProperties=s.length,i.propertyIndex=t.cix,i._name=t.nm,i}(t,e,i.propertyGroup),a=function(e,t,r){function i(t){return 1==t?s:r(--t)}t.transform.mProps.o.setGroupProperty(i),t.transform.mProps.p.setGroupProperty(i),t.transform.mProps.a.setGroupProperty(i),t.transform.mProps.s.setGroupProperty(i),t.transform.mProps.r.setGroupProperty(i),t.transform.mProps.sk&&(t.transform.mProps.sk.setGroupProperty(i),t.transform.mProps.sa.setGroupProperty(i));function s(t){return e.a.ix===t||"Anchor Point"===t?s.anchorPoint:e.o.ix===t||"Opacity"===t?s.opacity:e.p.ix===t||"Position"===t?s.position:e.r.ix===t||"Rotation"===t||"ADBE Vector Rotation"===t?s.rotation:e.s.ix===t||"Scale"===t?s.scale:e.sk&&e.sk.ix===t||"Skew"===t?s.skew:e.sa&&e.sa.ix===t||"Skew Axis"===t?s.skewAxis:void 0}return t.transform.op.setGroupProperty(i),Object.defineProperties(s,{opacity:{get:ExpressionPropertyInterface(t.transform.mProps.o)},position:{get:ExpressionPropertyInterface(t.transform.mProps.p)},anchorPoint:{get:ExpressionPropertyInterface(t.transform.mProps.a)},scale:{get:ExpressionPropertyInterface(t.transform.mProps.s)},rotation:{get:ExpressionPropertyInterface(t.transform.mProps.r)},skew:{get:ExpressionPropertyInterface(t.transform.mProps.sk)},skewAxis:{get:ExpressionPropertyInterface(t.transform.mProps.sa)},_name:{value:e.nm}}),s.ty="tr",s.mn=e.mn,s.propertyGroup=r,s}(t.it[t.it.length-1],e.it[e.it.length-1],i.propertyGroup);return i.content=s,i.transform=a,Object.defineProperty(i,"_name",{get:function(){return t.nm}}),i.numProperties=t.np,i.propertyIndex=t.ix,i.nm=t.nm,i.mn=t.mn,i}function GT(t,e,r){function i(t){return"Color"===t||"color"===t?i.color:"Opacity"===t||"opacity"===t?i.opacity:void 0}return Object.defineProperties(i,{color:{get:ExpressionPropertyInterface(e.c)},opacity:{get:ExpressionPropertyInterface(e.o)},_name:{value:t.nm},mn:{value:t.mn}}),e.c.setGroupProperty(r),e.o.setGroupProperty(r),i}function HT(t,e,r){function i(t){return 1===t?ob:r(t-1)}function s(t){return 1===t?h:i(t-1)}var a,n,o=t.d?t.d.length:0,h={};for(a=0;a<o;a+=1)n=a,Object.defineProperty(h,t.d[n].nm,{get:ExpressionPropertyInterface(e.d.dataProps[n].p)}),e.d.dataProps[a].p.setGroupProperty(s);function p(t){return"Color"===t||"color"===t?p.color:"Opacity"===t||"opacity"===t?p.opacity:"Stroke Width"===t||"stroke width"===t?p.strokeWidth:void 0}return Object.defineProperties(p,{color:{get:ExpressionPropertyInterface(e.c)},opacity:{get:ExpressionPropertyInterface(e.o)},strokeWidth:{get:ExpressionPropertyInterface(e.w)},dash:{get:function(){return h}},_name:{value:t.nm},mn:{value:t.mn}}),e.c.setGroupProperty(i),e.o.setGroupProperty(i),e.w.setGroupProperty(i),p}function IT(e,t,r){function i(t){return 1==t?s:r(--t)}function s(t){return t===e.e.ix||"End"===t||"end"===t?s.end:t===e.s.ix?s.start:t===e.o.ix?s.offset:void 0}return s.propertyIndex=e.ix,t.s.setGroupProperty(i),t.e.setGroupProperty(i),t.o.setGroupProperty(i),s.propertyIndex=e.ix,s.propertyGroup=r,Object.defineProperties(s,{start:{get:ExpressionPropertyInterface(t.s)},end:{get:ExpressionPropertyInterface(t.e)},offset:{get:ExpressionPropertyInterface(t.o)},_name:{value:e.nm}}),s.mn=e.mn,s}function KT(e,t,r){function i(t){return 1==t?a:r(--t)}a.propertyIndex=e.ix;var s="tm"===t.sh.ty?t.sh.prop:t.sh;function a(t){return e.p.ix===t?a.position:e.s.ix===t?a.size:void 0}return s.s.setGroupProperty(i),s.p.setGroupProperty(i),Object.defineProperties(a,{size:{get:ExpressionPropertyInterface(s.s)},position:{get:ExpressionPropertyInterface(s.p)},_name:{value:e.nm}}),a.mn=e.mn,a}function LT(e,t,r){function i(t){return 1==t?a:r(--t)}var s="tm"===t.sh.ty?t.sh.prop:t.sh;function a(t){return e.p.ix===t?a.position:e.r.ix===t?a.rotation:e.pt.ix===t?a.points:e.or.ix===t||"ADBE Vector Star Outer Radius"===t?a.outerRadius:e.os.ix===t?a.outerRoundness:!e.ir||e.ir.ix!==t&&"ADBE Vector Star Inner Radius"!==t?e.is&&e.is.ix===t?a.innerRoundness:void 0:a.innerRadius}return a.propertyIndex=e.ix,s.or.setGroupProperty(i),s.os.setGroupProperty(i),s.pt.setGroupProperty(i),s.p.setGroupProperty(i),s.r.setGroupProperty(i),e.ir&&(s.ir.setGroupProperty(i),s.is.setGroupProperty(i)),Object.defineProperties(a,{position:{get:ExpressionPropertyInterface(s.p)},rotation:{get:ExpressionPropertyInterface(s.r)},points:{get:ExpressionPropertyInterface(s.pt)},outerRadius:{get:ExpressionPropertyInterface(s.or)},outerRoundness:{get:ExpressionPropertyInterface(s.os)},innerRadius:{get:ExpressionPropertyInterface(s.ir)},innerRoundness:{get:ExpressionPropertyInterface(s.is)},_name:{value:e.nm}}),a.mn=e.mn,a}function MT(e,t,r){function i(t){return 1==t?a:r(--t)}var s="tm"===t.sh.ty?t.sh.prop:t.sh;function a(t){return e.p.ix===t?a.position:e.r.ix===t?a.roundness:e.s.ix===t||"Size"===t||"ADBE Vector Rect Size"===t?a.size:void 0}return a.propertyIndex=e.ix,s.p.setGroupProperty(i),s.s.setGroupProperty(i),s.r.setGroupProperty(i),Object.defineProperties(a,{position:{get:ExpressionPropertyInterface(s.p)},roundness:{get:ExpressionPropertyInterface(s.r)},size:{get:ExpressionPropertyInterface(s.s)},_name:{value:e.nm}}),a.mn=e.mn,a}function NT(e,t,r){var i=t;function s(t){if(e.r.ix===t||"Round Corners 1"===t)return s.radius}return s.propertyIndex=e.ix,i.rd.setGroupProperty(function(t){return 1==t?s:r(--t)}),Object.defineProperties(s,{radius:{get:ExpressionPropertyInterface(i.rd)},_name:{value:e.nm}}),s.mn=e.mn,s}function OT(e,t,r){function i(t){return 1==t?a:r(--t)}var s=t;function a(t){return e.c.ix===t||"Copies"===t?a.copies:e.o.ix===t||"Offset"===t?a.offset:void 0}return a.propertyIndex=e.ix,s.c.setGroupProperty(i),s.o.setGroupProperty(i),Object.defineProperties(a,{copies:{get:ExpressionPropertyInterface(s.c)},offset:{get:ExpressionPropertyInterface(s.o)},_name:{value:e.nm}}),a.mn=e.mn,a}function PT(t,e,r){var i=e.sh;function s(t){if("Shape"===t||"shape"===t||"Path"===t||"path"===t||"ADBE Vector Shape"===t||2===t)return s.path}return i.setGroupProperty(function(t){return 1==t?s:r(--t)}),Object.defineProperties(s,{path:{get:function(){return i.k&&i.getValue(),i}},shape:{get:function(){return i.k&&i.getValue(),i}},_name:{value:t.nm},ix:{value:t.ix},mn:{value:t.mn}}),s}var TextExpressionInterface=function(e){var r;function t(){}return Object.defineProperty(t,"sourceText",{get:function(){e.textProperty.getValue();var t=e.textProperty.currentData.t;return void 0!==t&&(e.textProperty.currentData.t=void 0,(r=new String(t)).value=t||new String(t)),r}}),t},LayerExpressionInterface=function(e){var r;function i(t){switch(t){case"ADBE Root Vectors Group":case"Contents":case 2:return i.shapeInterface;case 1:case 6:case"Transform":case"transform":case"ADBE Transform Group":return r;case 4:case"ADBE Effect Parade":case"effects":case"Effects":return i.effect}}i.toWorld=_V,i.fromWorld=aW,i.toComp=_V,i.fromComp=bW,i.sampleImage=cW,i.sourceRectAtTime=e.sourceRectAtTime.bind(e);var t=getDescriptor(r=TransformExpressionInterface((i._elem=e).finalTransform.mProp),"anchorPoint");return Object.defineProperties(i,{hasParent:{get:function(){return e.hierarchy.length}},parent:{get:function(){return e.hierarchy[0].layerInterface}},rotation:getDescriptor(r,"rotation"),scale:getDescriptor(r,"scale"),position:getDescriptor(r,"position"),opacity:getDescriptor(r,"opacity"),anchorPoint:t,anchor_point:t,transform:{get:function(){return r}},active:{get:function(){return e.isInRange}}}),i.startTime=e.data.st,i.index=e.data.ind,i.source=e.data.refId,i.height=0===e.data.ty?e.data.h:100,i.width=0===e.data.ty?e.data.w:100,i.inPoint=e.data.ip/e.comp.globalData.frameRate,i.outPoint=e.data.op/e.comp.globalData.frameRate,i._name=e.data.nm,i.registerMaskInterface=function(t){i.mask=new MaskManagerInterface(t,e)},i.registerEffectsInterface=function(t){i.effect=t},i};function _V(t,e){var r=new Matrix;if(r.reset(),this._elem.finalTransform.mProp.applyToMatrix(r),this._elem.hierarchy&&this._elem.hierarchy.length){var i,s=this._elem.hierarchy.length;for(i=0;i<s;i+=1)this._elem.hierarchy[i].finalTransform.mProp.applyToMatrix(r);return r.applyToPointArray(t[0],t[1],t[2]||0)}return r.applyToPointArray(t[0],t[1],t[2]||0)}function aW(t,e){var r=new Matrix;if(r.reset(),this._elem.finalTransform.mProp.applyToMatrix(r),this._elem.hierarchy&&this._elem.hierarchy.length){var i,s=this._elem.hierarchy.length;for(i=0;i<s;i+=1)this._elem.hierarchy[i].finalTransform.mProp.applyToMatrix(r);return r.inversePoint(t)}return r.inversePoint(t)}function bW(t){var e=new Matrix;if(e.reset(),this._elem.finalTransform.mProp.applyToMatrix(e),this._elem.hierarchy&&this._elem.hierarchy.length){var r,i=this._elem.hierarchy.length;for(r=0;r<i;r+=1)this._elem.hierarchy[r].finalTransform.mProp.applyToMatrix(e);return e.inversePoint(t)}return e.inversePoint(t)}function cW(){return[1,1,1,1]}var CompExpressionInterface=function(i){function t(t){for(var e=0,r=i.layers.length;e<r;){if(i.layers[e].nm===t||i.layers[e].ind===t)return i.elements[e].layerInterface;e+=1}return null}return Object.defineProperty(t,"_name",{value:i.data.nm}),(t.layer=t).pixelAspect=1,t.height=i.data.h||i.globalData.compSize.h,t.width=i.data.w||i.globalData.compSize.w,t.pixelAspect=1,t.frameDuration=1/i.globalData.frameRate,t.displayStartTime=0,t.numLayers=i.layers.length,t},TransformExpressionInterface=function(t){function e(t){switch(t){case"scale":case"Scale":case"ADBE Scale":case 6:return e.scale;case"rotation":case"Rotation":case"ADBE Rotation":case"ADBE Rotate Z":case 10:return e.rotation;case"ADBE Rotate X":return e.xRotation;case"ADBE Rotate Y":return e.yRotation;case"position":case"Position":case"ADBE Position":case 2:return e.position;case"ADBE Position_0":return e.xPosition;case"ADBE Position_1":return e.yPosition;case"ADBE Position_2":return e.zPosition;case"anchorPoint":case"AnchorPoint":case"Anchor Point":case"ADBE AnchorPoint":case 1:return e.anchorPoint;case"opacity":case"Opacity":case 11:return e.opacity}}if(Object.defineProperty(e,"rotation",{get:ExpressionPropertyInterface(t.r||t.rz)}),Object.defineProperty(e,"zRotation",{get:ExpressionPropertyInterface(t.rz||t.r)}),Object.defineProperty(e,"xRotation",{get:ExpressionPropertyInterface(t.rx)}),Object.defineProperty(e,"yRotation",{get:ExpressionPropertyInterface(t.ry)}),Object.defineProperty(e,"scale",{get:ExpressionPropertyInterface(t.s)}),t.p)var r=ExpressionPropertyInterface(t.p);return Object.defineProperty(e,"position",{get:function(){return t.p?r():[t.px.v,t.py.v,t.pz?t.pz.v:0]}}),Object.defineProperty(e,"xPosition",{get:ExpressionPropertyInterface(t.px)}),Object.defineProperty(e,"yPosition",{get:ExpressionPropertyInterface(t.py)}),Object.defineProperty(e,"zPosition",{get:ExpressionPropertyInterface(t.pz)}),Object.defineProperty(e,"anchorPoint",{get:ExpressionPropertyInterface(t.a)}),Object.defineProperty(e,"opacity",{get:ExpressionPropertyInterface(t.o)}),Object.defineProperty(e,"skew",{get:ExpressionPropertyInterface(t.sk)}),Object.defineProperty(e,"skewAxis",{get:ExpressionPropertyInterface(t.sa)}),Object.defineProperty(e,"orientation",{get:ExpressionPropertyInterface(t.or)}),e},ProjectInterface=function(){function t(t){for(var e=0,r=this.compositions.length;e<r;){if(this.compositions[e].data&&this.compositions[e].data.nm===t)return this.compositions[e].prepareFrame&&this.compositions[e].data.xt&&this.compositions[e].prepareFrame(this.currentFrame),this.compositions[e].compInterface;e+=1}}return t.compositions=[],t.currentFrame=0,t.registerComposition=LW,t};function LW(t){this.compositions.push(t)}var EffectsExpressionInterface={createEffectsInterface:function(s,t){if(s.effectsManager){var e,a=[],r=s.data.ef,i=s.effectsManager.effectElements.length;for(e=0;e<i;e+=1)a.push(TW(r[e],s.effectsManager.effectElements[e],t,s));return function(t){for(var e=s.data.ef||[],r=0,i=e.length;r<i;){if(t===e[r].nm||t===e[r].mn||t===e[r].ix)return a[r];r+=1}}}}};function TW(s,t,e,r){var i,a=[],n=s.ef.length;for(i=0;i<n;i+=1)5===s.ef[i].ty?a.push(TW(s.ef[i],t.effectElements[i],t.effectElements[i].propertyGroup,r)):a.push(UW(t.effectElements[i],s.ef[i].ty,r,o));function o(t){return 1===t?h:e(t-1)}var h=function(t){for(var e=s.ef,r=0,i=e.length;r<i;){if(t===e[r].nm||t===e[r].mn||t===e[r].ix)return 5===e[r].ty?a[r]:a[r]();r+=1}return a[0]()};return h.propertyGroup=o,"ADBE Color Control"===s.mn&&Object.defineProperty(h,"color",{get:function(){return a[0]()}}),Object.defineProperty(h,"numProperties",{get:function(){return s.np}}),h.active=h.enabled=0!==s.en,h}function UW(t,e,r,i){var s=ExpressionPropertyInterface(t.p);return t.p.setGroupProperty&&t.p.setGroupProperty(i),function(){return 10===e?r.comp.compInterface(t.p.v):s()}}var MaskManagerInterface=function(){function a(t,e){this._mask=t,this._data=e}Object.defineProperty(a.prototype,"maskPath",{get:function(){return this._mask.prop.k&&this._mask.prop.getValue(),this._mask.prop}});return function(e,t){var r,i=createSizedArray(e.viewData.length),s=e.viewData.length;for(r=0;r<s;r+=1)i[r]=new a(e.viewData[r],e.masksProperties[r]);return function(t){for(r=0;r<s;){if(e.masksProperties[r].nm===t)return i[r];r+=1}}}}(),ExpressionPropertyInterface=(KX={pv:0,v:0,mult:1},LX={pv:[0,0,0],v:[0,0,0],mult:1},function(t){return t?"unidimensional"===t.propType?function(t){t&&"pv"in t||(t=KX);var e=1/t.mult,r=t.pv*e,i=new Number(r);return i.value=r,MX(i,t,"unidimensional"),function(){return t.k&&t.getValue(),r=t.v*e,i.value!==r&&((i=new Number(r)).value=r,MX(i,t,"unidimensional")),i}}(t):function(e){e&&"pv"in e||(e=LX);var r=1/e.mult,i=e.pv.length,s=createTypedArray("float32",i),a=createTypedArray("float32",i);return s.value=a,MX(s,e,"multidimensional"),function(){e.k&&e.getValue();for(var t=0;t<i;t+=1)s[t]=a[t]=e.v[t]*r;return s}}(t):PX}),KX,LX,fY,gY;function MX(i,s,a){Object.defineProperty(i,"velocity",{get:function(){return s.getVelocityAtTime(s.comp.currentFrame)}}),i.numKeys=s.keyframes?s.keyframes.length:0,i.key=function(t){if(i.numKeys){var e="";e="s"in s.keyframes[t-1]?s.keyframes[t-1].s:"e"in s.keyframes[t-2]?s.keyframes[t-2].e:s.keyframes[t-2].s;var r="unidimensional"===a?new Number(e):Object.assign({},e);return r.time=s.keyframes[t-1].t/s.elem.comp.globalData.frameRate,r}return 0},i.valueAtTime=s.getValueAtTime,i.speedAtTime=s.getSpeedAtTime,i.velocityAtTime=s.getVelocityAtTime,i.propertyGroup=s.propertyGroup}function PX(){return KX}function hY(t,e){return this.textIndex=t+1,this.textTotal=e,this.v=this.getValue()*this.mult,this.v}function SliderEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function AngleEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function ColorEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,1,0,r)}function PointEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,1,0,r)}function LayerIndexEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function MaskIndexEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function CheckboxEffect(t,e,r){this.p=PropertyFactory.getProp(e,t.v,0,0,r)}function NoValueEffect(){this.p={}}function EffectsManager(t,e){var r=t.ef||[];this.effectElements=[];var i,s,a=r.length;for(i=0;i<a;i++)s=new GroupEffect(r[i],e),this.effectElements.push(s)}function GroupEffect(t,e){this.init(t,e)}fY=function(t,e){this.pv=1,this.comp=t.comp,this.elem=t,this.mult=.01,this.propType="textSelector",this.textTotal=e.totalChars,this.selectorValue=100,this.lastValue=[1,1,1],this.k=!0,this.x=!0,this.getValue=ExpressionManager.initiateExpression.bind(this)(t,e,this),this.getMult=hY,this.getVelocityAtTime=expressionHelpers.getVelocityAtTime,this.kf?this.getValueAtTime=expressionHelpers.getValueAtTime.bind(this):this.getValueAtTime=expressionHelpers.getStaticValueAtTime.bind(this),this.setGroupProperty=expressionHelpers.setGroupProperty},gY=TextSelectorProp.getTextSelectorProp,TextSelectorProp.getTextSelectorProp=function(t,e,r){return 1===e.t?new fY(t,e,r):gY(t,e,r)},extendPrototype([DynamicPropertyContainer],GroupEffect),GroupEffect.prototype.getValue=GroupEffect.prototype.iterateDynamicProperties,GroupEffect.prototype.init=function(t,e){this.data=t,this.effectElements=[],this.initDynamicPropertyContainer(e);var r,i,s=this.data.ef.length,a=this.data.ef;for(r=0;r<s;r+=1){switch(i=null,a[r].ty){case 0:i=new SliderEffect(a[r],e,this);break;case 1:i=new AngleEffect(a[r],e,this);break;case 2:i=new ColorEffect(a[r],e,this);break;case 3:i=new PointEffect(a[r],e,this);break;case 4:case 7:i=new CheckboxEffect(a[r],e,this);break;case 10:i=new LayerIndexEffect(a[r],e,this);break;case 11:i=new MaskIndexEffect(a[r],e,this);break;case 5:i=new EffectsManager(a[r],e,this);break;default:i=new NoValueEffect(a[r],e,this)}i&&this.effectElements.push(i)}};var lottiejs={},_isFrozen=!1;function loadAnimation(t){return animationManager.loadAnimation(t)}function setQuality(t){if("string"==typeof t)switch(t){case"high":defaultCurveSegments=200;break;case"medium":defaultCurveSegments=50;break;case"low":defaultCurveSegments=10}else!isNaN(t)&&1<t&&(defaultCurveSegments=t);roundValues(!(50<=defaultCurveSegments))}lottiejs.play=animationManager.play,lottiejs.pause=animationManager.pause,lottiejs.togglePause=animationManager.togglePause,lottiejs.setSpeed=animationManager.setSpeed,lottiejs.setDirection=animationManager.setDirection,lottiejs.stop=animationManager.stop,lottiejs.registerAnimation=animationManager.registerAnimation,lottiejs.loadAnimation=loadAnimation,lottiejs.resize=animationManager.resize,lottiejs.goToAndStop=animationManager.goToAndStop,lottiejs.destroy=animationManager.destroy,lottiejs.setQuality=setQuality,lottiejs.freeze=animationManager.freeze,lottiejs.unfreeze=animationManager.unfreeze,lottiejs.getRegisteredAnimations=animationManager.getRegisteredAnimations,lottiejs.version="5.5.2";var renderer="";return lottiejs}({}),currentAnimation=null,events={INITIALIZED:"initialized",RESIZED:"resized",PLAYING:"playing"};getCurrentCanvasSize=function(){var t=currentAnimation.renderer.canvasContext.canvas;return{height:t.height,width:t.width}},sendResizeEvent=function(){currentAnimation.renderer.canvasContext.canvas;postMessage({name:events.RESIZED,size:getCurrentCanvasSize()})},sendPlayEvent=function(){postMessage({name:events.PLAYING})},sendInitializedEvent=function(){currentAnimation.renderer.canvasContext.canvas;postMessage({name:events.INITIALIZED,success:currentAnimation.isLoaded})},onmessage=function(t){if(t&&t.data){var e=null;if(currentAnimation)e=currentAnimation.renderer.canvasContext.canvas;else{if(!t.data.canvas)return;e=t.data.canvas}if(t.data.drawSize&&0<t.data.drawSize.height&&0<t.data.drawSize.width&&(e.height=t.data.drawSize.height,e.width=t.data.drawSize.width,currentAnimation&&(currentAnimation.resize(),sendResizeEvent())),!currentAnimation){if(!t.data.animationData||!t.data.params)return;var r=t.data.params,i=e.getContext("2d");currentAnimation=lottiejs.loadAnimation({renderer:"canvas",loop:r.loop,autoplay:r.autoplay,animationData:t.data.animationData,rendererSettings:{context:i,scaleMode:"noScale",clearCanvas:!0}}),sendInitializedEvent(),r.autoplay&¤tAnimation.play(),currentAnimation.isLoaded&&!currentAnimation.isPaused&&sendPlayEvent()}}}; \ No newline at end of file
diff --git a/third_party/pylint/README.chromium b/third_party/pylint/README.chromium index 88e1f1a..86495e6c 100644 --- a/third_party/pylint/README.chromium +++ b/third_party/pylint/README.chromium
@@ -8,6 +8,14 @@ Description: This directory contains the pylint module. +This code is not included in the final build. It is only used to run linting +checks on code (usually at presubmit time). + +This is only used by code inside the src tree that needs to import the pylint +module directly. If you're using `pylint` to check your code (e.g. via the +PRESUBMIT.cfg file), then this copy is *not* used. That pylint comes from the +depot_tools repo instead. + Local Modifications: - applied upstream fix https://bitbucket.org/logilab/pylint/commits/5df347467ee0 - applied fix to work around bad interaction between sys.path manipulation in
diff --git a/tools/grit/grit/clique.py b/tools/grit/grit/clique.py index cd5b44c..bb19f1b 100644 --- a/tools/grit/grit/clique.py +++ b/tools/grit/grit/clique.py
@@ -9,7 +9,8 @@ from __future__ import print_function import re -import types + +import six from grit import constants from grit import exception @@ -271,7 +272,7 @@ ''' contents = translation.GetContent() for ix in range(len(contents)): - if (isinstance(contents[ix], types.StringTypes)): + if (isinstance(contents[ix], six.string_types)): contents[ix] = self.ModifyTextPart(lang, contents[ix])
diff --git a/tools/grit/grit/format/c_format.py b/tools/grit/grit/format/c_format.py index e944b7f..2a6ee12 100644 --- a/tools/grit/grit/format/c_format.py +++ b/tools/grit/grit/format/c_format.py
@@ -9,7 +9,8 @@ import os import re -import types + +import six from grit import util @@ -35,7 +36,7 @@ def Format(root, lang='en', output_dir='.'): """Outputs a C switch statement representing the string table.""" from grit.node import message - assert isinstance(lang, types.StringTypes) + assert isinstance(lang, six.string_types) yield _FormatHeader(root, output_dir)
diff --git a/tools/grit/grit/format/rc.py b/tools/grit/grit/format/rc.py index ac65f34..ed32bb8 100644 --- a/tools/grit/grit/format/rc.py +++ b/tools/grit/grit/format/rc.py
@@ -8,10 +8,11 @@ from __future__ import print_function import os -import types import re from functools import partial +import six + from grit import util from grit.node import misc @@ -315,7 +316,7 @@ def _FormatHeader(root, lang, output_dir): '''Returns the required preamble for RC files.''' - assert isinstance(lang, types.StringTypes) + assert isinstance(lang, six.string_types) assert isinstance(root, misc.GritNode) # Find the location of the resource header file, so that we can include # it. @@ -373,7 +374,7 @@ def _FormatSection(item, lang, output_dir): '''Writes out an .rc file section.''' - assert isinstance(lang, types.StringTypes) + assert isinstance(lang, six.string_types) from grit.node import structure assert isinstance(item, structure.StructureNode) @@ -402,7 +403,7 @@ StructureNode) process_html: False/True (ignored unless item is a StructureNode) ''' - assert isinstance(lang, types.StringTypes) + assert isinstance(lang, six.string_types) from grit.node import structure from grit.node import include assert isinstance(item, (structure.StructureNode, include.IncludeNode))
diff --git a/tools/grit/grit/gather/interface.py b/tools/grit/grit/gather/interface.py index 6149167..15d64f932 100644 --- a/tools/grit/grit/gather/interface.py +++ b/tools/grit/grit/gather/interface.py
@@ -8,7 +8,8 @@ from __future__ import print_function import os.path -import types + +import six from grit import clique from grit import util @@ -161,7 +162,7 @@ '''A convenience function for subclasses that loads the contents of the input file. ''' - if isinstance(self.rc_file, types.StringTypes): + if isinstance(self.rc_file, six.string_types): path = self.GetInputPath() # Hack: some unit tests supply an absolute path and no root node. if not os.path.isabs(path):
diff --git a/tools/grit/grit/gather/skeleton_gatherer.py b/tools/grit/grit/gather/skeleton_gatherer.py index e0f3074..b11862b 100644 --- a/tools/grit/grit/gather/skeleton_gatherer.py +++ b/tools/grit/grit/gather/skeleton_gatherer.py
@@ -8,7 +8,7 @@ from __future__ import print_function -import types +import six from grit.gather import interface from grit import clique @@ -78,17 +78,17 @@ out = [] for ix in range(len(self.skeleton_)): - if isinstance(self.skeleton_[ix], types.StringTypes): + if isinstance(self.skeleton_[ix], six.string_types): if skeleton_gatherer: # Make sure the skeleton is like the original - assert(isinstance(skeleton_gatherer.skeleton_[ix], types.StringTypes)) + assert(isinstance(skeleton_gatherer.skeleton_[ix], six.string_types)) out.append(skeleton_gatherer.skeleton_[ix]) else: out.append(self.skeleton_[ix]) else: if skeleton_gatherer: # Make sure the skeleton is like the original assert(not isinstance(skeleton_gatherer.skeleton_[ix], - types.StringTypes)) + six.string_types)) msg = self.skeleton_[ix].MessageForLanguage(lang, pseudo_if_not_available, fallback_to_english)
diff --git a/tools/grit/grit/gather/tr_html.py b/tools/grit/grit/gather/tr_html.py index ba59ff65..5a60696 100644 --- a/tools/grit/grit/gather/tr_html.py +++ b/tools/grit/grit/gather/tr_html.py
@@ -52,7 +52,8 @@ from __future__ import print_function import re -import types + +import six from grit import clique from grit import exception @@ -573,7 +574,7 @@ current += m.end() continue - if len(parts) and isinstance(parts[-1], types.StringTypes): + if len(parts) and isinstance(parts[-1], six.string_types): parts[-1] += html[current] else: parts.append(html[current]) @@ -582,7 +583,7 @@ msg_text = '' placeholders = [] for part in parts: - if isinstance(part, types.TupleType): + if isinstance(part, tuple): final_name = part[0]() original = part[1] msg_text += final_name @@ -594,7 +595,7 @@ description=description) content = msg.GetContent() for ix in range(len(content)): - if isinstance(content[ix], types.StringTypes): + if isinstance(content[ix], six.string_types): content[ix] = util.UnescapeHtml(content[ix], replace_nbsp=False) return msg @@ -658,7 +659,7 @@ out = [] for item in self.skeleton_: - if isinstance(item, types.StringTypes): + if isinstance(item, six.string_types): out.append(item) else: msg = item.MessageForLanguage(lang, @@ -718,8 +719,8 @@ if isinstance(self.skeleton_[ix], clique.MessageClique): msg = self.skeleton_[ix].GetMessage() for item in msg.GetContent(): - if (isinstance(item, types.StringTypes) and _NON_WHITESPACE.search(item) - and item != ' '): + if (isinstance(item, six.string_types) + and _NON_WHITESPACE.search(item) and item != ' '): got_text = True break if not got_text:
diff --git a/tools/grit/grit/gather/tr_html_unittest.py b/tools/grit/grit/gather/tr_html_unittest.py index 5d82276..1194853 100755 --- a/tools/grit/grit/gather/tr_html_unittest.py +++ b/tools/grit/grit/gather/tr_html_unittest.py
@@ -12,9 +12,9 @@ if __name__ == '__main__': sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) -import types import unittest +import six from six import StringIO from grit.gather import tr_html @@ -369,7 +369,7 @@ # For manual results inspection only... list = [] for item in html.skeleton_: - if isinstance(item, types.StringTypes): + if isinstance(item, six.string_types): list.append(item) else: list.append(item.GetMessage().GetPresentableContent())
diff --git a/tools/grit/grit/grd_reader.py b/tools/grit/grit/grd_reader.py index 2535ce94..514008a3 100755 --- a/tools/grit/grit/grd_reader.py +++ b/tools/grit/grit/grd_reader.py
@@ -10,10 +10,11 @@ import os.path import sys -import types import xml.sax import xml.sax.handler +import six + from grit import exception from grit import util from grit.node import mapping @@ -187,7 +188,7 @@ grit.exception.Parsing ''' - if isinstance(filename_or_stream, types.StringType): + if isinstance(filename_or_stream, six.string_types): source = filename_or_stream if dir is None: dir = util.dirname(filename_or_stream)
diff --git a/tools/grit/grit/node/base.py b/tools/grit/grit/node/base.py index 0519b59..5159c5e 100644 --- a/tools/grit/grit/node/base.py +++ b/tools/grit/grit/node/base.py
@@ -11,9 +11,10 @@ import os import struct import sys -import types from xml.sax import saxutils +import six + from grit import constants from grit import clique from grit import exception @@ -112,7 +113,7 @@ name: u'elementname' parent: grit.node.base.Node or subclass or None ''' - assert isinstance(name, types.StringTypes) + assert isinstance(name, six.string_types) assert not parent or isinstance(parent, Node) self.name = name self.parent = parent @@ -155,7 +156,7 @@ Return: None ''' - assert isinstance(content, types.StringTypes) + assert isinstance(content, six.string_types) if self._ContentType() != self._CONTENT_TYPE_NONE: self.mixed_content.append(content) elif content.strip() != '': @@ -172,8 +173,8 @@ Return: None ''' - assert isinstance(attrib, types.StringTypes) - assert isinstance(value, types.StringTypes) + assert isinstance(attrib, six.string_types) + assert isinstance(value, six.string_types) if self._IsValidAttribute(attrib, value): self.attrs[attrib] = value else: @@ -184,34 +185,34 @@ # TODO(joi) Rewrite this, it's extremely ugly! if len(self.mixed_content): - if isinstance(self.mixed_content[0], types.StringTypes): + if isinstance(self.mixed_content[0], six.string_types): # Remove leading and trailing chunks of pure whitespace. while (len(self.mixed_content) and - isinstance(self.mixed_content[0], types.StringTypes) and + isinstance(self.mixed_content[0], six.string_types) and self.mixed_content[0].strip() == ''): self.mixed_content = self.mixed_content[1:] # Strip leading and trailing whitespace from mixed content chunks # at front and back. if (len(self.mixed_content) and - isinstance(self.mixed_content[0], types.StringTypes)): + isinstance(self.mixed_content[0], six.string_types)): self.mixed_content[0] = self.mixed_content[0].lstrip() # Remove leading and trailing ''' (used to demarcate whitespace) if (len(self.mixed_content) and - isinstance(self.mixed_content[0], types.StringTypes)): + isinstance(self.mixed_content[0], six.string_types)): if self.mixed_content[0].startswith("'''"): self.mixed_content[0] = self.mixed_content[0][3:] if len(self.mixed_content): - if isinstance(self.mixed_content[-1], types.StringTypes): + if isinstance(self.mixed_content[-1], six.string_types): # Same stuff all over again for the tail end. while (len(self.mixed_content) and - isinstance(self.mixed_content[-1], types.StringTypes) and + isinstance(self.mixed_content[-1], six.string_types) and self.mixed_content[-1].strip() == ''): self.mixed_content = self.mixed_content[:-1] if (len(self.mixed_content) and - isinstance(self.mixed_content[-1], types.StringTypes)): + isinstance(self.mixed_content[-1], six.string_types)): self.mixed_content[-1] = self.mixed_content[-1].rstrip() if (len(self.mixed_content) and - isinstance(self.mixed_content[-1], types.StringTypes)): + isinstance(self.mixed_content[-1], six.string_types)): if self.mixed_content[-1].endswith("'''"): self.mixed_content[-1] = self.mixed_content[-1][:-3] @@ -244,7 +245,7 @@ '''Returns all CDATA of this element, concatenated into a single string. Note that this ignores any elements embedded in CDATA.''' return ''.join([c for c in self.mixed_content - if isinstance(c, types.StringTypes)]) + if isinstance(c, six.string_types)]) def __unicode__(self): '''Returns this node and all nodes below it as an XML document in a Unicode @@ -259,7 +260,7 @@ children and CDATA are layed out in a way that preserves internal whitespace. ''' - assert isinstance(indent, types.StringTypes) + assert isinstance(indent, six.string_types) content_one_line = (one_line or self._ContentType() == self._CONTENT_TYPE_MIXED) @@ -293,7 +294,7 @@ def ContentsAsXml(self, indent, one_line): '''Returns the contents of this node (CDATA and child elements) in XML format. If 'one_line' is true, the content will be laid out on one line.''' - assert isinstance(indent, types.StringTypes) + assert isinstance(indent, six.string_types) # Build the contents of the element. inside_parts = [] @@ -319,7 +320,7 @@ # If the last item is a string (not a node) and ends with whitespace, # we need to add the ''' delimiter. - if (isinstance(last_item, types.StringTypes) and + if (isinstance(last_item, six.string_types) and last_item.rstrip() != last_item): inside_parts[-1] = inside_parts[-1] + u"'''"
diff --git a/tools/grit/grit/node/message.py b/tools/grit/grit/node/message.py index a21c38c..cc8d217 100644 --- a/tools/grit/grit/node/message.py +++ b/tools/grit/grit/node/message.py
@@ -8,7 +8,8 @@ from __future__ import print_function import re -import types + +import six from grit.node import base @@ -166,7 +167,7 @@ placeholders = [] for item in self.mixed_content: - if isinstance(item, types.StringTypes): + if isinstance(item, six.string_types): # Not a <ph> element: fail if any <ph> formatters are detected. if _FORMATTERS.search(item): print(_BAD_PLACEHOLDER_MSG % (item, self.source)) @@ -303,7 +304,7 @@ items = message.GetContent() for ix, item in enumerate(items): - if isinstance(item, types.StringTypes): + if isinstance(item, six.string_types): # Ensure whitespace at front and back of message is correctly handled. if ix == 0: item = "'''" + item
diff --git a/tools/grit/grit/tclib.py b/tools/grit/grit/tclib.py index 23bef75..3403e87a2 100644 --- a/tools/grit/grit/tclib.py +++ b/tools/grit/grit/tclib.py
@@ -8,7 +8,8 @@ from __future__ import print_function import re -import types + +import six from grit import exception from grit import lazy_re @@ -83,7 +84,7 @@ ''' bits = [] for item in self.parts: - if isinstance(item, types.StringTypes): + if isinstance(item, six.string_types): bits.append(escaping_function(item)) else: bits.append(item.GetOriginal()) @@ -112,7 +113,7 @@ self.dirty = True def AppendText(self, text): - assert isinstance(text, types.StringTypes) + assert isinstance(text, six.string_types) assert text != '' self.parts.append(text)
diff --git a/tools/grit/grit/tclib_unittest.py b/tools/grit/grit/tclib_unittest.py index 94673a2..8570c8f 100755 --- a/tools/grit/grit/tclib_unittest.py +++ b/tools/grit/grit/tclib_unittest.py
@@ -12,9 +12,10 @@ if __name__ == '__main__': sys.path.append(os.path.join(os.path.dirname(__file__), '..')) -import types import unittest +import six + from grit import tclib from grit import exception @@ -26,14 +27,14 @@ msg = tclib.Message(text=u'Hello Earthlings', description='Greetings\n\t message') self.failUnlessEqual(msg.GetPresentableContent(), 'Hello Earthlings') - self.failUnless(isinstance(msg.GetPresentableContent(), types.StringTypes)) + self.failUnless(isinstance(msg.GetPresentableContent(), six.string_types)) self.failUnlessEqual(msg.GetDescription(), 'Greetings message') def testGetAttr(self): msg = tclib.Message() msg.AppendText(u'Hello') # Tests __getattr__ self.failUnless(msg.GetPresentableContent() == 'Hello') - self.failUnless(isinstance(msg.GetPresentableContent(), types.StringTypes)) + self.failUnless(isinstance(msg.GetPresentableContent(), six.string_types)) def testAll(self): text = u'Howdie USERNAME' @@ -43,7 +44,7 @@ trans = tclib.Translation(text=text, placeholders=phs) self.failUnless(trans.GetPresentableContent() == 'Howdie USERNAME') - self.failUnless(isinstance(trans.GetPresentableContent(), types.StringTypes)) + self.failUnless(isinstance(trans.GetPresentableContent(), six.string_types)) def testUnicodeReturn(self): text = u'\u00fe' @@ -65,7 +66,7 @@ transl = tclib.Translation(text=msg.GetPresentableContent(), placeholders=msg.GetPlaceholders()) content = transl.GetContent() - self.failUnless(isinstance(content[3], types.UnicodeType)) + self.failUnless(isinstance(content[3], six.string_types)) def testFingerprint(self): # This has Windows line endings. That is on purpose.
diff --git a/tools/grit/grit/tool/menu_from_parts.py b/tools/grit/grit/tool/menu_from_parts.py index 94a1fb0..c4b4adbc 100644 --- a/tools/grit/grit/tool/menu_from_parts.py +++ b/tools/grit/grit/tool/menu_from_parts.py
@@ -6,7 +6,7 @@ from __future__ import print_function -import types +import six from grit import grd_reader from grit import util @@ -61,7 +61,7 @@ contents = message.GetContent() for part in contents: - if isinstance(part, types.StringTypes): + if isinstance(part, six.string_types): id = grit.extern.tclib.GenerateMessageId(part) if id not in xtb: print("WARNING didn't find all translations for menu %s" %
diff --git a/tools/grit/grit/tool/rc2grd.py b/tools/grit/grit/tool/rc2grd.py index beb80e9..dc406aaa 100644 --- a/tools/grit/grit/tool/rc2grd.py +++ b/tools/grit/grit/tool/rc2grd.py
@@ -10,8 +10,8 @@ import getopt import re import sys -import types +import six from six import StringIO import grit.node.empty @@ -341,7 +341,7 @@ # Messages that contain only placeholders do not need translation. is_translateable = False for item in msg_obj.GetContent(): - if isinstance(item, types.StringTypes): + if isinstance(item, six.string_types): if not _WHITESPACE_ONLY.match(item): is_translateable = True @@ -389,7 +389,7 @@ # TODO(joi) Allow use of non-TotalRecall flavors of HTML placeholderizing msg = tr_html.HtmlToMessage(text, True) for item in msg.GetContent(): - if not isinstance(item, types.StringTypes): + if not isinstance(item, six.string_types): return msg # Contained at least one placeholder, so we're done # HTML placeholderization didn't do anything, so try to find printf or
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index ceaf035..e86cc49 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -8800,6 +8800,13 @@ <int value="7" label="Cookies, Storage, and Cache"/> </enum> +<enum name="ClickToCallDeviceState"> + <int value="0" label="Screen off, in background"/> + <int value="1" label="Screen on, in background"/> + <int value="2" label="Screen off, in foreground"/> + <int value="3" label="Screen on, in foreground"/> +</enum> + <enum name="ClientAppId"> <int value="0" label="Other"/> <int value="1" label="Gmail"/> @@ -19013,7 +19020,7 @@ label="DELETED_EXPERIMENTAL_MEDIAGALLERIES_ASSEMBLEMEDIAFILE"/> <int value="150" label="BOOKMARKMANAGERPRIVATE_STARTDRAG"/> <int value="151" label="BROWSINGDATA_REMOVEPASSWORDS"/> - <int value="152" label="DOWNLOADS_DRAG"/> + <int value="152" label="DELETED_DOWNLOADS_DRAG"/> <int value="153" label="INPUT_IME_SETCOMPOSITION"/> <int value="154" label="METRICSPRIVATE_RECORDUSERACTION"/> <int value="155" label="USB_RELEASEINTERFACE"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 02ee05830..862e8ce0 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -7635,6 +7635,9 @@ <histogram name="Ash.WindowCycleController.CycleTime" units="ms" expires_after="M77"> + <obsolete> + Deprecated as of 07/2019. + </obsolete> <owner>wutao@chromium.org</owner> <owner>tbuckley@google.com</owner> <summary> @@ -7643,7 +7646,8 @@ </summary> </histogram> -<histogram name="Ash.WindowCycleController.Items" units="items"> +<histogram name="Ash.WindowCycleController.Items" units="items" + expires_after="M79"> <owner>wutao@chromium.org</owner> <owner>tbuckley@google.com</owner> <summary> @@ -7654,6 +7658,9 @@ <histogram name="Ash.WindowCycleController.SelectionDepth" units="items" expires_after="M77"> + <obsolete> + Deprecated as of 07/2019. + </obsolete> <owner>wutao@chromium.org</owner> <owner>tbuckley@google.com</owner> <summary> @@ -122892,8 +122899,10 @@ </histogram> <histogram name="ServiceWorker.BackgroundFetchAbortEvent.Time" units="ms" - expires_after="M78"> + expires_after="M85"> + <owner>nator@chromium.org</owner> <owner>peter@chromium.org</owner> + <owner>rayankans@chromium.org</owner> <summary> The time taken between dispatching a BackgroundFetchAbortEvent to a Service Worker and receiving a message that it finished handling the event. Includes @@ -122901,8 +122910,11 @@ </summary> </histogram> -<histogram name="ServiceWorker.BackgroundFetchClickEvent.Time" units="ms"> +<histogram name="ServiceWorker.BackgroundFetchClickEvent.Time" units="ms" + expires_after="M85"> + <owner>nator@chromium.org</owner> <owner>peter@chromium.org</owner> + <owner>rayankans@chromium.org</owner> <summary> The time taken between dispatching a BackgroundFetchClickEvent to a Service Worker and receiving a message that it finished handling the event. Includes @@ -122924,8 +122936,11 @@ </summary> </histogram> -<histogram name="ServiceWorker.BackgroundFetchFailEvent.Time" units="ms"> +<histogram name="ServiceWorker.BackgroundFetchFailEvent.Time" units="ms" + expires_after="M85"> + <owner>nator@chromium.org</owner> <owner>peter@chromium.org</owner> + <owner>rayankans@chromium.org</owner> <summary> The time taken between dispatching a BackgroundFetchFailEvent to a Service Worker and receiving a message that it finished handling the event. Includes @@ -126883,6 +126898,27 @@ </summary> </histogram> +<histogram name="Sharing.ClickToCallPhoneCall" units="ms" expires_after="M80"> + <owner>mvanouwerkerk@chromium.org</owner> + <owner>knollr@chromium.org</owner> + <summary> + The time from opening the dialer until a phone call is initiated. This is + logged when we detect an outgoing phone call after opening the dialer as + part of the Click to Call feature. Android only. + </summary> +</histogram> + +<histogram name="Sharing.ClickToCallReceiveDeviceState" + enum="ClickToCallDeviceState" expires_after="M80"> + <owner>mvanouwerkerk@chromium.org</owner> + <owner>knollr@chromium.org</owner> + <summary> + The device state when receiving a Click to Call message. Indicates if the + screen is on or off and if Chrome is running in foreground. Recorded when + handling a Click to Call message. Android only. + </summary> +</histogram> + <histogram name="Sharing.ClickToCallSelectedAppIndex" units="index" expires_after="2020-02-02"> <!-- Name completed by histogram_suffixes name="SharingClickToCallUi" --> @@ -143792,37 +143828,6 @@ </summary> </histogram> -<histogram name="UMA.Dummy.Histogram.CheckOwnersExpansion.Basic"> - <owner>caitlinfischer@google.com</owner> - <owner>src/base/metrics/OWNERS</owner> - <summary> - The purpose of this histogram is to verify that components can be extracted - from an OWNERS file. After verifying, this histogram should be deleted. - </summary> -</histogram> - -<histogram - name="UMA.Dummy.Histogram.CheckOwnersExpansion.ComponentInHigherLevelDirectory"> - <owner>caitlinfischer@google.com</owner> - <owner>src/chrome/browser/metrics/oom/OWNERS</owner> - <summary> - The purpose of this histogram is to verify that a component from a - higher-level directory's OWNERS file is extracted when a component cannot be - found in the given OWNERS file. After verifying, this histogram should be - deleted. - </summary> -</histogram> - -<histogram name="UMA.Dummy.Histogram.CheckOwnersExpansion.NoComponent"> - <owner>caitlinfischer@google.com</owner> - <owner>src/jingle/OWNERS</owner> - <summary> - The purpose of this histogram is to verify that no component is added when a - component cannot be found in the given OWNERS file or in an OWNERS file in a - higher-level directory. After verifying, this histogram should be deleted. - </summary> -</histogram> - <histogram name="UMA.EnrollmentStatus" enum="EnrollmentStatus" expires_after="2020-01-26"> <owner>asvitkine@chromium.org</owner>
diff --git a/tools/perf/benchmarks/benchmark_smoke_unittest.py b/tools/perf/benchmarks/benchmark_smoke_unittest.py index 519c165a..a5062cc 100644 --- a/tools/perf/benchmarks/benchmark_smoke_unittest.py +++ b/tools/perf/benchmarks/benchmark_smoke_unittest.py
@@ -24,6 +24,7 @@ from py_utils import tempfile_ext from benchmarks import jetstream +from benchmarks import jetstream2 from benchmarks import octane from benchmarks import rasterize_and_record_micro from benchmarks import speedometer @@ -103,6 +104,7 @@ rasterize_and_record_micro, # Always fails on cq bot. speedometer, # Takes 101 seconds. jetstream, # Take 206 seconds. + jetstream2, # Causes CQ shard to timeout, crbug.com/992837 v8_browsing, # Flaky on Android, crbug.com/628368. }
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc index 64c03487..f92f31d 100644 --- a/ui/accessibility/ax_node_position_unittest.cc +++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -49,13 +49,13 @@ protected: void SetUp() override; void TearDown() override; - AXTree* CreateMultipageDocument(ui::AXNodeData& root_data, - ui::AXNodeData& page_1_data, - ui::AXNodeData& page_1_text_data, - ui::AXNodeData& page_2_data, - ui::AXNodeData& page_2_text_data, - ui::AXNodeData& page_3_data, - ui::AXNodeData& page_3_text_data) { + AXTree* CreateMultipageDocument(AXNodeData& root_data, + AXNodeData& page_1_data, + AXNodeData& page_1_text_data, + AXNodeData& page_2_data, + AXNodeData& page_2_text_data, + AXNodeData& page_3_data, + AXNodeData& page_3_text_data) { AXNodePosition::SetTreeForTesting(nullptr); root_data.id = 1; @@ -98,8 +98,8 @@ root_data.child_ids = {2, 4, 6}; - ui::AXTreeUpdate update; - ui::AXTreeData tree_data; + AXTreeUpdate update; + AXTreeData tree_data; AXTreeID new_id = AXTreeID::CreateNewAXTreeID(); tree_data.tree_id = new_id; update.tree_data = tree_data; @@ -127,17 +127,16 @@ // Creates a new AXTree from a vector of nodes. // Assumes the first node in the vector is the root. - std::unique_ptr<AXTree> CreateAXTree( - const std::vector<ui::AXNodeData>& nodes) { - ui::AXTreeUpdate update; - ui::AXTreeData tree_data; - tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID(); + std::unique_ptr<AXTree> CreateAXTree(const std::vector<AXNodeData>& nodes) { + AXTreeUpdate update; + AXTreeData tree_data; + tree_data.tree_id = AXTreeID::CreateNewAXTreeID(); update.tree_data = tree_data; update.has_tree_data = true; update.root_id = nodes[0].id; update.nodes = nodes; - tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID(); + tree_data.tree_id = AXTreeID::CreateNewAXTreeID(); update.tree_data = tree_data; std::unique_ptr<AXTree> tree = std::make_unique<AXTree>(update); AXNodePosition::SetTreeForTesting(tree.get()); @@ -230,8 +229,6 @@ button_.SetHasPopup(ax::mojom::HasPopup::kMenu); button_.SetName("Button"); button_.relative_bounds.bounds = gfx::RectF(20, 20, 200, 30); - button_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId, - check_box_.id); root_.child_ids.push_back(button_.id); check_box_.role = ax::mojom::Role::kCheckBox; @@ -240,8 +237,6 @@ check_box_.SetCheckedState(ax::mojom::CheckedState::kTrue); check_box_.SetName("Check box"); check_box_.relative_bounds.bounds = gfx::RectF(20, 50, 200, 30); - check_box_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId, - button_.id); root_.child_ids.push_back(check_box_.id); text_field_.role = ax::mojom::Role::kTextField; @@ -525,16 +520,16 @@ TEST_F(AXPositionTest, GetMaxTextOffsetUpdate) { AXNodePosition::SetTreeForTesting(nullptr); - ui::AXNodeData root_data; + AXNodeData root_data; root_data.id = 1; root_data.role = ax::mojom::Role::kRootWebArea; - ui::AXNodeData text_data; + AXNodeData text_data; text_data.id = 2; text_data.role = ax::mojom::Role::kStaticText; text_data.SetName("some text"); - ui::AXNodeData more_text_data; + AXNodeData more_text_data; more_text_data.id = 3; more_text_data.role = ax::mojom::Role::kStaticText; more_text_data.SetName("more text"); @@ -937,41 +932,41 @@ // ++++++++7 kInlineTextBox "more text" AXNodePosition::SetTreeForTesting(nullptr); - ui::AXNodeData root_data; + AXNodeData root_data; root_data.id = 1; root_data.role = ax::mojom::Role::kRootWebArea; root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - ui::AXNodeData static_text_data_1; + AXNodeData static_text_data_1; static_text_data_1.id = 2; static_text_data_1.role = ax::mojom::Role::kStaticText; static_text_data_1.SetName("some text"); - ui::AXNodeData some_text_data; + AXNodeData some_text_data; some_text_data.id = 3; some_text_data.role = ax::mojom::Role::kInlineTextBox; some_text_data.SetName("some text"); - ui::AXNodeData container_data; + AXNodeData container_data; container_data.id = 4; container_data.role = ax::mojom::Role::kGenericContainer; container_data.AddBoolAttribute( ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - ui::AXNodeData static_text_data_2; + AXNodeData static_text_data_2; static_text_data_2.id = 5; static_text_data_2.role = ax::mojom::Role::kStaticText; static_text_data_2.SetName("\nmore text"); - ui::AXNodeData preserved_newline_data; + AXNodeData preserved_newline_data; preserved_newline_data.id = 6; preserved_newline_data.role = ax::mojom::Role::kInlineTextBox; preserved_newline_data.SetName("\n"); preserved_newline_data.AddBoolAttribute( ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - ui::AXNodeData more_text_data; + AXNodeData more_text_data; more_text_data.id = 7; more_text_data.role = ax::mojom::Role::kInlineTextBox; more_text_data.SetName("more text"); @@ -981,19 +976,9 @@ static_text_data_2.child_ids = {6, 7}; root_data.child_ids = {2, 4}; - ui::AXTreeUpdate update; - ui::AXTreeData tree_data; - tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID(); - update.tree_data = tree_data; - update.has_tree_data = true; - update.root_id = root_data.id; - update.nodes = {root_data, static_text_data_1, some_text_data, - container_data, static_text_data_2, preserved_newline_data, - more_text_data}; - - std::unique_ptr<AXTree> new_tree; - new_tree.reset(new AXTree(update)); - AXNodePosition::SetTreeForTesting(new_tree.get()); + std::unique_ptr<AXTree> new_tree = CreateAXTree( + {root_data, static_text_data_1, some_text_data, container_data, + static_text_data_2, preserved_newline_data, more_text_data}); TestPositionType text_position1 = AXNodePosition::CreateTextPosition( new_tree->data().tree_id, root_data.id, 8 /* text_offset */, @@ -1099,68 +1084,68 @@ // ++++++++12 kInlineTextBox "\n" isLineBreakingObject AXNodePosition::SetTreeForTesting(nullptr); - ui::AXNodeData root_data; + AXNodeData root_data; root_data.id = 1; root_data.role = ax::mojom::Role::kRootWebArea; root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - ui::AXNodeData container_data_a; + AXNodeData container_data_a; container_data_a.id = 2; container_data_a.role = ax::mojom::Role::kGenericContainer; container_data_a.AddBoolAttribute( ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - ui::AXNodeData static_text_data_a; + AXNodeData static_text_data_a; static_text_data_a.id = 3; static_text_data_a.role = ax::mojom::Role::kStaticText; static_text_data_a.SetName("\n"); - ui::AXNodeData inline_text_data_a; + AXNodeData inline_text_data_a; inline_text_data_a.id = 4; inline_text_data_a.role = ax::mojom::Role::kInlineTextBox; inline_text_data_a.SetName("\n"); inline_text_data_a.AddBoolAttribute( ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - ui::AXNodeData container_data_b; + AXNodeData container_data_b; container_data_b.id = 5; container_data_b.role = ax::mojom::Role::kGenericContainer; container_data_b.AddBoolAttribute( ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - ui::AXNodeData static_text_data_b; + AXNodeData static_text_data_b; static_text_data_b.id = 6; static_text_data_b.role = ax::mojom::Role::kStaticText; static_text_data_b.SetName("some text"); - ui::AXNodeData inline_text_data_b_1; + AXNodeData inline_text_data_b_1; inline_text_data_b_1.id = 7; inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox; inline_text_data_b_1.SetName("some"); - ui::AXNodeData inline_text_data_b_2; + AXNodeData inline_text_data_b_2; inline_text_data_b_2.id = 8; inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox; inline_text_data_b_2.SetName(" "); - ui::AXNodeData inline_text_data_b_3; + AXNodeData inline_text_data_b_3; inline_text_data_b_3.id = 9; inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox; inline_text_data_b_3.SetName("text"); - ui::AXNodeData container_data_c; + AXNodeData container_data_c; container_data_c.id = 10; container_data_c.role = ax::mojom::Role::kGenericContainer; container_data_c.AddBoolAttribute( ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - ui::AXNodeData static_text_data_c; + AXNodeData static_text_data_c; static_text_data_c.id = 11; static_text_data_c.role = ax::mojom::Role::kStaticText; static_text_data_c.SetName("\n"); - ui::AXNodeData inline_text_data_c; + AXNodeData inline_text_data_c; inline_text_data_c.id = 12; inline_text_data_c.role = ax::mojom::Role::kInlineTextBox; inline_text_data_c.SetName("\n"); @@ -1178,28 +1163,11 @@ container_data_c.child_ids = {static_text_data_c.id}; static_text_data_c.child_ids = {inline_text_data_c.id}; - ui::AXTreeUpdate update; - ui::AXTreeData tree_data; - tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID(); - update.tree_data = tree_data; - update.has_tree_data = true; - update.root_id = root_data.id; - update.nodes = {root_data, - container_data_a, - container_data_b, - container_data_c, - static_text_data_a, - static_text_data_b, - static_text_data_c, - inline_text_data_a, - inline_text_data_b_1, - inline_text_data_b_2, - inline_text_data_b_3, - inline_text_data_c}; - - std::unique_ptr<AXTree> new_tree; - new_tree.reset(new AXTree(update)); - AXNodePosition::SetTreeForTesting(new_tree.get()); + std::unique_ptr<AXTree> new_tree = CreateAXTree( + {root_data, container_data_a, container_data_b, container_data_c, + static_text_data_a, static_text_data_b, static_text_data_c, + inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2, + inline_text_data_b_3, inline_text_data_c}); TestPositionType text_position1 = AXNodePosition::CreateTextPosition( new_tree->data().tree_id, inline_text_data_a.id, 0 /* text_offset */, @@ -1989,17 +1957,8 @@ root_data.child_ids = {text_data.id, more_text_data.id}; - AXTreeUpdate update; - AXTreeData tree_data; - tree_data.tree_id = AXTreeID::CreateNewAXTreeID(); - update.tree_data = tree_data; - update.has_tree_data = true; - update.root_id = root_data.id; - update.nodes = {root_data, text_data, more_text_data}; - - std::unique_ptr<AXTree> new_tree; - new_tree.reset(new AXTree(update)); - AXNodePosition::SetTreeForTesting(new_tree.get()); + std::unique_ptr<AXTree> new_tree = + CreateAXTree({root_data, text_data, more_text_data}); // Test CreatePreviousFormatStartPosition at the start of the document. TestPositionType text_position = AXNodePosition::CreateTextPosition( @@ -2025,16 +1984,14 @@ EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(more_text_data.id, test_position->anchor_id()); EXPECT_EQ(9, test_position->text_offset()); - - AXNodePosition::SetTreeForTesting(&tree_); } TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTextPosition) { AXNodePosition::SetTreeForTesting(nullptr); - ui::AXNodeData root_data, page_1_data, page_1_text_data, page_2_data, + AXNodeData root_data, page_1_data, page_1_text_data, page_2_data, page_2_text_data, page_3_data, page_3_text_data; - std::unique_ptr<ui::AXTree> new_tree(CreateMultipageDocument( + std::unique_ptr<AXTree> new_tree(CreateMultipageDocument( root_data, page_1_data, page_1_text_data, page_2_data, page_2_text_data, page_3_data, page_3_text_data)); AXNodePosition::SetTreeForTesting(new_tree.get()); @@ -2150,15 +2107,14 @@ AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); - - AXNodePosition::SetTreeForTesting(&tree_); } TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTreePosition) { AXNodePosition::SetTreeForTesting(nullptr); - ui::AXNodeData root_data, page_1_data, page_1_text_data, page_2_data, + + AXNodeData root_data, page_1_data, page_1_text_data, page_2_data, page_2_text_data, page_3_data, page_3_text_data; - std::unique_ptr<ui::AXTree> new_tree(CreateMultipageDocument( + std::unique_ptr<AXTree> new_tree(CreateMultipageDocument( root_data, page_1_data, page_1_text_data, page_2_data, page_2_text_data, page_3_data, page_3_text_data)); AXNodePosition::SetTreeForTesting(new_tree.get()); @@ -2273,8 +2229,6 @@ AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, null_position); EXPECT_TRUE(null_position->IsNullPosition()); - - AXNodePosition::SetTreeForTesting(&tree_); } TEST_F(AXPositionTest, CreatePagePositionWithNullPosition) { @@ -3739,18 +3693,8 @@ root_data.child_ids = {text_data.id, text_field_data.id, more_text_data.id}; text_field_data.child_ids = {empty_text_data.id}; - AXTreeUpdate update; - AXTreeData tree_data; - tree_data.tree_id = AXTreeID::CreateNewAXTreeID(); - update.tree_data = tree_data; - update.has_tree_data = true; - update.root_id = root_data.id; - update.nodes = {root_data, text_data, text_field_data, empty_text_data, - more_text_data}; - - std::unique_ptr<AXTree> new_tree; - new_tree.reset(new AXTree(update)); - AXNodePosition::SetTreeForTesting(new_tree.get()); + std::unique_ptr<AXTree> new_tree = CreateAXTree( + {root_data, text_data, text_field_data, empty_text_data, more_text_data}); // Test that CreateNextAnchorPosition will successfully navigate past the // empty text field. @@ -3761,8 +3705,140 @@ ASSERT_FALSE(text_position1->CreateNextAnchorPosition() ->CreateNextAnchorPosition() ->IsNullPosition()); +} - AXNodePosition::SetTreeForTesting(&tree_); +TEST_F(AXPositionTest, CreateLinePositionsMultipleAnchorsInSingleLine) { + // This test updates the tree structure to test a specific edge case - + // Create next and previous line start/end positions on a single line composed + // by multiple anchors; only two line boundaries should be resolved: either + // the start of the "before" text or at the end of "after". + // ++1 kRootWebArea + // ++++2 kStaticText + // ++++++3 kInlineTextBox "before" kNextOnLineId=6 + // ++++4 kGenericContainer + // ++++++5 kStaticText + // ++++++++6 kInlineTextBox "inside" kPreviousOnLineId=3 kNextOnLineId=8 + // ++++7 kStaticText + // ++++++8 kInlineTextBox "after" kPreviousOnLineId=6 + AXNodePosition::SetTreeForTesting(nullptr); + + AXNodeData root; + AXNodeData inline_box1; + AXNodeData inline_box2; + AXNodeData inline_box3; + AXNodeData inline_block; + AXNodeData static_text1; + AXNodeData static_text2; + AXNodeData static_text3; + + root.id = 1; + static_text1.id = 2; + inline_box1.id = 3; + inline_block.id = 4; + static_text2.id = 5; + inline_box2.id = 6; + static_text3.id = 7; + inline_box3.id = 8; + + root.role = ax::mojom::Role::kRootWebArea; + root.child_ids = {static_text1.id, inline_block.id, static_text3.id}; + + static_text1.role = ax::mojom::Role::kStaticText; + static_text1.SetName("before"); + static_text1.child_ids = {inline_box1.id}; + + inline_box1.role = ax::mojom::Role::kInlineTextBox; + inline_box1.SetName("before"); + inline_box1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId, + inline_box2.id); + + inline_block.role = ax::mojom::Role::kGenericContainer; + inline_block.child_ids = {static_text2.id}; + + static_text2.role = ax::mojom::Role::kStaticText; + static_text2.SetName("inside"); + static_text2.child_ids = {inline_box2.id}; + + inline_box2.role = ax::mojom::Role::kInlineTextBox; + inline_box2.SetName("inside"); + inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId, + inline_box1.id); + inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId, + inline_box3.id); + + static_text3.role = ax::mojom::Role::kStaticText; + static_text3.SetName("after"); + static_text3.child_ids = {inline_box3.id}; + + inline_box3.role = ax::mojom::Role::kInlineTextBox; + inline_box3.SetName("after"); + inline_box3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId, + inline_box2.id); + + std::unique_ptr<AXTree> new_tree = + CreateAXTree({root, static_text1, inline_box1, inline_block, static_text2, + inline_box2, static_text3, inline_box3}); + + TestPositionType text_position = AXNodePosition::CreateTextPosition( + new_tree->data().tree_id, inline_block.id, 3 /* text_offset */, + ax::mojom::TextAffinity::kDownstream); + ASSERT_NE(nullptr, text_position); + ASSERT_TRUE(text_position->IsTextPosition()); + + TestPositionType next_line_start_position = + text_position->CreateNextLineStartPosition( + AXBoundaryBehavior::CrossBoundary); + ASSERT_NE(nullptr, next_line_start_position); + EXPECT_TRUE(next_line_start_position->IsTextPosition()); + EXPECT_EQ(inline_box3.id, next_line_start_position->anchor_id()); + EXPECT_EQ(5, next_line_start_position->text_offset()); + + next_line_start_position = + next_line_start_position->CreateNextLineStartPosition( + AXBoundaryBehavior::CrossBoundary); + ASSERT_NE(nullptr, next_line_start_position); + EXPECT_TRUE(next_line_start_position->IsNullPosition()); + + TestPositionType previous_line_start_position = + text_position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::CrossBoundary); + ASSERT_NE(nullptr, previous_line_start_position); + EXPECT_TRUE(previous_line_start_position->IsTextPosition()); + EXPECT_EQ(inline_box1.id, previous_line_start_position->anchor_id()); + EXPECT_EQ(0, previous_line_start_position->text_offset()); + + previous_line_start_position = + previous_line_start_position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::CrossBoundary); + ASSERT_NE(nullptr, previous_line_start_position); + EXPECT_TRUE(previous_line_start_position->IsNullPosition()); + + TestPositionType next_line_end_position = + text_position->CreateNextLineEndPosition( + AXBoundaryBehavior::CrossBoundary); + ASSERT_NE(nullptr, next_line_end_position); + EXPECT_TRUE(next_line_end_position->IsTextPosition()); + EXPECT_EQ(inline_box3.id, next_line_end_position->anchor_id()); + EXPECT_EQ(5, next_line_end_position->text_offset()); + + next_line_end_position = next_line_end_position->CreateNextLineEndPosition( + AXBoundaryBehavior::CrossBoundary); + ASSERT_NE(nullptr, next_line_end_position); + EXPECT_TRUE(next_line_end_position->IsNullPosition()); + + TestPositionType previous_line_end_position = + text_position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::CrossBoundary); + ASSERT_NE(nullptr, previous_line_end_position); + EXPECT_TRUE(previous_line_end_position->IsTextPosition()); + EXPECT_EQ(inline_box1.id, previous_line_end_position->anchor_id()); + EXPECT_EQ(0, previous_line_end_position->text_offset()); + + previous_line_end_position = + previous_line_end_position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::CrossBoundary); + ASSERT_NE(nullptr, previous_line_end_position); + EXPECT_TRUE(previous_line_end_position->IsNullPosition()); } // @@ -4853,7 +4929,7 @@ {"TextPosition anchor_id=1 text_offset=6 " "affinity=downstream annotated_text=Line 1<\n>Line 2", "TextPosition anchor_id=1 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "affinity=upstream annotated_text=<L>ine 1\nLine 2", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( @@ -4863,8 +4939,8 @@ 13 /* text_offset at end of text field */, {"TextPosition anchor_id=4 text_offset=6 " "affinity=downstream annotated_text=Line 1<\n>Line 2", - "TextPosition anchor_id=4 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition anchor_id=2 text_offset=0 " + "affinity=downstream annotated_text=<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( @@ -4873,7 +4949,7 @@ ROOT_ID, 5 /* text_offset on the last character of "Line 1". */, {"TextPosition anchor_id=1 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "affinity=upstream annotated_text=<L>ine 1\nLine 2", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( @@ -4881,8 +4957,8 @@ }), TEXT_FIELD_ID, 5 /* text_offset on the last character of "Line 1". */, - {"TextPosition anchor_id=4 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 1\nLine 2", + {"TextPosition anchor_id=2 text_offset=0 " + "affinity=downstream annotated_text=<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( @@ -4892,8 +4968,8 @@ 4 /* text_offset */, {"TextPosition anchor_id=6 text_offset=6 " "affinity=downstream annotated_text=Line 1<>", - "TextPosition anchor_id=6 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 1", + "TextPosition anchor_id=2 text_offset=0 " + "affinity=downstream annotated_text=<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { return position->CreatePreviousLineEndPosition( @@ -4903,8 +4979,8 @@ 0 /* text_offset */, {"TextPosition anchor_id=6 text_offset=6 " "affinity=downstream annotated_text=Line 1<>", - "TextPosition anchor_id=6 text_offset=0 " - "affinity=downstream annotated_text=<L>ine 1", + "TextPosition anchor_id=2 text_offset=0 " + "affinity=downstream annotated_text=<>", "NullPosition"}})); INSTANTIATE_TEST_SUITE_P(
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h index 076d171..5f1bd11 100644 --- a/ui/accessibility/ax_position.h +++ b/ui/accessibility/ax_position.h
@@ -1160,10 +1160,9 @@ } while (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position); - // If the word boundary is in the same subtree, return a position rooted - // at the current position. This is necessary because we don't want to - // return any position that might be in the shadow DOM if the original - // position was not. + // If the word boundary is in the same subtree, return a position rooted at + // this position's anchor. This is necessary because we don't want to return + // a position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1223,10 +1222,9 @@ } while (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position); - // If the word boundary is in the same subtree, return a position rooted - // at the current position. This is necessary because we don't want to - // return any position that might be in the shadow DOM if the original - // position was not. + // If the word boundary is in the same subtree, return a position rooted at + // this position's anchor. This is necessary because we don't want to return + // a position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1291,10 +1289,9 @@ } while (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position); - // If the word boundary is in the same subtree, return a position rooted - // at the current position. This is necessary because we don't want to - // return any position that might be in the shadow DOM if the original - // position was not. + // If the word boundary is in the same subtree, return a position rooted at + // this position's anchor. This is necessary because we don't want to return + // a position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1361,10 +1358,9 @@ } while (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position); - // If the word boundary is in the same subtree, return a position rooted - // at the current position. This is necessary because we don't want to - // return any position that might be in the shadow DOM if the original - // position was not. + // If the word boundary is in the same subtree, return a position rooted at + // this position's anchor. This is necessary because we don't want to return + // a position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1384,6 +1380,7 @@ AXPositionInstance text_position = AsLeafTextPosition(); if (text_position->IsNullPosition()) return text_position; + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && text_position->AtStartOfLine()) { AXPositionInstance clone = Clone(); @@ -1392,25 +1389,32 @@ } do { - text_position = text_position->CreateNextLeafTextPosition(); - if (text_position->IsNullPosition()) { - if (AtEndOfAnchor() && - boundary_behavior == AXBoundaryBehavior::CrossBoundary) - return text_position; + AXPositionInstance next_position = + text_position->CreateNextLeafTextPosition(); + + if (next_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::CrossBoundary) { + if (AtEndOfAnchor()) + return next_position; + // We can't simply return the following position; break and after this + // loop we'll try to do some adjustments to the result position. + text_position = text_position->CreatePositionAtEndOfAnchor(); + break; + } return CreatePositionAtEndOfAnchor(); } // Continue searching for the next line start until the next logical text // position is reached. + text_position = std::move(next_position); } while ( !text_position->AtStartOfLine() || (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position)); // If the line boundary is in the same subtree, return a position rooted at - // the current position. - // This is necessary because we don't want to return any position that might - // be in the shadow DOM if the original position was not. + // this position's anchor. This is necessary because we don't want to return + // a position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1428,6 +1432,9 @@ AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); + if (text_position->IsNullPosition()) + return text_position; + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && text_position->AtStartOfLine()) { AXPositionInstance clone = Clone(); @@ -1436,11 +1443,9 @@ } do { - if (text_position->AtStartOfAnchor()) { - text_position = text_position->CreatePreviousLeafTextPosition(); - } else { - text_position = text_position->CreatePositionAtStartOfAnchor(); - } + text_position = text_position->AtStartOfAnchor() + ? text_position->CreatePreviousLeafTextPosition() + : text_position->CreatePositionAtStartOfAnchor(); if (text_position->IsNullPosition()) { if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) @@ -1456,9 +1461,8 @@ *this == *text_position)); // If the line boundary is in the same subtree, return a position rooted at - // the current position. - // This is necessary because we don't want to return any position that might - // be in the shadow DOM if the original position was not. + // this position's anchor. This is necessary because we don't want to return + // a position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1478,6 +1482,9 @@ AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); + if (text_position->IsNullPosition()) + return text_position; + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && text_position->AtEndOfLine()) { AXPositionInstance clone = Clone(); @@ -1492,12 +1499,9 @@ } do { - if (text_position->AtEndOfAnchor()) { - text_position = text_position->CreateNextLeafTextPosition() - ->CreatePositionAtEndOfAnchor(); - } else { - text_position = text_position->CreatePositionAtEndOfAnchor(); - } + if (text_position->AtEndOfAnchor()) + text_position = text_position->CreateNextLeafTextPosition(); + text_position = text_position->CreatePositionAtEndOfAnchor(); if (text_position->IsNullPosition()) { if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) @@ -1513,9 +1517,8 @@ *this == *text_position)); // If the line boundary is in the same subtree, return a position rooted at - // the current position. This is necessary because we don't want to return - // any position that might be in the shadow DOM if the original position was - // not. + // this position's anchor. This is necessary because we don't want to return + // a position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1537,6 +1540,7 @@ AXPositionInstance text_position = AsLeafTextPosition(); if (text_position->IsNullPosition()) return text_position; + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && text_position->AtEndOfLine()) { AXPositionInstance clone = Clone(); @@ -1551,26 +1555,33 @@ } do { - text_position = text_position->CreatePreviousLeafTextPosition() - ->CreatePositionAtEndOfAnchor(); - if (text_position->IsNullPosition()) { - if (AtStartOfAnchor() && - boundary_behavior == AXBoundaryBehavior::CrossBoundary) - return text_position; + AXPositionInstance previous_position = + text_position->CreatePreviousLeafTextPosition() + ->CreatePositionAtEndOfAnchor(); + + if (previous_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::CrossBoundary) { + if (AtStartOfAnchor()) + return previous_position; + // We can't simply return the following position; break and after this + // loop we'll try to do some adjustments to the result position. + text_position = text_position->CreatePositionAtStartOfAnchor(); + break; + } return CreatePositionAtStartOfAnchor(); } // Continue searching for the previous line end until the next logical // text position is reached. + text_position = std::move(previous_position); } while ( !text_position->AtEndOfLine() || (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position)); // If the line boundary is in the same subtree, return a position rooted at - // the current position. This is necessary because we don't want to return - // any position that might be in the shadow DOM if the original position was - // not. + // this position's anchor. This is necessary because we don't want to return + // a position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1670,10 +1681,9 @@ (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position)); - // If the boundary is in the same subtree, return a position rooted at the - // current position. This is necessary because we don't want to return any - // position that might be in the shadow DOM if the original position was - // not. + // If the boundary is in the same subtree, return a position rooted at this + // position's anchor. This is necessary because we don't want to return a + // position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1713,17 +1723,16 @@ return text_position; } - // Continue searching for the previous page start until the next + // Continue searching for the previous boundary start until the next // logical text position is reached. } while ( !at_start_condition.Run(text_position) || (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position)); - // If the boundary is in the same subtree, return a position rooted at the - // current position. This is necessary because we don't want to return any - // position that might be in the shadow DOM if the original position was - // not. + // If the boundary is in the same subtree, return a position rooted at this + // position's anchor. This is necessary because we don't want to return a + // position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1777,10 +1786,9 @@ (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position)); - // If the boundary is in the same subtree, return a position rooted at the - // current position. This is necessary because we don't want to return any - // position that might be in the shadow DOM if the original position was - // not. + // If the boundary is in the same subtree, return a position rooted at this + // position's anchor. This is necessary because we don't want to return a + // position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) { @@ -1831,10 +1839,9 @@ (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary && *this == *text_position)); - // If the boundary is in the same subtree, return a position rooted at the - // current position. This is necessary because we don't want to return any - // position that might be in the shadow DOM if the original position was - // not. + // If the boundary is in the same subtree, return a position rooted at this + // position's anchor. This is necessary because we don't want to return a + // position that might be in the shadow DOM when this position is not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); if (GetAnchor() == common_ancestor->GetAnchor()) {
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc index 7f32ce0..feb79f4 100644 --- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc +++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -2725,8 +2725,8 @@ // <button>Button</button><input type="checkbox">Line 1<br>Line 2 // |---------------------||---------------------| ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit( - TextPatternRangeEndpoint_End, TextUnit_Line, /*count*/ -1, &count)); - ASSERT_EQ(-1, count); + TextPatternRangeEndpoint_End, TextUnit_Word, /*count*/ -2, &count)); + ASSERT_EQ(-2, count); EXPECT_HRESULT_SUCCEEDED( text_range_provider->GetBoundingRectangles(rectangles.Receive())); expected_values = {20, 20, 200, 30, /* button */
diff --git a/ui/android/java/res/values/color_palette.xml b/ui/android/java/res/values/color_palette.xml index 7bf330e..7c44f37 100644 --- a/ui/android/java/res/values/color_palette.xml +++ b/ui/android/java/res/values/color_palette.xml
@@ -20,16 +20,16 @@ <color name="modern_grey_900">#202124</color> <color name="modern_grey_900_alpha_38" tools:ignore="UnusedResources">#61202124</color> - <color name="modern_grey_900_with_grey_200_alpha_4">#292A2D</color> - <color name="modern_grey_900_with_grey_200_alpha_6">#303134</color> - <color name="modern_grey_900_with_grey_200_alpha_8">#35363A</color> - <color name="modern_grey_900_with_grey_200_alpha_10">#38383C</color> + <color name="modern_grey_900_with_grey_200_alpha_5">#292A2D</color> + <color name="modern_grey_900_with_grey_200_alpha_8">#303134</color> + <color name="modern_grey_900_with_grey_200_alpha_11">#35363A</color> + <color name="modern_grey_900_with_grey_200_alpha_12">#38383C</color> <color name="black_alpha_38" tools:ignore="UnusedResources">#61000000</color> <color name="white_alpha_10" tools:ignore="UnusedResources">#1AFFFFFF</color> <color name="white_alpha_12" tools:ignore="UnusedResources">#1FFFFFFF</color> - <color name="white_alpha_20">#33FFFFFF</color> + <color name="white_alpha_20" tools:ignore="UnusedResources">#33FFFFFF</color> <color name="white_alpha_38" tools:ignore="UnusedResources">#61FFFFFF</color> <color name="white_alpha_50" tools:ignore="UnusedResources">#80FFFFFF</color> <color name="white_alpha_70">#B3FFFFFF</color> @@ -48,14 +48,14 @@ @android:color/white </color> <color name="default_icon_color_white_disabled" tools:ignore="UnusedResources"> - @color/white_alpha_20 + @color/white_alpha_50 </color> <color name="default_icon_color_white_pressed" tools:ignore="UnusedResources"> @color/white_alpha_50 </color> <color name="default_icon_color_secondary_dark">@color/modern_grey_600</color> - <color name="default_icon_color_secondary_light">@color/modern_grey_500</color> + <color name="default_icon_color_secondary_light">@color/white_alpha_70</color> <!-- Common text colors --> <color name="default_text_color_dark">@color/modern_grey_900</color> @@ -77,16 +77,16 @@ @color/modern_grey_900 </color> <color name="default_bg_color_dark_elev_1" tools:ignore="UnusedResources"> - @color/modern_grey_900_with_grey_200_alpha_4 + @color/modern_grey_900_with_grey_200_alpha_5 </color> <color name="default_bg_color_dark_elev_2" tools:ignore="UnusedResources"> - @color/modern_grey_900_with_grey_200_alpha_6 - </color> - <color name="default_bg_color_dark_elev_3" tools:ignore="UnusedResources"> @color/modern_grey_900_with_grey_200_alpha_8 </color> + <color name="default_bg_color_dark_elev_3" tools:ignore="UnusedResources"> + @color/modern_grey_900_with_grey_200_alpha_11 + </color> <color name="default_bg_color_dark_elev_4" tools:ignore="UnusedResources"> - @color/modern_grey_900_with_grey_200_alpha_10 + @color/modern_grey_900_with_grey_200_alpha_12 </color> <color name="divider_bg_color_dark">@color/modern_grey_300</color> <color name="divider_bg_color_light">@color/white_alpha_12</color>
diff --git a/ui/android/java/res_night/values-night/colors.xml b/ui/android/java/res_night/values-night/colors.xml index 26624138..133b5f2 100644 --- a/ui/android/java/res_night/values-night/colors.xml +++ b/ui/android/java/res_night/values-night/colors.xml
@@ -8,7 +8,7 @@ <color name="default_text_color">@color/default_text_color_light</color> <color name="default_text_color_inverse">@color/default_text_color_dark</color> <color name="default_text_color_secondary">@color/white_alpha_70</color> - <color name="default_text_color_tertiary">@color/white_alpha_38</color> + <color name="default_text_color_tertiary">@color/white_alpha_50</color> <color name="default_text_color_blue">@color/modern_blue_300</color> <color name="default_text_color_link">@color/modern_blue_300</color> <color name="disabled_text_color_link">@color/modern_grey_300_alpha_38</color>
diff --git a/ui/aura/client/aura_constants.cc b/ui/aura/client/aura_constants.cc index 5826c67..079633f 100644 --- a/ui/aura/client/aura_constants.cc +++ b/ui/aura/client/aura_constants.cc
@@ -70,7 +70,6 @@ kPreFullscreenShowStateKey, ui::SHOW_STATE_DEFAULT) DEFINE_UI_CLASS_PROPERTY_KEY(int, kResizeBehaviorKey, kResizeBehaviorCanResize) -DEFINE_UI_CLASS_PROPERTY_KEY(int, kResizeHandleInset, 0) DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Rect, kRestoreBoundsKey, nullptr) DEFINE_UI_CLASS_PROPERTY_KEY(ui::WindowShowState, kShowStateKey,
diff --git a/ui/aura/client/aura_constants.h b/ui/aura/client/aura_constants.h index 0991ada..0dc161c5 100644 --- a/ui/aura/client/aura_constants.h +++ b/ui/aura/client/aura_constants.h
@@ -134,12 +134,6 @@ // ResizeBehavior values. AURA_EXPORT extern const WindowProperty<int>* const kResizeBehaviorKey; -// Reserves a number of dip around the window (i.e. inset from its exterior -// border) for event routing back to the top level window. This is used for -// routing events to toplevel window resize handles. It should only be respected -// for restored windows (maximized and fullscreen can't be drag-resized). -AURA_EXPORT extern const WindowProperty<int>* const kResizeHandleInset; - // A property key to store the restore bounds in screen coordinates for a // window. AURA_EXPORT extern const WindowProperty<gfx::Rect*>* const kRestoreBoundsKey;
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn index f4e54c54..28496fb 100644 --- a/ui/events/BUILD.gn +++ b/ui/events/BUILD.gn
@@ -513,6 +513,7 @@ "blink/prediction/kalman_predictor_unittest.cc", "blink/prediction/least_squares_predictor_unittest.cc", "blink/prediction/linear_predictor_unittest.cc", + "blink/prediction/one_euro_filter_unittests.cc", "blink/scroll_predictor_unittest.cc", "blink/web_input_event_traits_unittest.cc", "blink/web_input_event_unittest.cc",
diff --git a/ui/events/blink/BUILD.gn b/ui/events/blink/BUILD.gn index e4b823f..3f1e498c 100644 --- a/ui/events/blink/BUILD.gn +++ b/ui/events/blink/BUILD.gn
@@ -53,6 +53,8 @@ "prediction/least_squares_predictor.h", "prediction/linear_predictor.cc", "prediction/linear_predictor.h", + "prediction/one_euro_filter.cc", + "prediction/one_euro_filter.h", "prediction/predictor_factory.cc", "prediction/predictor_factory.h", "scroll_predictor.cc",
diff --git a/ui/events/blink/DEPS b/ui/events/blink/DEPS index 4a931578..adc4c31 100644 --- a/ui/events/blink/DEPS +++ b/ui/events/blink/DEPS
@@ -22,4 +22,6 @@ "+ui/display/win", "+ui/gfx", "+ui/gfx/geometry", + + "+third_party/one_euro_filter/src/one_euro_filter.h", ]
diff --git a/ui/events/blink/prediction/empty_filter.cc b/ui/events/blink/prediction/empty_filter.cc index dd3a3ff1..66a5056 100644 --- a/ui/events/blink/prediction/empty_filter.cc +++ b/ui/events/blink/prediction/empty_filter.cc
@@ -10,9 +10,9 @@ EmptyFilter::EmptyFilter() {} EmptyFilter::~EmptyFilter() {} -bool EmptyFilter::Filter(const base::TimeTicks timestamp, +bool EmptyFilter::Filter(const base::TimeTicks& timestamp, gfx::PointF* position) const { - return true; + return position != nullptr; } const char* EmptyFilter::GetName() const {
diff --git a/ui/events/blink/prediction/empty_filter.h b/ui/events/blink/prediction/empty_filter.h index 4b8bdd5..ed661d0 100644 --- a/ui/events/blink/prediction/empty_filter.h +++ b/ui/events/blink/prediction/empty_filter.h
@@ -18,7 +18,7 @@ // Filters the position sent to the filter at a specific timestamp. // Returns true if the value is filtered, false otherwise. - bool Filter(const base::TimeTicks timestamp, + bool Filter(const base::TimeTicks& timestamp, gfx::PointF* position) const override; // Returns the name of the filter
diff --git a/ui/events/blink/prediction/filter_factory.cc b/ui/events/blink/prediction/filter_factory.cc index 65de8e9c..5928dd002 100644 --- a/ui/events/blink/prediction/filter_factory.cc +++ b/ui/events/blink/prediction/filter_factory.cc
@@ -3,28 +3,91 @@ // found in the LICENSE file. #include "ui/events/blink/prediction/filter_factory.h" +#include "base/metrics/field_trial.h" +#include "base/metrics/field_trial_params.h" +#include "base/strings/string_number_conversions.h" #include "ui/events/blink/prediction/empty_filter.h" +#include "ui/events/blink/prediction/one_euro_filter.h" +#include "ui/events/blink/prediction/predictor_factory.h" namespace ui { namespace input_prediction { const char kFilterNameEmpty[] = "empty_filter"; +const char kFilterNameOneEuro[] = "one_euro_filter"; } // namespace input_prediction namespace { using input_prediction::FilterType; +using input_prediction::PredictorType; } // namespace +FilterFactory::FilterFactory( + const base::Feature& feature, + const input_prediction::PredictorType predictor_type, + const input_prediction::FilterType filter_type) { + LoadFilterParams(feature, predictor_type, filter_type); +} + +FilterFactory::~FilterFactory() {} + +void FilterFactory::LoadFilterParams( + const base::Feature& feature, + const input_prediction::PredictorType predictor_type, + const input_prediction::FilterType filter_type) { + if (filter_type == FilterType::kOneEuro) { + base::FieldTrialParams one_euro_filter_param = { + {OneEuroFilter::kParamBeta, ""}, {OneEuroFilter::kParamMincutoff, ""}}; + double beta, mincutoff; + // Only save the params if they are given in the fieldtrials params + if (base::GetFieldTrialParamsByFeature(feature, &one_euro_filter_param) && + base::StringToDouble(one_euro_filter_param[OneEuroFilter::kParamBeta], + &beta) && + base::StringToDouble( + one_euro_filter_param[OneEuroFilter::kParamMincutoff], + &mincutoff)) { + FilterParamMapKey param_key = {FilterType::kOneEuro, predictor_type}; + FilterParams param_value = {{OneEuroFilter::kParamMincutoff, mincutoff}, + {OneEuroFilter::kParamBeta, beta}}; + filter_params_map_.insert(std::make_pair(param_key, param_value)); + } + } +} + FilterType FilterFactory::GetFilterTypeFromName( const std::string& filter_name) { - return FilterType::kEmpty; + if (filter_name == input_prediction::kFilterNameOneEuro) + return FilterType::kOneEuro; + else + return FilterType::kEmpty; } std::unique_ptr<InputFilter> FilterFactory::CreateFilter( - input_prediction::FilterType filter_type) { - return std::make_unique<EmptyFilter>(); + const FilterType filter_type, + const PredictorType predictor_type) { + FilterParams filter_params; + GetFilterParams(filter_type, predictor_type, &filter_params); + if (filter_type == FilterType::kOneEuro) { + if (filter_params.empty()) + return std::make_unique<OneEuroFilter>(); + else + return std::make_unique<OneEuroFilter>( + filter_params.find(OneEuroFilter::kParamMincutoff)->second, + filter_params.find(OneEuroFilter::kParamBeta)->second); + } else + return std::make_unique<EmptyFilter>(); +} + +void FilterFactory::GetFilterParams(const FilterType filter_type, + const PredictorType predictor_type, + FilterParams* filter_params) { + FilterParamMapKey key = {filter_type, predictor_type}; + auto params = filter_params_map_.find(key); + if (params != filter_params_map_.end()) { + *filter_params = params->second; + } } } // namespace ui
diff --git a/ui/events/blink/prediction/filter_factory.h b/ui/events/blink/prediction/filter_factory.h index 12da7406..42447b9 100644 --- a/ui/events/blink/prediction/filter_factory.h +++ b/ui/events/blink/prediction/filter_factory.h
@@ -5,38 +5,95 @@ #ifndef UI_EVENTS_BLINK_PREDICTION_FILTER_FACTORY_H_ #define UI_EVENTS_BLINK_PREDICTION_FILTER_FACTORY_H_ +#include "base/feature_list.h" #include "ui/events/blink/prediction/input_filter.h" +#include "ui/events/blink/prediction/predictor_factory.h" namespace ui { -namespace input_prediction { +namespace test { +class FilterFactoryTest; +} // namespace test +namespace input_prediction { extern const char kFilterNameEmpty[]; +extern const char kFilterNameOneEuro[]; enum class FilterType { kEmpty, + kOneEuro, }; } // namespace input_prediction -// FilterFactory is a class containing static public methods to create filters. +// Structure used as key in the unordered_map to store different filter params +// in function of a trio {Filter, Predictor, Feature} +struct FilterParamMapKey { + bool operator==(const FilterParamMapKey& other) const { + return filter_type == other.filter_type && + predictor_type == other.predictor_type; + } + input_prediction::FilterType filter_type; + input_prediction::PredictorType predictor_type; +}; + +// Used to compute a hash value for FilterParamMapKey so it can be used as key +// in a hashmap +struct FilterParamMapKeyHash { + std::size_t operator()(const FilterParamMapKey& k) const { + return std::hash<const input_prediction::FilterType>{}(k.filter_type) ^ + std::hash<const input_prediction::PredictorType>{}(k.predictor_type); + } +}; + +using FilterParams = std::unordered_map<std::string, double>; +using FilterParamsMap = + std::unordered_map<FilterParamMapKey, FilterParams, FilterParamMapKeyHash>; + +// FilterFactory is a class containing methods to create filters. // It defines filters name and type constants. It also reads filter settings // from fieldtrials if needed. class FilterFactory { public: + FilterFactory(const base::Feature& feature, + const input_prediction::PredictorType predictor_type, + const input_prediction::FilterType filter_type); + ~FilterFactory(); + // Returns the FilterType associated to the given filter // name if found, otherwise returns kFilterTypeEmpty - static input_prediction::FilterType GetFilterTypeFromName( + input_prediction::FilterType GetFilterTypeFromName( const std::string& filter_name); // Returns the filter designed by its type. - static std::unique_ptr<InputFilter> CreateFilter( - input_prediction::FilterType filter_type); + // Since filters can have different parameters in function of the current + // predictor used, it also needs to be given as parameter. + std::unique_ptr<InputFilter> CreateFilter( + const input_prediction::FilterType filter_type, + const input_prediction::PredictorType predictor_type); private: - FilterFactory() = delete; - ~FilterFactory() = delete; -}; + friend class test::FilterFactoryTest; + // Map storing filter parameters for a pair {FilterType, PredictorType}. + // Currently the map is only storing values from fieldtrials params, but + // default parameters might be added for a specific predictor/filter pair + // in the future. + FilterParamsMap filter_params_map_; + + // Initialize the filter_params_map_ with values from fieldtrials params for + // a given feature, predictor and filter. + // Might initialize some default values for specific predictor/filter pair in + // the future. + void LoadFilterParams(const base::Feature& feature, + const input_prediction::PredictorType predictor_type, + const input_prediction::FilterType filter_type); + + // Gets filter params for a specific key couple {FilterType, PredictorType} + // If params are found, update the given filter_params map. + void GetFilterParams(const input_prediction::FilterType filter_type, + const input_prediction::PredictorType predictor_type, + FilterParams* filter_params); +}; } // namespace ui #endif // UI_EVENTS_BLINK_PREDICTION_FILTER_FACTORY_H_ \ No newline at end of file
diff --git a/ui/events/blink/prediction/filter_factory_unittests.cc b/ui/events/blink/prediction/filter_factory_unittests.cc index 4cc7906..41e8aadf 100644 --- a/ui/events/blink/prediction/filter_factory_unittests.cc +++ b/ui/events/blink/prediction/filter_factory_unittests.cc
@@ -2,32 +2,135 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/test/scoped_feature_list.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/blink/blink_features.h" #include "ui/events/blink/prediction/filter_factory.h" +#include "ui/events/blink/prediction/one_euro_filter.h" namespace ui { namespace test { +namespace { +using base::Feature; +using input_prediction::FilterType; +using input_prediction::PredictorType; +} // namespace + class FilterFactoryTest : public testing::Test { public: - explicit FilterFactoryTest() {} + FilterFactoryTest() { + CreateNewFactory(features::kFilteringScrollPrediction, + PredictorType::kScrollPredictorTypeKalman, + FilterType::kEmpty); + } + + void GetFilterParams(const FilterType& filter_type, + const PredictorType& predictor_type, + FilterParams* filter_params) { + factory_->GetFilterParams(filter_type, predictor_type, filter_params); + } + + FilterType GetFilterTypeFromName(const std::string& filter_name) { + return factory_->GetFilterTypeFromName(filter_name); + } + + std::unique_ptr<InputFilter> CreateFilter( + const input_prediction::FilterType filter_type, + const input_prediction::PredictorType predictor_type) { + return factory_->CreateFilter(filter_type, predictor_type); + } + + void CreateNewFactory(const base::Feature& feature, + const input_prediction::PredictorType predictor_type, + const input_prediction::FilterType filter_type) { + factory_ = + std::make_unique<FilterFactory>(feature, predictor_type, filter_type); + } + + private: + std::unique_ptr<FilterFactory> factory_; DISALLOW_COPY_AND_ASSIGN(FilterFactoryTest); }; // Check if the FilterType returned is correct TEST_F(FilterFactoryTest, TestGetFilterType) { - EXPECT_EQ( - input_prediction::FilterType::kEmpty, - FilterFactory::GetFilterTypeFromName(input_prediction::kFilterNameEmpty)); - // Default type Empty EXPECT_EQ(input_prediction::FilterType::kEmpty, - FilterFactory::GetFilterTypeFromName("")); + GetFilterTypeFromName(input_prediction::kFilterNameEmpty)); + + EXPECT_EQ(input_prediction::FilterType::kOneEuro, + GetFilterTypeFromName(input_prediction::kFilterNameOneEuro)); + + // Default type Empty + EXPECT_EQ(input_prediction::FilterType::kEmpty, GetFilterTypeFromName("")); +} + +TEST_F(FilterFactoryTest, TestCreateFilter) { + EXPECT_STREQ( + input_prediction::kFilterNameEmpty, + CreateFilter(input_prediction::FilterType::kEmpty, + input_prediction::PredictorType::kScrollPredictorTypeEmpty) + ->GetName()); + + EXPECT_STREQ( + input_prediction::kFilterNameOneEuro, + CreateFilter(input_prediction::FilterType::kOneEuro, + input_prediction::PredictorType::kScrollPredictorTypeEmpty) + ->GetName()); +} + +// Test there is no params available for OneEuro filter +TEST_F(FilterFactoryTest, TestOneEuroNoParams) { + FilterParams filter_params; + + GetFilterParams(FilterType::kOneEuro, + PredictorType::kScrollPredictorTypeKalman, &filter_params); + EXPECT_TRUE(filter_params.empty()); +} + +// Test we get the params sent via fieldtrials params +TEST_F(FilterFactoryTest, TestOneEuroParams) { + FilterParams filter_params; + + base::test::ScopedFeatureList scoped_feature_list; + base::FieldTrialParams field_trial_params; + + field_trial_params[OneEuroFilter::kParamMincutoff] = "33"; + field_trial_params[OneEuroFilter::kParamBeta] = "42"; + scoped_feature_list.Reset(); + scoped_feature_list.InitAndEnableFeatureWithParameters( + features::kFilteringScrollPrediction, field_trial_params); + + // Create a new factory to load fieldtrials params values + CreateNewFactory(features::kFilteringScrollPrediction, + PredictorType::kScrollPredictorTypeKalman, + FilterType::kOneEuro); + + GetFilterParams(FilterType::kOneEuro, + PredictorType::kScrollPredictorTypeKalman, &filter_params); + + EXPECT_EQ((int)filter_params.size(), 2); + EXPECT_EQ(filter_params.find(OneEuroFilter::kParamMincutoff)->second, 33); + EXPECT_EQ(filter_params.find(OneEuroFilter::kParamBeta)->second, 42); + + // fieldtrials params shouldn't be available for another predictor + filter_params.clear(); + GetFilterParams(FilterType::kOneEuro, PredictorType::kScrollPredictorTypeLsq, + &filter_params); + + EXPECT_TRUE(filter_params.empty()); } TEST_F(FilterFactoryTest, TestGetFilter) { - EXPECT_STREQ(input_prediction::kFilterNameEmpty, - FilterFactory::CreateFilter(input_prediction::FilterType::kEmpty) + EXPECT_STREQ( + input_prediction::kFilterNameEmpty, + CreateFilter(FilterType::kEmpty, PredictorType::kScrollPredictorTypeEmpty) + ->GetName()); + + EXPECT_STREQ(input_prediction::kFilterNameOneEuro, + CreateFilter(FilterType::kOneEuro, + PredictorType::kScrollPredictorTypeEmpty) ->GetName()); }
diff --git a/ui/events/blink/prediction/input_filter.h b/ui/events/blink/prediction/input_filter.h index 68aab232..264d58dc 100644 --- a/ui/events/blink/prediction/input_filter.h +++ b/ui/events/blink/prediction/input_filter.h
@@ -18,7 +18,7 @@ // Filters the position sent to the filter at a specific timestamp. // Returns true if the value is filtered, false otherwise. - virtual bool Filter(const base::TimeTicks timestamp, + virtual bool Filter(const base::TimeTicks& timestamp, gfx::PointF* position) const = 0; // Returns the name of the filter
diff --git a/ui/events/blink/prediction/one_euro_filter.cc b/ui/events/blink/prediction/one_euro_filter.cc new file mode 100644 index 0000000..8e14dbe --- /dev/null +++ b/ui/events/blink/prediction/one_euro_filter.cc
@@ -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. + +#include "ui/events/blink/prediction/one_euro_filter.h" +#include "ui/events/blink/prediction/filter_factory.h" + +namespace ui { + +const double OneEuroFilter::kDefaultFrequency; +const double OneEuroFilter::kDefaultMincutoff; +const double OneEuroFilter::kDefaultBeta; +const double OneEuroFilter::kDefaultDcutoff; + +const char OneEuroFilter::kParamBeta[]; +const char OneEuroFilter::kParamMincutoff[]; + +OneEuroFilter::OneEuroFilter(double mincutoff, double beta) { + x_filter_ = std::make_unique<one_euro_filter::OneEuroFilter>( + kDefaultFrequency, mincutoff, beta, kDefaultDcutoff); + y_filter_ = std::make_unique<one_euro_filter::OneEuroFilter>( + kDefaultFrequency, mincutoff, beta, kDefaultDcutoff); +} + +OneEuroFilter::~OneEuroFilter() {} + +bool OneEuroFilter::Filter(const base::TimeTicks& timestamp, + gfx::PointF* position) const { + if (position == nullptr) + return false; + one_euro_filter::TimeStamp ts = (timestamp - base::TimeTicks()).InSecondsF(); + position->set_x(x_filter_->Filter(position->x(), ts)); + position->set_y(y_filter_->Filter(position->y(), ts)); + return true; +} + +const char* OneEuroFilter::GetName() const { + return input_prediction::kFilterNameOneEuro; +} + +InputFilter* OneEuroFilter::Clone() { + OneEuroFilter* new_filter = new OneEuroFilter(); + new_filter->x_filter_.reset(x_filter_->Clone()); + new_filter->y_filter_.reset(y_filter_->Clone()); + return new_filter; +} + +void OneEuroFilter::Reset() { + x_filter_->Reset(); + y_filter_->Reset(); +} + +} // namespace ui \ No newline at end of file
diff --git a/ui/events/blink/prediction/one_euro_filter.h b/ui/events/blink/prediction/one_euro_filter.h new file mode 100644 index 0000000..480b0b69 --- /dev/null +++ b/ui/events/blink/prediction/one_euro_filter.h
@@ -0,0 +1,50 @@ +// 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 UI_EVENTS_BLINK_PREDICTION_ONE_EURO_FILTER_H_ +#define UI_EVENTS_BLINK_PREDICTION_ONE_EURO_FILTER_H_ + +#include "third_party/one_euro_filter/src/one_euro_filter.h" +#include "ui/events/blink/prediction/input_filter.h" + +namespace ui { + +// This class uses the 1€ filter from third party. +// See this page : http://cristal.univ-lille.fr/~casiez/1euro/ +// to know how the filter works and how to tune it +class OneEuroFilter : public InputFilter { + public: + OneEuroFilter(double mincutoff = kDefaultMincutoff, + double beta = kDefaultBeta); + ~OneEuroFilter() override; + + bool Filter(const base::TimeTicks& timestamp, + gfx::PointF* position) const override; + + const char* GetName() const override; + + InputFilter* Clone() override; + + void Reset() override; + + // Default parameters values for the filter + static constexpr double kDefaultFrequency = 60; + static constexpr double kDefaultMincutoff = 1.0; + static constexpr double kDefaultBeta = 0.001; + static constexpr double kDefaultDcutoff = 1.0; + + // Names of the fieldtrials used to tune the filter + static constexpr char kParamBeta[] = "beta"; + static constexpr char kParamMincutoff[] = "mincutoff"; + + private: + std::unique_ptr<one_euro_filter::OneEuroFilter> x_filter_; + std::unique_ptr<one_euro_filter::OneEuroFilter> y_filter_; + + DISALLOW_COPY_AND_ASSIGN(OneEuroFilter); +}; + +} // namespace ui + +#endif // UI_EVENTS_BLINK_PREDICTION_ONE_EURO_FILTER_H_ \ No newline at end of file
diff --git a/ui/events/blink/prediction/one_euro_filter_unittests.cc b/ui/events/blink/prediction/one_euro_filter_unittests.cc new file mode 100644 index 0000000..7131a71a --- /dev/null +++ b/ui/events/blink/prediction/one_euro_filter_unittests.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 "base/rand_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/blink/prediction/filter_factory.h" +#include "ui/events/blink/prediction/input_filter_unittest_helpers.h" +#include "ui/events/blink/prediction/one_euro_filter.h" + +namespace ui { +namespace test { + +class OneEuroFilterTest : public InputFilterTest { + public: + explicit OneEuroFilterTest() {} + + void SetUp() override { filter_ = std::make_unique<ui::OneEuroFilter>(1, 1); } + + DISALLOW_COPY_AND_ASSIGN(OneEuroFilterTest); +}; + +TEST_F(OneEuroFilterTest, TestClone) { + TestCloneFilter(); +} + +TEST_F(OneEuroFilterTest, TestReset) { + TestResetFilter(); +} + +// Check if sending values between 0 and 1 keeps filtered values between 0 and 1 +TEST_F(OneEuroFilterTest, filteringValues) { + base::TimeTicks ts = blink::WebInputEvent::GetStaticTimeStampForTests(); + gfx::PointF point; + for (int i = 0; i < 100; i++) { + point.SetPoint(base::RandDouble(), base::RandDouble()); + EXPECT_TRUE(filter_->Filter(ts, &point)); + EXPECT_LT(point.x(), 1.0); + EXPECT_LT(point.y(), 1.0); + EXPECT_GT(point.x(), 0.0); + EXPECT_GT(point.y(), 0.0); + } +} + +} // namespace test +} // namespace ui \ No newline at end of file
diff --git a/ui/events/blink/scroll_predictor.cc b/ui/events/blink/scroll_predictor.cc index e973aac..09d2c48 100644 --- a/ui/events/blink/scroll_predictor.cc +++ b/ui/events/blink/scroll_predictor.cc
@@ -8,7 +8,6 @@ #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" #include "base/trace_event/trace_event.h" -#include "ui/events/blink/prediction/filter_factory.h" #include "ui/events/blink/prediction/predictor_factory.h" using blink::WebInputEvent; @@ -34,8 +33,12 @@ features::kFilteringScrollPrediction, "filter"); input_prediction::FilterType filter_type = - ui::FilterFactory::GetFilterTypeFromName(filter_name); - filter_ = ui::FilterFactory::CreateFilter(filter_type); + filter_factory_->GetFilterTypeFromName(filter_name); + + filter_factory_ = std::make_unique<FilterFactory>( + features::kFilteringScrollPrediction, predictor_type, filter_type); + + filter_ = filter_factory_->CreateFilter(filter_type, predictor_type); } }
diff --git a/ui/events/blink/scroll_predictor.h b/ui/events/blink/scroll_predictor.h index 08d50cb5..b9b7d44 100644 --- a/ui/events/blink/scroll_predictor.h +++ b/ui/events/blink/scroll_predictor.h
@@ -9,7 +9,7 @@ #include "ui/events/base_event_utils.h" #include "ui/events/blink/event_with_callback.h" -#include "ui/events/blink/prediction/input_filter.h" +#include "ui/events/blink/prediction/filter_factory.h" #include "ui/events/blink/prediction/input_predictor.h" namespace ui { @@ -63,6 +63,8 @@ std::unique_ptr<InputPredictor> predictor_; std::unique_ptr<InputFilter> filter_; + std::unique_ptr<FilterFactory> filter_factory_; + // Whether predicted scroll events should be filtered or not bool filtering_enabled_ = false;
diff --git a/ui/events/blink/scroll_predictor_unittest.cc b/ui/events/blink/scroll_predictor_unittest.cc index 568d57f96..e944b97f 100644 --- a/ui/events/blink/scroll_predictor_unittest.cc +++ b/ui/events/blink/scroll_predictor_unittest.cc
@@ -111,23 +111,20 @@ bool isFilteringEnabled() { return scroll_predictor_->filtering_enabled_; } - void ConfigurePredictorFieldTrial(const base::Feature& feature, - const std::string& predictor_type) { + void ConfigurePredictorFieldTrialAndInitialize( + const base::Feature& feature, + const std::string& predictor_type) { base::FieldTrialParams params; params["predictor"] = predictor_type; - scoped_feature_list_.Reset(); scoped_feature_list_.InitAndEnableFeatureWithParameters(feature, params); EXPECT_EQ(params["predictor"], GetFieldTrialParamValueByFeature(feature, "predictor")); + scroll_predictor_ = std::make_unique<ScrollPredictor>(); } - void VerifyPredictorType(const char* expected_type) { - EXPECT_EQ(expected_type, scroll_predictor_->predictor_->GetName()); - } - - void ConfigureFilterFieldTrial(const base::Feature& feature, - const std::string& filter_name) { + void ConfigureFilterFieldTrialAndInitialize(const base::Feature& feature, + const std::string& filter_name) { base::FieldTrialParams params; params["filter"] = filter_name; @@ -135,6 +132,38 @@ scoped_feature_list_.InitAndEnableFeatureWithParameters(feature, params); EXPECT_EQ(params["filter"], GetFieldTrialParamValueByFeature(feature, "filter")); + scroll_predictor_ = std::make_unique<ScrollPredictor>(); + } + + void ConfigurePredictorAndFilterFieldTrialAndInitialize( + const base::Feature& pred_feature, + const std::string& predictor_type, + const base::Feature& filter_feature, + const std::string& filter_type) { + base::FieldTrialParams pred_field_params; + pred_field_params["predictor"] = predictor_type; + base::test::ScopedFeatureList::FeatureAndParams prediction_params = { + pred_feature, pred_field_params}; + + base::FieldTrialParams filter_field_params; + filter_field_params["filter"] = filter_type; + base::test::ScopedFeatureList::FeatureAndParams filter_params = { + filter_feature, filter_field_params}; + + scoped_feature_list_.Reset(); + scoped_feature_list_.InitWithFeaturesAndParameters( + {prediction_params, filter_params}, {}); + + EXPECT_EQ(pred_field_params["predictor"], + GetFieldTrialParamValueByFeature(pred_feature, "predictor")); + EXPECT_EQ(filter_field_params["filter"], + GetFieldTrialParamValueByFeature(filter_feature, "filter")); + + scroll_predictor_ = std::make_unique<ScrollPredictor>(); + } + + void VerifyPredictorType(const char* expected_type) { + EXPECT_EQ(expected_type, scroll_predictor_->predictor_->GetName()); } void VerifyFilterType(const char* expected_type) { @@ -397,40 +426,43 @@ // When resampling is enabled, predictor type is set from // kResamplingScrollEvents. - ConfigurePredictorFieldTrial(features::kResamplingScrollEvents, - input_prediction::kScrollPredictorNameEmpty); - scroll_predictor_ = std::make_unique<ScrollPredictor>(); + ConfigurePredictorFieldTrialAndInitialize( + features::kResamplingScrollEvents, + input_prediction::kScrollPredictorNameEmpty); VerifyPredictorType(input_prediction::kScrollPredictorNameEmpty); - ConfigurePredictorFieldTrial(features::kResamplingScrollEvents, - input_prediction::kScrollPredictorNameLsq); - scroll_predictor_ = std::make_unique<ScrollPredictor>(); + ConfigurePredictorFieldTrialAndInitialize( + features::kResamplingScrollEvents, + input_prediction::kScrollPredictorNameLsq); VerifyPredictorType(input_prediction::kScrollPredictorNameLsq); - ConfigurePredictorFieldTrial(features::kResamplingScrollEvents, - input_prediction::kScrollPredictorNameKalman); - scroll_predictor_ = std::make_unique<ScrollPredictor>(); + ConfigurePredictorFieldTrialAndInitialize( + features::kResamplingScrollEvents, + input_prediction::kScrollPredictorNameKalman); VerifyPredictorType(input_prediction::kScrollPredictorNameKalman); - ConfigurePredictorFieldTrial( + ConfigurePredictorFieldTrialAndInitialize( features::kResamplingScrollEvents, input_prediction::kScrollPredictorNameLinearFirst); - scroll_predictor_ = std::make_unique<ScrollPredictor>(); VerifyPredictorType(input_prediction::kScrollPredictorNameLinearFirst); } // Check the right filter is selected TEST_F(ScrollPredictorTest, DefaultFilter) { - ConfigureFilterFieldTrial(features::kFilteringScrollPrediction, ""); - scroll_predictor_ = std::make_unique<ScrollPredictor>(); + ConfigureFilterFieldTrialAndInitialize(features::kFilteringScrollPrediction, + ""); VerifyFilterType(input_prediction::kFilterNameEmpty); EXPECT_TRUE(isFilteringEnabled()); - ConfigureFilterFieldTrial(features::kFilteringScrollPrediction, - input_prediction::kFilterNameEmpty); - scroll_predictor_ = std::make_unique<ScrollPredictor>(); + ConfigureFilterFieldTrialAndInitialize(features::kFilteringScrollPrediction, + input_prediction::kFilterNameEmpty); VerifyFilterType(input_prediction::kFilterNameEmpty); EXPECT_TRUE(isFilteringEnabled()); + + ConfigureFilterFieldTrialAndInitialize(features::kFilteringScrollPrediction, + input_prediction::kFilterNameOneEuro); + VerifyFilterType(input_prediction::kFilterNameOneEuro); + EXPECT_TRUE(isFilteringEnabled()); } // We first send 100 events to the scroll predictor with kalman predictor @@ -438,9 +470,9 @@ // We then send the same events with kalman and the empty filter, we should // expect the same results. TEST_F(ScrollPredictorTest, FilteringPrediction) { - ConfigureFilterFieldTrial(features::kResamplingScrollEvents, - input_prediction::kScrollPredictorNameKalman); - scroll_predictor_ = std::make_unique<ScrollPredictor>(); + ConfigurePredictorFieldTrialAndInitialize( + features::kResamplingScrollEvents, + input_prediction::kScrollPredictorNameKalman); std::vector<double> accumulated_deltas; WebScopedInputEvent gesture_update; @@ -456,10 +488,10 @@ EXPECT_EQ((int)accumulated_deltas.size(), 100); // Now we enable filtering and compare the deltas - ConfigurePredictorFieldTrial(features::kResamplingScrollEvents, - input_prediction::kScrollPredictorNameKalman); - ConfigureFilterFieldTrial(features::kFilteringScrollPrediction, - input_prediction::kFilterNameEmpty); + ConfigurePredictorAndFilterFieldTrialAndInitialize( + features::kResamplingScrollEvents, + input_prediction::kScrollPredictorNameKalman, + features::kFilteringScrollPrediction, input_prediction::kFilterNameEmpty); scroll_predictor_ = std::make_unique<ScrollPredictor>(); for (int i = 0; i < 100; i++) {
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn index fd72f31..877db007 100644 --- a/ui/gl/BUILD.gn +++ b/ui/gl/BUILD.gn
@@ -4,6 +4,7 @@ import("//build/buildflag_header.gni") import("//build/config/chrome_build.gni") +import("//build/config/chromecast_build.gni") import("//build/config/jumbo.gni") import("//build/config/linux/pkg_config.gni") import("//build/config/ui.gni") @@ -13,7 +14,7 @@ declare_args() { enable_swiftshader = (is_win || is_linux || (is_mac && use_egl) || - is_chromeos || is_fuchsia) && + is_chromeos || is_fuchsia) && !is_cast_audio_only && (target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" || target_cpu == "arm64" || target_cpu == "mipsel" || target_cpu == "mips64el")
diff --git a/ui/gl/OWNERS b/ui/gl/OWNERS index 6502886..f59dfd6 100644 --- a/ui/gl/OWNERS +++ b/ui/gl/OWNERS
@@ -1,5 +1,6 @@ backer@chromium.org kbr@chromium.org +sunnyps@chromium.org zmo@chromium.org per-file *gl_image*=reveman@chromium.org per-file *gl_image*=dcastagna@chromium.org
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h index cd7070b3..a5066b03 100644 --- a/ui/native_theme/native_theme.h +++ b/ui/native_theme/native_theme.h
@@ -7,6 +7,7 @@ #include <map> +#include "base/containers/flat_map.h" #include "base/macros.h" #include "base/observer_list.h" #include "build/build_config.h"
diff --git a/ui/ozone/platform/headless/BUILD.gn b/ui/ozone/platform/headless/BUILD.gn index 635cc8f..c71628cf 100644 --- a/ui/ozone/platform/headless/BUILD.gn +++ b/ui/ozone/platform/headless/BUILD.gn
@@ -31,6 +31,7 @@ "//ui/events/ozone:events_ozone_layout", "//ui/events/platform", "//ui/gfx/geometry", + "//ui/gl:buildflags", "//ui/ozone:ozone_base", "//ui/ozone/common", "//ui/platform_window",
diff --git a/ui/ozone/platform/headless/headless_surface_factory.cc b/ui/ozone/platform/headless/headless_surface_factory.cc index 4ec93cec..7218de3a3 100644 --- a/ui/ozone/platform/headless/headless_surface_factory.cc +++ b/ui/ozone/platform/headless/headless_surface_factory.cc
@@ -20,6 +20,7 @@ #include "ui/gfx/native_pixmap.h" #include "ui/gfx/skia_util.h" #include "ui/gfx/vsync_provider.h" +#include "ui/gl/buildflags.h" #include "ui/gl/gl_surface_egl.h" #include "ui/ozone/common/egl_util.h" #include "ui/ozone/common/gl_ozone_egl.h" @@ -174,7 +175,11 @@ std::vector<gl::GLImplementation> HeadlessSurfaceFactory::GetAllowedGLImplementations() { - return std::vector<gl::GLImplementation>{gl::kGLImplementationSwiftShaderGL}; + return std::vector<gl::GLImplementation> { +#if BUILDFLAG(ENABLE_SWIFTSHADER) + gl::kGLImplementationSwiftShaderGL, +#endif + }; } GLOzone* HeadlessSurfaceFactory::GetGLOzone(
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index 9a434f2..278263d 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn
@@ -204,7 +204,6 @@ "layout/flex_layout.h", "layout/flex_layout_types.h", "layout/grid_layout.h", - "layout/interpolating_layout_manager.h", "layout/layout_manager.h", "layout/layout_manager_base.h", "layout/layout_provider.h", @@ -413,7 +412,6 @@ "layout/flex_layout_types.cc", "layout/flex_layout_types_internal.cc", "layout/grid_layout.cc", - "layout/interpolating_layout_manager.cc", "layout/layout_manager.cc", "layout/layout_manager_base.cc", "layout/layout_provider.cc", @@ -1050,7 +1048,6 @@ "layout/flex_layout_types_internal_unittest.cc", "layout/flex_layout_unittest.cc", "layout/grid_layout_unittest.cc", - "layout/interpolating_layout_manager_unittest.cc", "layout/layout_manager_base_unittest.cc", "metadata/metadata_unittest.cc", "metadata/type_conversion_unittest.cc",
diff --git a/ui/views/accessible_pane_view.h b/ui/views/accessible_pane_view.h index 1c70cd36d..813fd13 100644 --- a/ui/views/accessible_pane_view.h +++ b/ui/views/accessible_pane_view.h
@@ -35,7 +35,7 @@ // If |initial_focus| is not NULL, that control will get // the initial focus, if it's enabled and focusable. Returns true if // the pane was able to receive focus. - virtual bool SetPaneFocus(View* initial_focus); + bool SetPaneFocus(View* initial_focus); bool pane_has_focus() const { return pane_has_focus_; } @@ -75,15 +75,15 @@ // Returns the parent of |v|. Subclasses can override this if // they need custom focus search behavior. - virtual View* GetParentForFocusSearch(View* v); + View* GetParentForFocusSearch(View* v); // Returns true if |v| is contained within the hierarchy rooted at |root| // for the purpose of focus searching. Subclasses can override this if // they need custom focus search behavior. - virtual bool ContainsForFocusSearch(View* root, const View* v); + bool ContainsForFocusSearch(View* root, const View* v); // Remove pane focus. - virtual void RemovePaneFocus(); + void RemovePaneFocus(); View* GetFirstFocusableChild(); View* GetLastFocusableChild();
diff --git a/ui/views/controls/button/label_button.h b/ui/views/controls/button/label_button.h index c183274e..2162f9e 100644 --- a/ui/views/controls/button/label_button.h +++ b/ui/views/controls/button/label_button.h
@@ -134,13 +134,13 @@ // Returns the available area for the label and image. Subclasses can change // these bounds if they need room to do manual painting. - virtual gfx::Rect GetChildAreaBounds(); + gfx::Rect GetChildAreaBounds(); // Fills |params| with information about the button. virtual void GetExtraParams(ui::NativeTheme::ExtraParams* params) const; // Resets colors from the NativeTheme, explicitly set colors are unchanged. - virtual void ResetColorsFromNativeTheme(); + void ResetColorsFromNativeTheme(); // Changes the visual styling to match changes in the default state. Returns // the PropertyEffects triggered as a result.
diff --git a/ui/views/layout/flex_layout_types.h b/ui/views/layout/flex_layout_types.h index 4c6d71a..1e6c1e06 100644 --- a/ui/views/layout/flex_layout_types.h +++ b/ui/views/layout/flex_layout_types.h
@@ -128,9 +128,9 @@ }; // Represents insets in a single dimension. -class Inset1D { +class VIEWS_EXPORT Inset1D { public: - constexpr Inset1D() = default; + constexpr Inset1D() {} constexpr explicit Inset1D(int all) : leading_(all), trailing_(all) {} constexpr Inset1D(int leading, int trailing) : leading_(leading), trailing_(trailing) {} @@ -159,9 +159,9 @@ }; // Represents a line segment in one dimension with a starting point and length. -class Span { +class VIEWS_EXPORT Span { public: - constexpr Span() = default; + constexpr Span() {} constexpr Span(int start, int length) : start_(start), length_(length) {} constexpr int start() const { return start_; }
diff --git a/ui/views/layout/layout_manager_base.cc b/ui/views/layout/layout_manager_base.cc index 1992d5c..261e101c 100644 --- a/ui/views/layout/layout_manager_base.cc +++ b/ui/views/layout/layout_manager_base.cc
@@ -11,42 +11,59 @@ namespace views { +bool LayoutManagerBase::ChildLayout::operator==( + const ChildLayout& other) const { + // Note: if the view is not visible, the bounds do not matter as they will not + // be set. + return child_view == other.child_view && visible == other.visible && + (!visible || bounds == other.bounds); +} + LayoutManagerBase::ProposedLayout::ProposedLayout() = default; LayoutManagerBase::ProposedLayout::ProposedLayout(const ProposedLayout& other) = default; LayoutManagerBase::ProposedLayout::ProposedLayout(ProposedLayout&& other) = default; +LayoutManagerBase::ProposedLayout::ProposedLayout( + const gfx::Size& size, + const std::initializer_list<ChildLayout>& children) + : host_size(size), child_layouts(children) {} LayoutManagerBase::ProposedLayout::~ProposedLayout() = default; LayoutManagerBase::ProposedLayout& LayoutManagerBase::ProposedLayout::operator=( const ProposedLayout& other) = default; LayoutManagerBase::ProposedLayout& LayoutManagerBase::ProposedLayout::operator=( ProposedLayout&& other) = default; +bool LayoutManagerBase::ProposedLayout::operator==( + const ProposedLayout& other) const { + return host_size == other.host_size && child_layouts == other.child_layouts; +} + LayoutManagerBase::~LayoutManagerBase() = default; gfx::Size LayoutManagerBase::GetPreferredSize(const View* host) const { DCHECK_EQ(host_view_, host); - if (!preferred_size_) - preferred_size_ = CalculateProposedLayout(SizeBounds()).host_size; - return *preferred_size_; + if (!cached_preferred_size_) + cached_preferred_size_ = CalculateProposedLayout(SizeBounds()).host_size; + return *cached_preferred_size_; } gfx::Size LayoutManagerBase::GetMinimumSize(const View* host) const { DCHECK_EQ(host_view_, host); - if (!minimum_size_) - minimum_size_ = CalculateProposedLayout(SizeBounds(0, 0)).host_size; - return *minimum_size_; + if (!cached_minimum_size_) + cached_minimum_size_ = CalculateProposedLayout(SizeBounds(0, 0)).host_size; + return *cached_minimum_size_; } int LayoutManagerBase::GetPreferredHeightForWidth(const View* host, int width) const { - if (!last_height_for_width_ || last_height_for_width_->width() != width) { + if (!cached_height_for_width_ || cached_height_for_width_->width() != width) { const int height = CalculateProposedLayout(SizeBounds(width, base::nullopt)) .host_size.height(); - last_height_for_width_ = gfx::Size(width, height); + cached_height_for_width_ = gfx::Size(width, height); } - return last_height_for_width_->height(); + return cached_height_for_width_->height(); } void LayoutManagerBase::Layout(View* host) { @@ -56,19 +73,19 @@ } void LayoutManagerBase::InvalidateLayout() { - minimum_size_.reset(); - preferred_size_.reset(); - last_height_for_width_.reset(); - last_requested_size_.reset(); + cached_minimum_size_.reset(); + cached_preferred_size_.reset(); + cached_height_for_width_.reset(); + cached_layout_size_.reset(); } LayoutManagerBase::ProposedLayout LayoutManagerBase::GetProposedLayout( const gfx::Size& host_size) const { - if (!last_requested_size_ || *last_requested_size_ != host_size) { - last_requested_size_ = host_size; - last_layout_ = CalculateProposedLayout(SizeBounds(host_size)); + if (cached_layout_size_ != host_size) { + cached_layout_size_ = host_size; + cached_layout_ = CalculateProposedLayout(SizeBounds(host_size)); } - return last_layout_; + return cached_layout_; } void LayoutManagerBase::SetChildViewIgnoredByLayout(View* child_view, @@ -138,7 +155,13 @@ bool LayoutManagerBase::IsChildIncludedInLayout(const View* child) const { const auto it = child_infos_.find(child); - DCHECK(it != child_infos_.end()); + + // During callbacks when a child is removed we can get in a state where a view + // in the child list of the host view is not in |child_infos_|. In that case, + // the view is being removed and is not part of the layout. + if (it == child_infos_.end()) + return false; + return !it->second.ignored && it->second.can_be_visible; }
diff --git a/ui/views/layout/layout_manager_base.h b/ui/views/layout/layout_manager_base.h index 5ff97926..6b7b22f 100644 --- a/ui/views/layout/layout_manager_base.h +++ b/ui/views/layout/layout_manager_base.h
@@ -31,9 +31,14 @@ // Represents layout information for a child view within a host being laid // out. struct VIEWS_EXPORT ChildLayout { + bool operator==(const ChildLayout& other) const; + bool operator!=(const ChildLayout& other) const { + return !(*this == other); + } + View* child_view = nullptr; - gfx::Rect bounds; bool visible = false; + gfx::Rect bounds; }; // Contains a full layout specification for the children of the host view. @@ -42,9 +47,16 @@ ~ProposedLayout(); ProposedLayout(const ProposedLayout& other); ProposedLayout(ProposedLayout&& other); + ProposedLayout(const gfx::Size& size, + const std::initializer_list<ChildLayout>& children); ProposedLayout& operator=(const ProposedLayout& other); ProposedLayout& operator=(ProposedLayout&& other); + bool operator==(const ProposedLayout& other) const; + bool operator!=(const ProposedLayout& other) const { + return !(*this == other); + } + // The size of the host view given the size bounds for this layout. If both // dimensions of the size bounds are specified, this will be the same size. gfx::Size host_size; @@ -83,6 +95,41 @@ protected: LayoutManagerBase(); + // Direct cache control for subclasses that want to override default caching + // behavior. Use at your own risk. + base::Optional<gfx::Size> cached_minimum_size() const { + return cached_minimum_size_; + } + void set_cached_minimum_size( + const base::Optional<gfx::Size>& minimum_size) const { + cached_minimum_size_ = minimum_size; + } + const base::Optional<gfx::Size>& cached_preferred_size() const { + return cached_preferred_size_; + } + void set_cached_preferred_size( + const base::Optional<gfx::Size>& preferred_size) const { + cached_preferred_size_ = preferred_size; + } + const base::Optional<gfx::Size>& cached_height_for_width() const { + return cached_height_for_width_; + } + void set_cached_height_for_width( + const base::Optional<gfx::Size>& height_for_width) const { + cached_height_for_width_ = height_for_width; + } + const base::Optional<gfx::Size>& cached_layout_size() const { + return cached_layout_size_; + } + void set_cached_layout_size( + const base::Optional<gfx::Size>& layout_size) const { + cached_layout_size_ = layout_size; + } + const ProposedLayout& cached_layout() const { return cached_layout_; } + void set_cached_layout(const ProposedLayout& layout) const { + cached_layout_ = layout; + } + bool IsChildIncludedInLayout(const View* child) const; // Creates a proposed layout for the host view, including bounds and @@ -111,11 +158,11 @@ // Do some really simple caching because layout generation can cost as much // as 1ms or more for complex views. - mutable base::Optional<gfx::Size> minimum_size_; - mutable base::Optional<gfx::Size> preferred_size_; - mutable base::Optional<gfx::Size> last_height_for_width_; - mutable base::Optional<gfx::Size> last_requested_size_; - mutable ProposedLayout last_layout_; + mutable base::Optional<gfx::Size> cached_minimum_size_; + mutable base::Optional<gfx::Size> cached_preferred_size_; + mutable base::Optional<gfx::Size> cached_height_for_width_; + mutable base::Optional<gfx::Size> cached_layout_size_; + mutable ProposedLayout cached_layout_; DISALLOW_COPY_AND_ASSIGN(LayoutManagerBase); };
diff --git a/ui/views/layout/layout_manager_base_unittest.cc b/ui/views/layout/layout_manager_base_unittest.cc index 36e39551a..eb333a2 100644 --- a/ui/views/layout/layout_manager_base_unittest.cc +++ b/ui/views/layout/layout_manager_base_unittest.cc
@@ -164,7 +164,7 @@ layout.host_size.set_height(std::max( layout.host_size.height(), bounds.bottom() + kChildViewPadding)); } - layout.child_layouts.push_back({*it, bounds, visible}); + layout.child_layouts.push_back({*it, visible, bounds}); } ++num_layouts_generated_; return layout; @@ -225,12 +225,9 @@ // Set the child visibility and bounds. constexpr gfx::Rect kChild1Bounds(3, 4, 10, 15); constexpr gfx::Rect kChild3Bounds(20, 21, 12, 14); - layout.child_layouts.push_back( - LayoutManagerBase::ChildLayout{child(0), kChild1Bounds, true}); - layout.child_layouts.push_back( - LayoutManagerBase::ChildLayout{child(1), gfx::Rect(), false}); - layout.child_layouts.push_back( - LayoutManagerBase::ChildLayout{child(2), kChild3Bounds, true}); + layout.child_layouts.push_back({child(0), true, kChild1Bounds}); + layout.child_layouts.push_back({child(1), false}); + layout.child_layouts.push_back({child(2), true, kChild3Bounds}); layout_manager()->ApplyLayout(layout); @@ -251,10 +248,8 @@ // Set the child visibility and bounds. constexpr gfx::Rect kChild1Bounds(3, 4, 10, 15); constexpr gfx::Rect kChild2Bounds(1, 2, 3, 4); - layout.child_layouts.push_back( - LayoutManagerBase::ChildLayout{child(0), kChild1Bounds, true}); - layout.child_layouts.push_back( - LayoutManagerBase::ChildLayout{child(2), gfx::Rect(), false}); + layout.child_layouts.push_back({child(0), true, kChild1Bounds}); + layout.child_layouts.push_back({child(2), false}); // We'll set the second child separately. child(1)->SetVisible(true); @@ -476,4 +471,39 @@ delete child_view; } +TEST(LayoutManagerBase_ProposedLayoutTest, Equality) { + View* ptr0 = nullptr; + View* ptr1 = ptr0 + 1; + View* ptr2 = ptr0 + 2; + using ProposedLayout = LayoutManagerBase::ProposedLayout; + ProposedLayout a; + ProposedLayout b; + EXPECT_TRUE(a == b); + a.host_size = {1, 2}; + EXPECT_FALSE(a == b); + b.host_size = {1, 2}; + EXPECT_TRUE(a == b); + a.child_layouts.push_back({ptr0, true, {1, 1, 2, 2}}); + EXPECT_FALSE(a == b); + b.child_layouts.push_back(a.child_layouts[0]); + EXPECT_TRUE(a == b); + a.child_layouts[0].visible = false; + EXPECT_FALSE(a == b); + b.child_layouts[0].visible = false; + EXPECT_TRUE(a == b); + b.child_layouts[0].bounds = {0, 0, 3, 3}; + // Since |visible| == false, changing bounds doesn't change anything. + EXPECT_TRUE(a == b); + a.child_layouts[0].visible = true; + b.child_layouts[0].visible = true; + EXPECT_FALSE(a == b); + a.child_layouts[0].visible = false; + b.child_layouts[0].visible = false; + a.child_layouts.push_back({ptr1, true, {1, 2, 3, 4}}); + b.child_layouts.push_back({ptr2, true, {1, 2, 3, 4}}); + EXPECT_FALSE(a == b); + b.child_layouts[1].child_view = ptr1; + EXPECT_TRUE(a == b); +} + } // namespace views
diff --git a/ui/views/test/views_test_base.cc b/ui/views/test/views_test_base.cc index c2ea8a7..f2181fe 100644 --- a/ui/views/test/views_test_base.cc +++ b/ui/views/test/views_test_base.cc
@@ -64,7 +64,12 @@ } // namespace -ViewsTestBase::ViewsTestBase() = default; +ViewsTestBase::ViewsTestBase() { + // MaterialDesignController is initialized here instead of in SetUp because + // a subclass might construct a MaterialDesignControllerTestAPI as a member to + // override the value, and this must happen first. + ui::MaterialDesignController::Initialize(); +} ViewsTestBase::~ViewsTestBase() { CHECK(setup_called_) @@ -82,7 +87,6 @@ has_compositing_manager_ = InitializeVisuals(); testing::Test::SetUp(); - ui::MaterialDesignController::Initialize(); setup_called_ = true; if (!views_delegate_for_setup_) views_delegate_for_setup_ = std::make_unique<TestViewsDelegate>();
diff --git a/ui/views/view.h b/ui/views/view.h index 75f488b..1a40510 100644 --- a/ui/views/view.h +++ b/ui/views/view.h
@@ -1095,13 +1095,13 @@ // Note that you can set multiple accelerators for a view by invoking this // method several times. Note also that AcceleratorPressed is invoked only // when CanHandleAccelerators() is true. - virtual void AddAccelerator(const ui::Accelerator& accelerator); + void AddAccelerator(const ui::Accelerator& accelerator); // Removes the specified accelerator for this view. - virtual void RemoveAccelerator(const ui::Accelerator& accelerator); + void RemoveAccelerator(const ui::Accelerator& accelerator); // Removes all the keyboard accelerators for this view. - virtual void ResetAccelerators(); + void ResetAccelerators(); // Overridden from AcceleratorTarget: bool AcceleratorPressed(const ui::Accelerator& accelerator) override; @@ -1144,8 +1144,8 @@ // Convenience method to retrieve the FocusManager associated with the // Widget that contains this view. This can return NULL if this view is not // part of a view hierarchy with a Widget. - virtual FocusManager* GetFocusManager(); - virtual const FocusManager* GetFocusManager() const; + FocusManager* GetFocusManager(); + const FocusManager* GetFocusManager() const; // Request keyboard focus. The receiving view will become the focused view. virtual void RequestFocus(); @@ -1330,7 +1330,7 @@ // Scrolls the view's bounds or some subset thereof to be visible. By default // this function calls ScrollRectToVisible(GetLocalBounds()). - virtual void ScrollViewToVisible(); + void ScrollViewToVisible(); // The following methods are used by ScrollView to determine the amount // to scroll relative to the visible bounds of the view. For example, a @@ -1351,10 +1351,12 @@ // // See VariableRowHeightScrollHelper and FixedRowHeightScrollHelper for // implementations of common cases. - virtual int GetPageScrollIncrement(ScrollView* scroll_view, - bool is_horizontal, bool is_positive); - virtual int GetLineScrollIncrement(ScrollView* scroll_view, - bool is_horizontal, bool is_positive); + int GetPageScrollIncrement(ScrollView* scroll_view, + bool is_horizontal, + bool is_positive); + int GetLineScrollIncrement(ScrollView* scroll_view, + bool is_horizontal, + bool is_positive); void AddObserver(ViewObserver* observer); void RemoveObserver(ViewObserver* observer); @@ -1518,7 +1520,7 @@ // Finds the layer that this view paints to (it may belong to an ancestor // view), then reorders the immediate children of that layer to match the // order of the view tree. - virtual void ReorderLayers(); + void ReorderLayers(); // This reorders the immediate children of |*parent_layer| to match the // order of the view tree. Child layers which are owned by a view are
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html b/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html index 907971e..c15c64182 100644 --- a/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html +++ b/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
@@ -32,6 +32,8 @@ .nameservers { /* Aligns with the start of cr-radio-button's text. */ margin-inline-start: 38px; + padding-bottom: 0; + padding-top: 0; } .nameservers:not([changeable]) {