diff --git a/DEPS b/DEPS index eac91af77..5382ad0 100644 --- a/DEPS +++ b/DEPS
@@ -40,11 +40,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': 'dc87c95382d5beab7fdae2e654d6de568fbe1671', + 'skia_revision': '135c908812874e1b1ee1b9dcaf7d55544d1f8e2a', # 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': '632d0542e57d2553c73892e4f45fd442f155e256', + 'v8_revision': '10b9a3e5fedf25be365758a2cec66284b3d3874b', # 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. @@ -64,7 +64,7 @@ # 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': '3b91290ba31e1d5bd0147f44f9cc723a72b6eb9a', + 'pdfium_revision': 'ce8e51e6c444f6caaa160bf8b1decd5d7ec84e6f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other.
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 729b427..9d29982 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -353,6 +353,8 @@ _ANDROID_SPECIFIC_PYDEPS_FILES = [ 'build/android/test_runner.pydeps', 'build/android/test_wrapper/logdog_wrapper.pydeps', + 'build/secondary/third_party/android_platform/' + 'development/scripts/stack.pydeps', 'net/tools/testserver/testserver.pydeps', ]
diff --git a/base/BUILD.gn b/base/BUILD.gn index a5efd6e2..16f5784 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -2130,6 +2130,7 @@ "template_util_unittest.cc", "test/histogram_tester_unittest.cc", "test/mock_callback_unittest.cc", + "test/scoped_feature_list_unittest.cc", "test/scoped_mock_time_message_loop_task_runner_unittest.cc", "test/scoped_task_scheduler_unittest.cc", "test/test_pending_task_unittest.cc",
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc index f0f3f4e..22b20b9 100644 --- a/base/test/scoped_feature_list.cc +++ b/base/test/scoped_feature_list.cc
@@ -4,24 +4,79 @@ #include "base/test/scoped_feature_list.h" +#include <algorithm> #include <string> +#include <vector> + +#include "base/stl_util.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" namespace base { namespace test { namespace { -static std::string GetFeatureString( +std::vector<StringPiece> GetFeatureVector( const std::initializer_list<base::Feature>& features) { - std::string output; + std::vector<StringPiece> output; for (const base::Feature& feature : features) { - if (!output.empty()) - output += ","; - output += feature.name; + output.push_back(feature.name); } + return output; } +// Extracts a feature name from a feature state string. For example, given +// the input "*MyLovelyFeature<SomeFieldTrial", returns "MyLovelyFeature". +StringPiece GetFeatureName(StringPiece feature) { + StringPiece feature_name = feature; + + // Remove default info. + if (feature_name.starts_with("*")) + feature_name = feature_name.substr(1); + + // Remove field_trial info. + std::size_t index = feature_name.find("<"); + if (index != std::string::npos) + feature_name = feature_name.substr(0, index); + + return feature_name; +} + +struct Features { + std::vector<StringPiece> enabled_feature_list; + std::vector<StringPiece> disabled_feature_list; +}; + +// Merges previously-specified feature overrides with those passed into one of +// the Init() methods. |features| should be a list of features previously +// overridden to be in the |override_state|. |merged_features| should contain +// the enabled and disabled features passed into the Init() method, plus any +// overrides merged as a result of previous calls to this function. +void OverrideFeatures(const std::string& features, + base::FeatureList::OverrideState override_state, + Features* merged_features) { + std::vector<StringPiece> features_list = + SplitStringPiece(features, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); + + for (StringPiece feature : features_list) { + StringPiece feature_name = GetFeatureName(feature); + + if (ContainsValue(merged_features->enabled_feature_list, feature_name) || + ContainsValue(merged_features->disabled_feature_list, feature_name)) + continue; + + if (override_state == FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE) { + merged_features->enabled_feature_list.push_back(feature); + } else { + DCHECK_EQ(override_state, + FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE); + merged_features->disabled_feature_list.push_back(feature); + } + } +} + } // namespace ScopedFeatureList::ScopedFeatureList() {} @@ -40,13 +95,6 @@ InitWithFeatureList(std::move(feature_list)); } -void ScopedFeatureList::InitWithFeatures( - const std::initializer_list<base::Feature>& enabled_features, - const std::initializer_list<base::Feature>& disabled_features) { - InitFromCommandLine(GetFeatureString(enabled_features), - GetFeatureString(disabled_features)); -} - void ScopedFeatureList::InitWithFeatureList( std::unique_ptr<FeatureList> feature_list) { DCHECK(!original_feature_list_); @@ -62,12 +110,43 @@ InitWithFeatureList(std::move(feature_list)); } +void ScopedFeatureList::InitWithFeatures( + const std::initializer_list<base::Feature>& enabled_features, + const std::initializer_list<base::Feature>& disabled_features) { + Features merged_features; + merged_features.enabled_feature_list = GetFeatureVector(enabled_features); + merged_features.disabled_feature_list = GetFeatureVector(disabled_features); + + base::FeatureList* feature_list = base::FeatureList::GetInstance(); + + // |current_enabled_features| and |current_disabled_features| must declare out + // of if scope to avoid them out of scope before JoinString calls because + // |merged_features| may contains StringPiece which holding pointer points to + // |current_enabled_features| and |current_disabled_features|. + std::string current_enabled_features; + std::string current_disabled_features; + if (feature_list) { + base::FeatureList::GetInstance()->GetFeatureOverrides( + ¤t_enabled_features, ¤t_disabled_features); + OverrideFeatures(current_enabled_features, + FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE, + &merged_features); + OverrideFeatures(current_disabled_features, + FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE, + &merged_features); + } + + std::string enabled = JoinString(merged_features.enabled_feature_list, ","); + std::string disabled = JoinString(merged_features.disabled_feature_list, ","); + InitFromCommandLine(enabled, disabled); +} + void ScopedFeatureList::InitAndEnableFeature(const base::Feature& feature) { - InitFromCommandLine(feature.name, std::string()); + InitWithFeatures({feature}, {}); } void ScopedFeatureList::InitAndDisableFeature(const base::Feature& feature) { - InitFromCommandLine(std::string(), feature.name); + InitWithFeatures({}, {feature}); } } // namespace test
diff --git a/base/test/scoped_feature_list.h b/base/test/scoped_feature_list.h index 99e07f5..e1df6c6a 100644 --- a/base/test/scoped_feature_list.h +++ b/base/test/scoped_feature_list.h
@@ -22,29 +22,42 @@ ScopedFeatureList(); ~ScopedFeatureList(); + // WARNING: This method will reset any globally configured features to their + // default values, which can hide feature interaction bugs. Please use + // sparingly. https://crbug.com/713390 // Initializes and registers a FeatureList instance with no overrides. void Init(); + // WARNING: This method will reset any globally configured features to their + // default values, which can hide feature interaction bugs. Please use + // sparingly. https://crbug.com/713390 // Initializes and registers the given FeatureList instance. void InitWithFeatureList(std::unique_ptr<FeatureList> feature_list); - // Initializes and registers a FeatureList instance with the given enabled - // and disabled features. - void InitWithFeatures( - const std::initializer_list<base::Feature>& enabled_features, - const std::initializer_list<base::Feature>& disabled_features); - - // Initializes and registers a FeatureList instance with the given + // WARNING: This method will reset any globally configured features to their + // default values, which can hide feature interaction bugs. Please use + // sparingly. https://crbug.com/713390 + // Initializes and registers a FeatureList instance with only the given // enabled and disabled features (comma-separated names). void InitFromCommandLine(const std::string& enable_features, const std::string& disable_features); - // Initializes and registers a FeatureList instance enabling a single - // feature. + // Initializes and registers a FeatureList instance based on present + // FeatureList and overridden with the given enabled and disabled features. + // Any feature overrides already present in the global FeatureList will + // continue to apply, unless they conflict with the overrides passed into this + // method. This is important for testing potentially unexpected feature + // interactions. + void InitWithFeatures( + const std::initializer_list<base::Feature>& enabled_features, + const std::initializer_list<base::Feature>& disabled_features); + + // Initializes and registers a FeatureList instance based on present + // FeatureList and overridden with single enabled feature. void InitAndEnableFeature(const base::Feature& feature); - // Initializes and registers a FeatureList instance disabling a single - // feature. + // Initializes and registers a FeatureList instance based on present + // FeatureList and overridden with single disabled feature. void InitAndDisableFeature(const base::Feature& feature); private:
diff --git a/base/test/scoped_feature_list_unittest.cc b/base/test/scoped_feature_list_unittest.cc new file mode 100644 index 0000000..21d1b4d --- /dev/null +++ b/base/test/scoped_feature_list_unittest.cc
@@ -0,0 +1,188 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test/scoped_feature_list.h" + +#include <string> +#include "base/metrics/field_trial.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace test { + +namespace { + +const base::Feature kTestFeature1{"TestFeature1", + base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kTestFeature2{"TestFeature2", + base::FEATURE_DISABLED_BY_DEFAULT}; + +void ExpectFeatures(const std::string& enabled_features, + const std::string& disabled_features) { + base::FeatureList* list = base::FeatureList::GetInstance(); + std::string actual_enabled_features; + std::string actual_disabled_features; + + list->GetFeatureOverrides(&actual_enabled_features, + &actual_disabled_features); + + EXPECT_EQ(enabled_features, actual_enabled_features); + EXPECT_EQ(disabled_features, actual_disabled_features); +} + +} // namespace + +class ScopedFeatureListTest : public testing::Test { + public: + ScopedFeatureListTest() { + // Clear default feature list. + std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); + feature_list->InitializeFromCommandLine(std::string(), std::string()); + original_feature_list_ = base::FeatureList::ClearInstanceForTesting(); + base::FeatureList::SetInstance(std::move(feature_list)); + } + + ~ScopedFeatureListTest() override { + // Restore feature list. + if (original_feature_list_) { + base::FeatureList::ClearInstanceForTesting(); + base::FeatureList::RestoreInstanceForTesting( + std::move(original_feature_list_)); + } + } + + private: + // Save the present FeatureList and restore it after test finish. + std::unique_ptr<FeatureList> original_feature_list_; + + DISALLOW_COPY_AND_ASSIGN(ScopedFeatureListTest); +}; + +TEST_F(ScopedFeatureListTest, BasicScoped) { + ExpectFeatures(std::string(), std::string()); + EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature1)); + { + test::ScopedFeatureList feature_list1; + feature_list1.InitFromCommandLine("TestFeature1", std::string()); + ExpectFeatures("TestFeature1", std::string()); + EXPECT_TRUE(FeatureList::IsEnabled(kTestFeature1)); + } + ExpectFeatures(std::string(), std::string()); + EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature1)); +} + +TEST_F(ScopedFeatureListTest, EnableFeatureOverrideDisable) { + test::ScopedFeatureList feature_list1; + feature_list1.InitWithFeatures({}, {kTestFeature1}); + + { + test::ScopedFeatureList feature_list2; + feature_list2.InitWithFeatures({kTestFeature1}, {}); + ExpectFeatures("TestFeature1", std::string()); + } +} + +TEST_F(ScopedFeatureListTest, FeatureOverrideNotMakeDuplicate) { + test::ScopedFeatureList feature_list1; + feature_list1.InitWithFeatures({}, {kTestFeature1}); + + { + test::ScopedFeatureList feature_list2; + feature_list2.InitWithFeatures({}, {kTestFeature1}); + ExpectFeatures(std::string(), "TestFeature1"); + } +} + +TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithDefault) { + test::ScopedFeatureList feature_list1; + feature_list1.InitFromCommandLine("*TestFeature1", std::string()); + + { + test::ScopedFeatureList feature_list2; + feature_list2.InitWithFeatures({kTestFeature1}, {}); + ExpectFeatures("TestFeature1", std::string()); + } +} + +TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithDefault2) { + test::ScopedFeatureList feature_list1; + feature_list1.InitFromCommandLine("*TestFeature1", std::string()); + + { + test::ScopedFeatureList feature_list2; + feature_list2.InitWithFeatures({}, {kTestFeature1}); + ExpectFeatures(std::string(), "TestFeature1"); + } +} + +TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithEnabledFieldTried) { + test::ScopedFeatureList feature_list1; + + std::unique_ptr<FeatureList> feature_list(new FeatureList); + FieldTrialList field_trial_list(nullptr); + FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample", "A"); + feature_list->RegisterFieldTrialOverride( + kTestFeature1.name, FeatureList::OVERRIDE_ENABLE_FEATURE, trial); + feature_list1.InitWithFeatureList(std::move(feature_list)); + + { + test::ScopedFeatureList feature_list2; + feature_list2.InitWithFeatures({kTestFeature1}, {}); + ExpectFeatures("TestFeature1", std::string()); + } +} + +TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithDisabledFieldTried) { + test::ScopedFeatureList feature_list1; + + std::unique_ptr<FeatureList> feature_list(new FeatureList); + FieldTrialList field_trial_list(nullptr); + FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample", "A"); + feature_list->RegisterFieldTrialOverride( + kTestFeature1.name, FeatureList::OVERRIDE_DISABLE_FEATURE, trial); + feature_list1.InitWithFeatureList(std::move(feature_list)); + + { + test::ScopedFeatureList feature_list2; + feature_list2.InitWithFeatures({kTestFeature1}, {}); + ExpectFeatures("TestFeature1", std::string()); + } +} + +TEST_F(ScopedFeatureListTest, FeatureOverrideKeepsOtherExistingFeature) { + test::ScopedFeatureList feature_list1; + feature_list1.InitWithFeatures({}, {kTestFeature1}); + + { + test::ScopedFeatureList feature_list2; + feature_list2.InitWithFeatures({}, {kTestFeature2}); + EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature1)); + EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature2)); + } +} + +TEST_F(ScopedFeatureListTest, FeatureOverrideKeepsOtherExistingFeature2) { + test::ScopedFeatureList feature_list1; + feature_list1.InitWithFeatures({}, {kTestFeature1}); + + { + test::ScopedFeatureList feature_list2; + feature_list2.InitWithFeatures({kTestFeature2}, {}); + ExpectFeatures("TestFeature2", "TestFeature1"); + } +} + +TEST_F(ScopedFeatureListTest, FeatureOverrideKeepsOtherExistingDefaultFeature) { + test::ScopedFeatureList feature_list1; + feature_list1.InitFromCommandLine("*TestFeature1", std::string()); + + { + test::ScopedFeatureList feature_list2; + feature_list2.InitWithFeatures({}, {kTestFeature2}); + ExpectFeatures("*TestFeature1", "TestFeature2"); + } +} + +} // namespace test +} // namespace base
diff --git a/build/android/gyp/create_stack_script.py b/build/android/gyp/create_stack_script.py new file mode 100755 index 0000000..56556958 --- /dev/null +++ b/build/android/gyp/create_stack_script.py
@@ -0,0 +1,84 @@ +#!/usr/bin/env python +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + + +import argparse +import os +import sys +import textwrap + +from util import build_utils + +SCRIPT_TEMPLATE = textwrap.dedent( + """\ + #!/usr/bin/env python + # + # This file was generated by build/android/gyp/create_stack_script.py + + import os + import sys + + def main(argv): + script_directory = os.path.dirname(__file__) + resolve = lambda p: os.path.abspath(os.path.join(script_directory, p)) + script_path = resolve('{script_path}') + script_args = {script_args} + script_path_args = {script_path_args} + for arg, path in script_path_args: + script_args.extend([arg, resolve(path)]) + script_cmd = [script_path] + script_args + argv + print ' '.join(script_cmd) + os.execv(script_path, script_cmd) + + if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) + """) + + +def main(args): + + parser = argparse.ArgumentParser() + build_utils.AddDepfileOption(parser) + parser.add_argument( + '--script-path', + help='Path to the wrapped script.') + parser.add_argument( + '--script-output-path', + help='Path to the output script.') + group = parser.add_argument_group('Path arguments') + group.add_argument('--output-directory') + group.add_argument('--packed-libs') + + args, script_args = parser.parse_known_args(build_utils.ExpandFileArgs(args)) + + def relativize(p): + return os.path.relpath(p, os.path.dirname(args.script_output_path)) + + script_path = relativize(args.script_path) + + script_path_args = [] + if args.output_directory: + script_path_args.append( + ('--output-directory', relativize(args.output_directory))) + if args.packed_libs: + for p in build_utils.ParseGnList(args.packed_libs): + script_path_args.append(('--packed-lib', relativize(p))) + + with open(args.script_output_path, 'w') as script: + script.write(SCRIPT_TEMPLATE.format( + script_path=script_path, + script_args=script_args, + script_path_args=script_path_args)) + + os.chmod(args.script_output_path, 0750) + + if args.depfile: + build_utils.WriteDepfile(args.depfile, args.script_output_path) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 3f592bd..0ab225b 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -638,6 +638,61 @@ } } +template("stack_script") { + forward_variables_from(invoker, [ "testonly" ]) + + _stack_target_name = invoker.stack_target_name + + action(target_name) { + forward_variables_from(invoker, + [ + "data_deps", + "deps", + ]) + if (!defined(deps)) { + deps = [] + } + if (!defined(data_deps)) { + data_deps = [] + } + + data_deps += + [ "//third_party/android_platform/development/scripts:stack_py" ] + + script = "//build/android/gyp/create_stack_script.py" + depfile = "$target_gen_dir/$target_name.d" + + _stack_script = "//third_party/android_platform/development/scripts/stack" + + _generated_script = "$root_build_dir/bin/stack_${_stack_target_name}" + + outputs = [ + _generated_script, + ] + data = [ + _generated_script, + ] + + args = [ + "--depfile", + rebase_path(depfile, root_build_dir), + "--output-directory", + rebase_path(root_build_dir, root_build_dir), + "--script-path", + rebase_path(_stack_script, root_build_dir), + "--script-output-path", + rebase_path(_generated_script, root_build_dir), + "--arch=$target_cpu", + ] + if (defined(invoker.packed_libraries)) { + args += [ + "--packed-libs", + invoker.packed_libraries, + ] + } + } +} + if (enable_java_templates) { import("//build/config/zip.gni") import("//third_party/ijar/ijar.gni")
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index eddbbf3..a08a475 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -2100,6 +2100,17 @@ _extra_native_libs_deps += [ "//base/android/linker:chromium_android_linker" ] } + + _create_stack_script_rule_name = "${_template_name}__stack_script" + _final_deps += [ ":${_create_stack_script_rule_name}" ] + stack_script(_create_stack_script_rule_name) { + stack_target_name = invoker.target_name + deps = _native_libs_deps + if (_native_libs_deps != [] && _enable_relocation_packing) { + packed_libraries = _native_libs_file_arg + deps += [ _native_libs_file_arg_dep ] + } + } } if (defined(invoker.loadable_modules) && invoker.loadable_modules != []) { _extra_native_libs_even_when_incremental += invoker.loadable_modules
diff --git a/build/print_python_deps.py b/build/print_python_deps.py index 50a1f08f..92c75a53 100755 --- a/build/print_python_deps.py +++ b/build/print_python_deps.py
@@ -38,7 +38,8 @@ if not path.startswith(_SRC_ROOT): continue - if path.endswith('.pyc'): + if (path.endswith('.pyc') + or (path.endswith('c') and not os.path.splitext(path)[1])): path = path[:-1] src_paths.add(path)
diff --git a/build/secondary/third_party/android_platform/development/scripts/BUILD.gn b/build/secondary/third_party/android_platform/development/scripts/BUILD.gn new file mode 100644 index 0000000..6dabf5ed --- /dev/null +++ b/build/secondary/third_party/android_platform/development/scripts/BUILD.gn
@@ -0,0 +1,13 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +group("stack_py") { + _py_files = read_file( + "//build/secondary/third_party/android_platform/development/scripts/stack.pydeps", + "list lines") + + set_sources_assignment_filter([ "#*" ]) + sources = _py_files + data = sources +}
diff --git a/build/secondary/third_party/android_platform/development/scripts/stack.pydeps b/build/secondary/third_party/android_platform/development/scripts/stack.pydeps new file mode 100644 index 0000000..2f46729e --- /dev/null +++ b/build/secondary/third_party/android_platform/development/scripts/stack.pydeps
@@ -0,0 +1,19 @@ +# Generated by running: +# build/print_python_deps.py --root third_party/android_platform/development/scripts --output build/secondary/third_party/android_platform/development/scripts/stack.pydeps third_party/android_platform/development/scripts/stack +../../../../build/android/pylib/__init__.py +../../../../build/android/pylib/constants/__init__.py +../../../../build/android/pylib/symbols/__init__.py +../../../../build/android/pylib/symbols/elf_symbolizer.py +../../../catapult/devil/devil/__init__.py +../../../catapult/devil/devil/android/__init__.py +../../../catapult/devil/devil/android/constants/__init__.py +../../../catapult/devil/devil/android/constants/chrome.py +../../../catapult/devil/devil/android/sdk/__init__.py +../../../catapult/devil/devil/android/sdk/keyevent.py +../../../catapult/devil/devil/android/sdk/version_codes.py +../../../catapult/devil/devil/constants/__init__.py +../../../catapult/devil/devil/constants/exit_codes.py +stack +stack_core.py +stack_libs.py +symbol.py
diff --git a/chrome/VERSION b/chrome/VERSION index 7570bad..39608e4 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=60 MINOR=0 -BUILD=3084 +BUILD=3085 PATCH=0
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index 49f441c..18cb439 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml
@@ -632,6 +632,14 @@ android:exported="false"> </service> + <!-- Service for decoding images in a sandboxed process. --> + <service + android:description="@string/decoder_description" + android:name="org.chromium.chrome.browser.photo_picker.DecoderService" + android:exported="false" + android:isolatedProcess="true" + android:process=":decoder_service" /> + <!-- Providers for chrome data. --> <provider android:name="org.chromium.chrome.browser.provider.ChromeBrowserProvider" android:authorities="{{ manifest_package }}.ChromeBrowserProvider;{{ manifest_package }}.browser;{{ manifest_package }}"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java index 233abced..cb415ee6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
@@ -68,6 +68,8 @@ private static final String DATA_REDUCTION_HAS_EVER_BEEN_ENABLED_PREF = "BANDWIDTH_REDUCTION_PROXY_HAS_EVER_BEEN_ENABLED"; + public static final String DATA_REDUCTION_FIRST_ENABLED_TIME = + "BANDWIDTH_REDUCTION_FIRST_ENABLED_TIME"; private static final String PARAM_PERSISTENT_MENU_ITEM_ENABLED = "persistent_menu_item_enabled"; @@ -155,6 +157,15 @@ * data reduction statistics if this is the first time the SPDY proxy has been enabled. */ public void setDataReductionProxyEnabled(Context context, boolean enabled) { + if (enabled + && ContextUtils.getAppSharedPreferences().getLong( + DATA_REDUCTION_FIRST_ENABLED_TIME, 0) + == 0) { + ContextUtils.getAppSharedPreferences() + .edit() + .putLong(DATA_REDUCTION_FIRST_ENABLED_TIME, System.currentTimeMillis()) + .apply(); + } ContextUtils.getAppSharedPreferences().edit() .putBoolean(DATA_REDUCTION_ENABLED_PREF, enabled).apply(); nativeSetDataReductionProxyEnabled(mNativeDataReductionProxySettings, enabled); @@ -203,12 +214,25 @@ } /** + * Returns the time that the proxy was first enabled. If data saving statistics are cleared, + * this is set to the reset time. + * @return The time that the proxy was first enabled in milliseconds since the epoch. + */ + public long getDataReductionProxyFirstEnabledTime() { + return ContextUtils.getAppSharedPreferences().getLong(DATA_REDUCTION_FIRST_ENABLED_TIME, 0); + } + + /** * Clears all data saving statistics. */ public void clearDataSavingStatistics() { // When the data saving statistics are cleared, reset the snackbar promo that tells the user // how much data they have saved using Data Saver so far. DataReductionPromoUtils.saveSnackbarPromoDisplayed(0); + ContextUtils.getAppSharedPreferences() + .edit() + .putLong(DATA_REDUCTION_FIRST_ENABLED_TIME, System.currentTimeMillis()) + .apply(); nativeClearDataSavingStatistics(mNativeDataReductionProxySettings); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/BitmapUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/BitmapUtils.java new file mode 100644 index 0000000..ddad8aa --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/BitmapUtils.java
@@ -0,0 +1,110 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.photo_picker; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import java.io.FileDescriptor; + +/** + * A collection of utility functions for dealing with bitmaps. + */ +class BitmapUtils { + /** + * Takes a |bitmap| and returns a square thumbnail of |size|x|size| from the center of the + * bitmap specified. + * @param bitmap The bitmap to adjust. + * @param size The desired size (width and height). + * @return The new bitmap thumbnail. + */ + private static Bitmap sizeBitmap(Bitmap bitmap, int size) { + // TODO(finnur): Investigate options that require fewer bitmaps to be created. + bitmap = ensureMinSize(bitmap, size); + bitmap = cropToSquare(bitmap, size); + return bitmap; + } + + /** + * Given a FileDescriptor, decodes the contents and returns a bitmap of + * dimensions |size|x|size|. + * @param descriptor The FileDescriptor for the file to read. + * @param size The width and height of the bitmap to return. + * @return The resulting bitmap. + */ + public static Bitmap decodeBitmapFromFileDescriptor(FileDescriptor descriptor, int size) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFileDescriptor(descriptor, null, options); + options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, size); + options.inJustDecodeBounds = false; + Bitmap bitmap = BitmapFactory.decodeFileDescriptor(descriptor, null, options); + + if (bitmap == null) return null; + + return sizeBitmap(bitmap, size); + } + + /** + * Calculates the sub-sampling factor {@link BitmapFactory#inSampleSize} option for a given + * image dimensions, which will be used to create a bitmap of a pre-determined size (as small as + * possible without either dimension shrinking below |minSize|. + * @param width The calculated width of the image to decode. + * @param height The calculated height of the image to decode. + * @param minSize The maximum size the image should be (in either dimension). + * @return The sub-sampling factor (power of two: 1 = no change, 2 = half-size, etc). + */ + private static int calculateInSampleSize(int width, int height, int minSize) { + int inSampleSize = 1; + if (width > minSize && height > minSize) { + inSampleSize = Math.min(width, height) / minSize; + } + return inSampleSize; + } + + /** + * Ensures a |bitmap| is at least |size| in both width and height. + * @param bitmap The bitmap to modify. + * @param size The minimum size (width and height). + * @return The resulting (scaled) bitmap. + */ + private static Bitmap ensureMinSize(Bitmap bitmap, int size) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + if (width >= size && height >= size) return bitmap; + + if (width < size) { + float scale = (float) size / width; + width = size; + height *= scale; + } + + if (height < size) { + float scale = (float) size / height; + height = size; + width *= scale; + } + + return Bitmap.createScaledBitmap(bitmap, width, height, true); + } + + /** + * Crops a |bitmap| to a certain square |size| + * @param bitmap The bitmap to crop. + * @param size The size desired (width and height). + * @return The resulting (square) bitmap. + */ + private static Bitmap cropToSquare(Bitmap bitmap, int size) { + int x = 0; + int y = 0; + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + if (width == size && height == size) return bitmap; + + if (width > size) x = (width - size) / 2; + if (height > size) y = (height - size) / 2; + return Bitmap.createBitmap(bitmap, x, y, size, size); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java new file mode 100644 index 0000000..0cd7713 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java
@@ -0,0 +1,135 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.photo_picker; + +import android.app.Service; +import android.content.Intent; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; + +import org.chromium.base.Log; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * A service to accept requests to take image file contents and decode them. + */ +public class DecoderService extends Service { + // Message ids for communicating with the client. + + // A message sent by the client to decode an image. + static final int MSG_DECODE_IMAGE = 1; + // A message sent by the server to notify the client of the results of the decoding. + static final int MSG_IMAGE_DECODED_REPLY = 2; + + // The keys for the bundle when passing data to and from this service. + static final String KEY_FILE_DESCRIPTOR = "file_descriptor"; + static final String KEY_FILE_PATH = "file_path"; + static final String KEY_IMAGE_BITMAP = "image_bitmap"; + static final String KEY_IMAGE_BYTE_COUNT = "image_byte_count"; + static final String KEY_IMAGE_DESCRIPTOR = "image_descriptor"; + static final String KEY_SIZE = "size"; + static final String KEY_SUCCESS = "success"; + + // A tag for logging error messages. + private static final String TAG = "ImageDecoder"; + + /** + * Handler for incoming messages from clients. + */ + static class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_DECODE_IMAGE: + Bundle bundle = null; + Messenger client = null; + String filePath = ""; + int size = 0; + try { + Bundle payload = msg.getData(); + client = msg.replyTo; + + filePath = payload.getString(KEY_FILE_PATH); + ParcelFileDescriptor pfd = payload.getParcelable(KEY_FILE_DESCRIPTOR); + size = payload.getInt(KEY_SIZE); + + // Setup a minimum viable response to parent process. Will be fleshed out + // further below. + bundle = new Bundle(); + bundle.putString(KEY_FILE_PATH, filePath); + bundle.putBoolean(KEY_SUCCESS, false); + + FileDescriptor fd = pfd.getFileDescriptor(); + Bitmap bitmap = BitmapUtils.decodeBitmapFromFileDescriptor(fd, size); + try { + pfd.close(); + } catch (IOException e) { + Log.e(TAG, "Closing failed " + filePath + " (size: " + size + ") " + e); + } + + if (bitmap == null) { + Log.e(TAG, "Decode failed " + filePath + " (size: " + size + ")"); + sendReply(client, bundle); // Sends SUCCESS == false; + return; + } + + // The most widely supported, easiest, and reasonably efficient method is to + // decode to an immutable bitmap and just return the bitmap over binder. It + // will internally memcpy itself to ashmem and then just send over the file + // descriptor. In the receiving process it will just leave the bitmap on + // ashmem since it's immutable and carry on. + bundle.putParcelable(KEY_IMAGE_BITMAP, bitmap); + bundle.putBoolean(KEY_SUCCESS, true); + sendReply(client, bundle); + bitmap.recycle(); + } catch (Exception e) { + // This service has no UI and maintains no state so if it crashes on + // decoding a photo, it is better UX to eat the exception instead of showing + // a crash dialog and discarding other requests that have already been sent. + Log.e(TAG, + "Unexpected error during decoding " + filePath + " (size: " + size + + ") " + e); + + if (bundle != null && client != null) sendReply(client, bundle); + } + break; + default: + super.handleMessage(msg); + } + } + + private void sendReply(Messenger client, Bundle bundle) { + Message reply = Message.obtain(null, MSG_IMAGE_DECODED_REPLY); + reply.setData(bundle); + try { + client.send(reply); + } catch (RemoteException remoteException) { + Log.e(TAG, "Remote error while replying: " + remoteException); + } + } + } + + /** + * The target we publish for clients to send messages to IncomingHandler. + */ + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + /** + * When binding to the service, we return an interface to our messenger + * for sending messages to the service. + */ + @Override + public IBinder onBind(Intent intent) { + return mMessenger.getBinder(); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java new file mode 100644 index 0000000..ae83641 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
@@ -0,0 +1,294 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.photo_picker; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.StrictMode; + +import org.chromium.base.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.LinkedHashMap; + +/** + * A class to communicate with the {@link DecoderService}. + */ +public class DecoderServiceHost { + // A tag for logging error messages. + private static final String TAG = "ImageDecoderHost"; + + /** + * Interface for notifying clients of the service being ready. + */ + public interface ServiceReadyCallback { + /** + * A function to define to receive a notification once the service is up and running. + */ + void serviceReady(); + } + + /** + * An interface notifying clients when an image has finished decoding. + */ + public interface ImageDecodedCallback { + /** + * A function to define to receive a notification that an image has been decoded. + * @param filePath The file path for the newly decoded image. + * @param bitmap The results of the decoding (or placeholder image, if failed). + */ + void imageDecodedCallback(String filePath, Bitmap bitmap); + } + + /** + * Class for interacting with the main interface of the service. + */ + private class DecoderServiceConnection implements ServiceConnection { + // The callback to use to notify the service being ready. + private ServiceReadyCallback mCallback; + + public DecoderServiceConnection(ServiceReadyCallback callback) { + mCallback = callback; + } + + // Called when a connection to the service has been established. + public void onServiceConnected(ComponentName name, IBinder service) { + mService = new Messenger(service); + mBound = true; + mCallback.serviceReady(); + } + + // Called when a connection to the service has been lost. + public void onServiceDisconnected(ComponentName name) { + mBound = false; + } + } + + /** + * Class for keeping track of the data involved with each request. + */ + private static class DecoderServiceParams { + // The path to the file containing the bitmap to decode. + public String mFilePath; + + // The requested size (width and height) of the bitmap, once decoded. + public int mSize; + + // The callback to use to communicate the results of the decoding. + ImageDecodedCallback mCallback; + + public DecoderServiceParams(String filePath, int size, ImageDecodedCallback callback) { + mFilePath = filePath; + mSize = size; + mCallback = callback; + } + } + + // Map of file paths to decoder parameters in order of request. + private LinkedHashMap<String, DecoderServiceParams> mRequests = new LinkedHashMap<>(); + LinkedHashMap<String, DecoderServiceParams> getRequests() { + return mRequests; + } + + // The callback used to notify the client when the service is ready. + private ServiceReadyCallback mCallback; + + // Messenger for communicating with the remote service. + Messenger mService = null; + + // Our service connection to the {@link DecoderService}. + private DecoderServiceConnection mConnection; + + // Flag indicating whether we are bound to the service. + boolean mBound; + + // The inbound messenger used by the remote service to communicate with us. + final Messenger mMessenger = new Messenger(new IncomingHandler(this)); + + /** + * The DecoderServiceHost constructor. + * @param callback The callback to use when communicating back to the client. + */ + public DecoderServiceHost(ServiceReadyCallback callback) { + mCallback = callback; + } + + /** + * Initiate binding with the {@link DecoderService}. + * @param context The context to use. + */ + public void bind(Context context) { + mConnection = new DecoderServiceConnection(mCallback); + Intent intent = new Intent(context, DecoderService.class); + context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + /** + * Unbind from the {@link DecoderService}. + * @param context The context to use. + */ + public void unbind(Context context) { + if (mBound) { + context.unbindService(mConnection); + mBound = false; + } + } + + /** + * Accepts a request to decode a single image. Queues up the request and reports back + * asynchronously on |callback|. + * @param filePath The path to the file to decode. + * @param size The requested size (width and height) of the resulting bitmap. + * @param callback The callback to use to communicate the decoding results. + */ + public void decodeImage(String filePath, int size, ImageDecodedCallback callback) { + DecoderServiceParams params = new DecoderServiceParams(filePath, size, callback); + mRequests.put(filePath, params); + if (mRequests.size() == 1) dispatchNextDecodeImageRequest(); + } + + /** + * Dispatches the next image for decoding (from the queue). + */ + private void dispatchNextDecodeImageRequest() { + if (mRequests.entrySet().iterator().hasNext()) { + DecoderServiceParams params = mRequests.entrySet().iterator().next().getValue(); + dispatchDecodeImageRequest(params.mFilePath, params.mSize); + } + } + + /** + * Ties up all the loose ends from the decoding request (communicates the results of the + * decoding process back to the client, and takes care of house-keeping chores regarding + * the request queue). + * @param filePath The path to the image that was just decoded. + * @param bitmap The resulting decoded bitmap. + */ + public void closeRequest(String filePath, Bitmap bitmap) { + DecoderServiceParams params = getRequests().get(filePath); + if (params != null) { + params.mCallback.imageDecodedCallback(filePath, bitmap); + getRequests().remove(filePath); + } + dispatchNextDecodeImageRequest(); + } + + /** + * Communicates with the server to decode a single bitmap. + * @param filePath The path to the image on disk. + * @param size The requested width and height of the resulting bitmap. + */ + private void dispatchDecodeImageRequest(String filePath, int size) { + // Obtain a file descriptor to send over to the sandboxed process. + File file = new File(filePath); + FileInputStream inputFile = null; + ParcelFileDescriptor pfd = null; + Bundle bundle = new Bundle(); + + // The restricted utility process can't open the file to read the + // contents, so we need to obtain a file descriptor to pass over. + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + try { + try { + inputFile = new FileInputStream(file); + FileDescriptor fd = inputFile.getFD(); + pfd = ParcelFileDescriptor.dup(fd); + bundle.putParcelable(DecoderService.KEY_FILE_DESCRIPTOR, pfd); + } catch (IOException e) { + Log.e(TAG, "Unable to obtain FileDescriptor: " + e); + closeRequest(filePath, null); + } + } finally { + try { + if (inputFile != null) inputFile.close(); + } catch (IOException e) { + Log.e(TAG, "Unable to close inputFile: " + e); + } + StrictMode.setThreadPolicy(oldPolicy); + } + + if (pfd == null) return; + + // Prepare and send the data over. + Message payload = Message.obtain(null, DecoderService.MSG_DECODE_IMAGE); + payload.replyTo = mMessenger; + bundle.putString(DecoderService.KEY_FILE_PATH, filePath); + bundle.putInt(DecoderService.KEY_SIZE, size); + payload.setData(bundle); + try { + mService.send(payload); + pfd.close(); + } catch (RemoteException e) { + Log.e(TAG, "Communications failed (Remote): " + e); + closeRequest(filePath, null); + } catch (IOException e) { + Log.e(TAG, "Communications failed (IO): " + e); + closeRequest(filePath, null); + } + } + + /** + * Cancels a request to decode an image (if it hasn't already been dispatched). + * @param filePath The path to the image to cancel decoding. + */ + public void cancelDecodeImage(String filePath) { + mRequests.remove(filePath); + } + + /** + * A class for handling communications from the service to us. + */ + static class IncomingHandler extends Handler { + // The DecoderServiceHost object to communicate with. + private final WeakReference<DecoderServiceHost> mHost; + + /** + * Constructor for IncomingHandler. + * @param host The DecoderServiceHost object to communicate with. + */ + IncomingHandler(DecoderServiceHost host) { + mHost = new WeakReference<DecoderServiceHost>(host); + } + + @Override + public void handleMessage(Message msg) { + DecoderServiceHost host = mHost.get(); + if (host == null) { + super.handleMessage(msg); + return; + } + + switch (msg.what) { + case DecoderService.MSG_IMAGE_DECODED_REPLY: + Bundle payload = msg.getData(); + + // Read the reply back from the service. + String filePath = payload.getString(DecoderService.KEY_FILE_PATH); + Boolean success = payload.getBoolean(DecoderService.KEY_SUCCESS); + Bitmap bitmap = success + ? (Bitmap) payload.getParcelable(DecoderService.KEY_IMAGE_BITMAP) + : null; + host.closeRequest(filePath, bitmap); + break; + default: + super.handleMessage(msg); + } + } + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java index 2b64157..66cfc45 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java
@@ -10,6 +10,7 @@ import java.io.File; import java.io.FileFilter; import java.util.HashSet; +import java.util.List; import java.util.Locale; /** @@ -23,13 +24,12 @@ /** * Contructs a MimeTypeFileFilter object. - * @param acceptAttr A comma seperated list of MIME types this filter accepts. - * For example: images/gif, video/*. + * @param mimeTypes A list of MIME types this filter accepts. + * For example: images/gif, video/*. */ - // TODO(finnur): Convert param to List. - public MimeTypeFileFilter(@NonNull String acceptAttr) { - for (String field : acceptAttr.toLowerCase(Locale.US).split(",")) { - field = field.trim(); + public MimeTypeFileFilter(@NonNull List<String> mimeTypes) { + for (String field : mimeTypes) { + field = field.trim().toLowerCase(Locale.US); if (field.startsWith(".")) { mExtensions.add(field.substring(1)); } else if (field.endsWith("/*")) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java index 6070779..268ae51 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java
@@ -189,8 +189,13 @@ mBitmapDetails = bitmapDetails; setItem(bitmapDetails); - setThumbnailBitmap(thumbnail); - mImageLoaded = !placeholder; + if (isCameraTile() || isGalleryTile()) { + initializeSpecialTile(mBitmapDetails); + mImageLoaded = true; + } else { + setThumbnailBitmap(thumbnail); + mImageLoaded = !placeholder; + } updateSelectionState(); } @@ -217,8 +222,6 @@ mSpecialTile, null, image, null, null); mSpecialTile.setText(labelStringId); - initialize(bitmapDetails, null, false); - // Reset visibility, since #initialize() sets mSpecialTile visibility to GONE. mSpecialTile.setVisibility(View.VISIBLE); } @@ -266,6 +269,7 @@ * re-used. */ private void resetTile() { + mIconView.setImageBitmap(null); mUnselectedView.setVisibility(View.GONE); mSelectedView.setVisibility(View.GONE); mScrim.setVisibility(View.GONE); @@ -323,15 +327,14 @@ } private boolean isGalleryTile() { - // TODO(finnur): Remove the null checks here and below. - return mBitmapDetails != null && mBitmapDetails.type() == PickerBitmap.GALLERY; + return mBitmapDetails.type() == PickerBitmap.GALLERY; } private boolean isCameraTile() { - return mBitmapDetails != null && mBitmapDetails.type() == PickerBitmap.CAMERA; + return mBitmapDetails.type() == PickerBitmap.CAMERA; } private boolean isPictureTile() { - return mBitmapDetails == null || mBitmapDetails.type() == PickerBitmap.PICTURE; + return mBitmapDetails.type() == PickerBitmap.PICTURE; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java index 70d18a8..bd8dba0de 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java
@@ -5,9 +5,6 @@ package org.chromium.chrome.browser.photo_picker; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; import android.support.v7.widget.RecyclerView.ViewHolder; import android.text.TextUtils; @@ -16,7 +13,8 @@ /** * Holds on to a {@link PickerBitmapView} that displays information about a picker bitmap. */ -public class PickerBitmapViewHolder extends ViewHolder { +public class PickerBitmapViewHolder + extends ViewHolder implements DecoderServiceHost.ImageDecodedCallback { // Our parent category. private PickerCategoryView mCategoryView; @@ -35,11 +33,9 @@ mItemView = itemView; } - /** - * The notification handler for when an image has been decoded. - * @param filePath The file path for the newly decoded image. - * @param bitmap The results of the decoding (or placeholder image, if failed). - */ + // DecoderServiceHost.ImageDecodedCallback + + @Override public void imageDecodedCallback(String filePath, Bitmap bitmap) { if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) { return; @@ -65,31 +61,16 @@ if (mBitmapDetails.type() == PickerBitmap.CAMERA || mBitmapDetails.type() == PickerBitmap.GALLERY) { - mItemView.initializeSpecialTile(mBitmapDetails); + mItemView.initialize(mBitmapDetails, null, false); return; } // TODO(finnur): Use cached image, if available. - // TODO(finnur): Use decoder instead. - int size = mCategoryView.getImageSize(); - imageDecodedCallback(mBitmapDetails.getFilePath(), createPlaceholderBitmap(size, size)); - } + mItemView.initialize(mBitmapDetails, null, true); - /** - * Creates a placeholder bitmap. - * @param width The requested width of the resulting bitmap. - * @param height The requested height of the resulting bitmap. - * @return Placeholder bitmap. - */ - // TODO(finnur): Remove once the decoder is in place. - private Bitmap createPlaceholderBitmap(int width, int height) { - Bitmap placeholder = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(placeholder); - Paint paint = new Paint(); - paint.setColor(Color.GRAY); - canvas.drawRect(0, 0, (float) width, (float) height, paint); - return placeholder; + int size = mCategoryView.getImageSize(); + mCategoryView.getDecoderServiceHost().decodeImage(mBitmapDetails.getFilePath(), size, this); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java index 6c73972..9ed19c9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
@@ -20,6 +20,7 @@ import org.chromium.chrome.browser.widget.selection.SelectionDelegate; import org.chromium.ui.PhotoPickerListener; +import java.util.Arrays; import java.util.List; /** @@ -27,7 +28,8 @@ * the photo picker, for example the RecyclerView and the bitmap caches. */ public class PickerCategoryView extends RelativeLayout - implements FileEnumWorkerTask.FilesEnumeratedCallback, OnMenuItemClickListener { + implements FileEnumWorkerTask.FilesEnumeratedCallback, RecyclerView.RecyclerListener, + DecoderServiceHost.ServiceReadyCallback, OnMenuItemClickListener { // The dialog that owns us. private PhotoPickerDialog mDialog; @@ -46,6 +48,9 @@ // The callback to notify the listener of decisions reached in the picker. private PhotoPickerListener mListener; + // The host class for the decoding service. + private DecoderServiceHost mDecoderServiceHost; + // The RecyclerView showing the images. private RecyclerView mRecyclerView; @@ -71,6 +76,9 @@ // A worker task for asynchronously enumerating files off the main thread. private FileEnumWorkerTask mWorkerTask; + // Whether the connection to the service has been established. + private boolean mServiceReady; + public PickerCategoryView(Context context) { super(context); postConstruction(context); @@ -84,6 +92,11 @@ private void postConstruction(Context context) { mContext = context; + mDecoderServiceHost = new DecoderServiceHost(this); + mDecoderServiceHost.bind(mContext); + + enumerateBitmaps(); + mSelectionDelegate = new SelectionDelegate<PickerBitmap>(); View root = LayoutInflater.from(context).inflate(R.layout.photo_picker_dialog, this); @@ -107,18 +120,21 @@ mRecyclerView.addItemDecoration(new GridSpacingItemDecoration(mColumns, mPadding)); // TODO(finnur): Implement caching. - // TODO(finnur): Remove this once the decoder service is in place. - prepareBitmaps(); } /** - * Cancels any outstanding requests. + * Severs the connection to the decoding utility process and cancels any outstanding requests. */ public void onDialogDismissed() { if (mWorkerTask != null) { mWorkerTask.cancel(true); mWorkerTask = null; } + + if (mDecoderServiceHost != null) { + mDecoderServiceHost.unbind(mContext); + mDecoderServiceHost = null; + } } /** @@ -141,8 +157,25 @@ @Override public void filesEnumeratedCallback(List<PickerBitmap> files) { mPickerBitmaps = files; - if (files != null && files.size() > 0) { - mPickerAdapter.notifyDataSetChanged(); + processBitmaps(); + } + + // DecoderServiceHost.ServiceReadyCallback: + + @Override + public void serviceReady() { + mServiceReady = true; + processBitmaps(); + } + + // RecyclerView.RecyclerListener: + + @Override + public void onViewRecycled(RecyclerView.ViewHolder holder) { + PickerBitmapViewHolder bitmapHolder = (PickerBitmapViewHolder) holder; + String filePath = bitmapHolder.getFilePath(); + if (filePath != null) { + getDecoderServiceHost().cancelDecodeImage(filePath); } } @@ -162,6 +195,16 @@ return false; } + /** + * Start loading of bitmaps, once files have been enumerated and service is + * ready to decode. + */ + private void processBitmaps() { + if (mServiceReady && mPickerBitmaps != null) { + mPickerAdapter.notifyDataSetChanged(); + } + } + // Simple accessors: public int getImageSize() { @@ -176,6 +219,10 @@ return mPickerBitmaps; } + public DecoderServiceHost getDecoderServiceHost() { + return mDecoderServiceHost; + } + public boolean isMultiSelectAllowed() { return mMultiSelectionAllowed; } @@ -212,14 +259,15 @@ } /** - * Prepares bitmaps for loading. + * Asynchronously enumerates bitmaps on disk. */ - private void prepareBitmaps() { + private void enumerateBitmaps() { if (mWorkerTask != null) { mWorkerTask.cancel(true); } - mWorkerTask = new FileEnumWorkerTask(this, new MimeTypeFileFilter("image/*")); + mWorkerTask = + new FileEnumWorkerTask(this, new MimeTypeFileFilter(Arrays.asList("image/*"))); mWorkerTask.execute(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java index f77c23d..20eea20 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java
@@ -53,12 +53,17 @@ DataReductionProxySettings.getInstance() .getContentLengthSavedInHistorySummary()); - long millisSinceEpoch = + long chartStartDateInMillisSinceEpoch = DataReductionProxySettings.getInstance().getDataReductionLastUpdateTime() - DateUtils.DAY_IN_MILLIS * ChartDataUsageView.DAYS_IN_CHART; + long firstEnabledInMillisSinceEpoch = DataReductionProxySettings.getInstance() + .getDataReductionProxyFirstEnabledTime(); + long mostRecentTime = chartStartDateInMillisSinceEpoch > firstEnabledInMillisSinceEpoch + ? chartStartDateInMillisSinceEpoch + : firstEnabledInMillisSinceEpoch; + final int flags = DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_NO_YEAR; - String date = - DateUtils.formatDateTime(getContext(), millisSinceEpoch, flags).toString(); + String date = DateUtils.formatDateTime(getContext(), mostRecentTime, flags).toString(); itemText.setText( getContext().getString(R.string.data_reduction_saved_label, dataSaved)); @@ -67,6 +72,12 @@ int lightActiveColor = ApiCompatibilityUtils.getColor( getContext().getResources(), R.color.light_active_color); itemText.setTextColor(lightActiveColor); + + // Reset the icon to blue. + ImageView icon = (ImageView) findViewById(R.id.chart_icon); + LayerDrawable layers = (LayerDrawable) icon.getDrawable(); + Drawable chart = layers.findDrawableByLayerId(R.id.main_menu_chart); + chart.setColorFilter(null); } else { DataReductionProxyUma.dataReductionProxyUIAction( DataReductionProxyUma.ACTION_MAIN_MENU_DISPLAYED_OFF);
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd index ade32fc..4caca00c 100644 --- a/chrome/android/java/strings/android_chrome_strings.grd +++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2816,6 +2816,11 @@ <ph name="BEGIN_LINK"><link></ph>Get help<ph name="END_LINK"></link></ph> </message> + <!-- Photo Picker strings --> + <message name="IDS_DECODER_DESCRIPTION" desc="The title for the image decoder utility service."> + Image decoder + </message> + <!-- Migration strings --> <message name="IDS_TAB_SWITCHER_CALLOUT_BODY" desc="Indicates that clicking the tab switcher button gives you quick access to your tabs."> Tap this button for quick access to your tabs.
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index d1d08c6..7764a071 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -784,6 +784,9 @@ "java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java", "java/src/org/chromium/chrome/browser/permissions/PermissionDialogDelegate.java", "java/src/org/chromium/chrome/browser/physicalweb/BitmapHttpRequest.java", + "java/src/org/chromium/chrome/browser/photo_picker/BitmapUtils.java", + "java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java", + "java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java", "java/src/org/chromium/chrome/browser/photo_picker/FileEnumWorkerTask.java", "java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java", "java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java", @@ -1628,6 +1631,7 @@ "javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserverTest.java", "javatests/src/org/chromium/chrome/browser/widget/findinpage/FindTest.java", "javatests/src/org/chromium/chrome/test/ParametersOnMultiTest.java", + "javatests/src/org/chromium/chrome/test/crash/IntentionalCrashTest.java", "javatests/src/org/chromium/chrome/test/util/ChromeSigninUtilsTest.java", "javatests/src/org/chromium/chrome/test/util/parameters/SigninParametersTest.java", ]
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java index c4cf630..b819925 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java
@@ -125,6 +125,7 @@ */ @LargeTest @Feature({"CopylessPaste"}) + @DisabledTest(message = "crbug.com/713895") public void testNoMeta() throws InterruptedException, TimeoutException { loadUrl(mTestServer.getURL(NODATA_PAGE)); mCallbackHelper.waitForCallback(0);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/test/crash/IntentionalCrashTest.java b/chrome/android/javatests/src/org/chromium/chrome/test/crash/IntentionalCrashTest.java new file mode 100644 index 0000000..3988cb0 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/test/crash/IntentionalCrashTest.java
@@ -0,0 +1,69 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.test.crash; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.ChromeSwitches; +import org.chromium.chrome.test.ChromeActivityTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; + +/** Tests that intentionally crash in different ways. + * + * These are all purposefully disabled and should only be run manually. + */ +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE) +public class IntentionalCrashTest { + @Rule + public ChromeActivityTestRule<ChromeActivity> mActivityTestRule = + new ChromeActivityTestRule(ChromeActivity.class); + + @DisabledTest + @Test + public void testRendererCrash() { + try { + mActivityTestRule.startMainActivityWithURL("chrome://crash"); + } catch (InterruptedException e) { + Assert.fail(e.toString()); + } + } + + @DisabledTest + @Test + public void testBrowserCrash() { + try { + mActivityTestRule.startMainActivityWithURL("chrome://inducebrowsercrashforrealz"); + } catch (InterruptedException e) { + Assert.fail(e.toString()); + } + } + + @DisabledTest + @Test + public void testJavaCrash() { + try { + mActivityTestRule.startMainActivityWithURL("chrome://java-crash"); + } catch (InterruptedException e) { + Assert.fail(e.toString()); + } + } + + @DisabledTest + @Test + public void testGpuCrash() { + try { + mActivityTestRule.startMainActivityWithURL("chrome://gpucrash"); + } catch (InterruptedException e) { + Assert.fail(e.toString()); + } + } +}
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 14c1ced..41c56ba 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -561,20 +561,6 @@ }; #endif // OS_CHROMEOS -#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || \ - defined(OS_WIN) -const FeatureEntry::Choice kAppMenuIconChoices[] = { - {flags_ui::kGenericExperimentChoiceDefault, "", ""}, - {flag_descriptions::kAppMenuIconOldBehavior, switches::kAppMenuIcon, - switches::kAppMenuIconOldBehavior}, - {flag_descriptions::kAppMenuIconPersistentOpenedState, - switches::kAppMenuIcon, switches::kAppMenuIconPersistentOpenedState}, - {flag_descriptions::kAppMenuIconPersistentClosedState, - switches::kAppMenuIcon, switches::kAppMenuIconPersistentClosedState}, -}; -#endif // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || - // defined(OS_WIN) - #if defined(OS_ANDROID) const FeatureEntry::FeatureParam kContentSuggestionsCategoryOrderFeatureVariationGeneral[] = { @@ -2638,9 +2624,9 @@ flag_descriptions::kOmniboxEntitySuggestionsName, flag_descriptions::kOmniboxEntitySuggestionsDescription, kOsDesktop, FEATURE_VALUE_TYPE(omnibox::kOmniboxEntitySuggestions)}, - {"app-menu-icon", flag_descriptions::kAppMenuIconName, - flag_descriptions::kAppMenuIconDescription, kOsDesktop, - MULTI_VALUE_TYPE(kAppMenuIconChoices)}, + {"enable-new-app-menu-icon", flag_descriptions::kEnableNewAppMenuIconName, + flag_descriptions::kEnableNewAppMenuIconDescription, kOsDesktop, + SINGLE_VALUE_TYPE(switches::kEnableNewAppMenuIcon)}, #endif // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || // defined(OS_WIN)
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index e3e4ea7..458b7189 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2783,11 +2783,9 @@ const char kPauseBackgroundTabsDescription[] = "Pause timers in background tabs after 5 minutes on desktop."; -const char kAppMenuIconName[] = "App Menu Icon"; -const char kAppMenuIconDescription[] = "Changes the icon in the app menu."; -const char kAppMenuIconOldBehavior[] = "Old Behavior"; -const char kAppMenuIconPersistentOpenedState[] = "Persistent Opened State"; -const char kAppMenuIconPersistentClosedState[] = "Persistent Closed State"; +const char kEnableNewAppMenuIconName[] = "Enable the New App Menu Icon"; +const char kEnableNewAppMenuIconDescription[] = + "Use the new app menu icon with update notification animations."; #endif // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || // defined(OS_WIN)
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 0e71765..8cc1b352 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -3016,20 +3016,11 @@ extern const char kPauseBackgroundTabsName[]; extern const char kPauseBackgroundTabsDescription[]; -// Name of the flag to change the app menu icon. -extern const char kAppMenuIconName[]; +// Name of the flag to enable the new app menu icon. +extern const char kEnableNewAppMenuIconName[]; -// Description of the flag to change the app menu icon. -extern const char kAppMenuIconDescription[]; - -// Description of the app menu icon's old appearance. -extern const char kAppMenuIconOldBehavior[]; - -// Description of the app menu animated icon's persistent opened state. -extern const char kAppMenuIconPersistentOpenedState[]; - -// Description of the app menu animated icon's persistent closed state. -extern const char kAppMenuIconPersistentClosedState[]; +// Description of the flag to enable the new app menu icon. +extern const char kEnableNewAppMenuIconDescription[]; #endif // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || // defined(OS_WIN)
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js index 6c19a60..82954ac 100644 --- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js +++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -326,15 +326,20 @@ /** @override */ resetCategoryPermissionForOrigin: function( primaryPattern, secondaryPattern, contentType, incognito) { - chrome.send('resetCategoryPermissionForOrigin', + chrome.send( + 'resetCategoryPermissionForOrigin', [primaryPattern, secondaryPattern, contentType, incognito]); }, /** @override */ setCategoryPermissionForOrigin: function( primaryPattern, secondaryPattern, contentType, value, incognito) { - chrome.send('setCategoryPermissionForOrigin', - [primaryPattern, secondaryPattern, contentType, value, incognito]); + // TODO(dschuyler): It may be incorrect for JS to send the embeddingOrigin + // pattern. Look into removing this parameter from site_settings_handler. + // Ignoring the |secondaryPattern| and using '' instead is a quick-fix. + chrome.send( + 'setCategoryPermissionForOrigin', + [primaryPattern, '', contentType, value, incognito]); }, /** @override */
diff --git a/chrome/browser/ui/views/toolbar/app_menu_animation.cc b/chrome/browser/ui/views/toolbar/app_menu_animation.cc index 8d643ca..01d0b5a 100644 --- a/chrome/browser/ui/views/toolbar/app_menu_animation.cc +++ b/chrome/browser/ui/views/toolbar/app_menu_animation.cc
@@ -60,7 +60,7 @@ void AppMenuAnimation::AppMenuDot::Paint(const gfx::PointF& center_point, SkColor start_color, - SkColor severity_color, + SkColor target_color, gfx::Canvas* canvas, const gfx::Rect& bounds, const gfx::SlideAnimation* animation) { @@ -110,8 +110,8 @@ point.Offset(-dot_width / 2, -dot_height / 2); SkColor color = is_opening ? gfx::Tween::ColorValueBetween( - color_progress, start_color, severity_color) - : severity_color; + color_progress, start_color, target_color) + : target_color; cc::PaintFlags flags; flags.setColor(color); @@ -124,10 +124,8 @@ canvas->DrawRoundRect(gfx::RectF(point, dot_size), 2.0, flags); } -AppMenuAnimation::AppMenuAnimation(AppMenuButton* owner, - bool should_animate_closed) +AppMenuAnimation::AppMenuAnimation(AppMenuButton* owner, SkColor initial_color) : owner_(owner), - should_animate_closed_(should_animate_closed), animation_(base::MakeUnique<gfx::SlideAnimation>(this)), bottom_dot_(base::TimeDelta(), kBottomWidthOpenInterval, @@ -138,8 +136,8 @@ top_dot_(base::TimeDelta::FromMilliseconds(kDotDelayMs * 2), kTopWidthOpenInterval, kTopStrokeOpenInterval), - start_color_(gfx::kPlaceholderColor), - severity_color_(gfx::kPlaceholderColor) { + start_color_(initial_color), + target_color_(initial_color) { animation_->SetSlideDuration(kOpenDurationMs); animation_->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN); } @@ -156,20 +154,14 @@ gfx::PointF bottom_point = middle_point; bottom_point.Offset(0, y_offset); - middle_dot_.Paint(middle_point, start_color_, severity_color_, canvas, bounds, + middle_dot_.Paint(middle_point, start_color_, target_color_, canvas, bounds, animation_.get()); - top_dot_.Paint(top_point, start_color_, severity_color_, canvas, bounds, + top_dot_.Paint(top_point, start_color_, target_color_, canvas, bounds, animation_.get()); - bottom_dot_.Paint(bottom_point, start_color_, severity_color_, canvas, bounds, + bottom_dot_.Paint(bottom_point, start_color_, target_color_, canvas, bounds, animation_.get()); } -void AppMenuAnimation::SetIconColors(SkColor start_color, - SkColor severity_color) { - start_color_ = start_color; - severity_color_ = severity_color; -} - void AppMenuAnimation::StartAnimation() { if (!animation_->is_animating()) { animation_->SetSlideDuration(kOpenDurationMs); @@ -179,15 +171,13 @@ } void AppMenuAnimation::AnimationEnded(const gfx::Animation* animation) { - if (animation_->IsShowing() && should_animate_closed_) { + if (animation_->IsShowing()) { animation_->SetSlideDuration(kCloseDurationMs); animation_->Hide(); - return; + } else { + start_color_ = target_color_; } - if (!animation_->IsShowing()) - start_color_ = severity_color_; - owner_->AppMenuAnimationEnded(); }
diff --git a/chrome/browser/ui/views/toolbar/app_menu_animation.h b/chrome/browser/ui/views/toolbar/app_menu_animation.h index 1ff69ee..122b1c97 100644 --- a/chrome/browser/ui/views/toolbar/app_menu_animation.h +++ b/chrome/browser/ui/views/toolbar/app_menu_animation.h
@@ -20,15 +20,14 @@ // This class is used for animating and drawing the app menu icon. class AppMenuAnimation : public gfx::AnimationDelegate { public: - AppMenuAnimation(AppMenuButton* owner, bool should_animate_closed); + AppMenuAnimation(AppMenuButton* owner, SkColor initial_color); ~AppMenuAnimation() override; // Paints the app menu icon. void PaintAppMenu(gfx::Canvas* canvas, const gfx::Rect& bounds); - // Updates the icon colors. - void SetIconColors(SkColor start_color, SkColor severity_color); + void set_target_color(SkColor target_color) { target_color_ = target_color; } // Starts the animation if it's not already running. void StartAnimation(); @@ -70,9 +69,6 @@ AppMenuButton* const owner_; - // True if the animation should close after it finishes opening. - const bool should_animate_closed_; - std::unique_ptr<gfx::SlideAnimation> animation_; AppMenuDot bottom_dot_; @@ -80,12 +76,12 @@ AppMenuDot top_dot_; // The starting color of the dots. The animation is expected to transition - // from this color to |severity_color_|. + // from this color to |target_color_|. SkColor start_color_; // The severity color of the dots. This is final color at the end of the // animation. - SkColor severity_color_; + SkColor target_color_; DISALLOW_COPY_AND_ASSIGN(AppMenuAnimation); };
diff --git a/chrome/browser/ui/views/toolbar/app_menu_button.cc b/chrome/browser/ui/views/toolbar/app_menu_button.cc index 6b3b5bd..272cc0d 100644 --- a/chrome/browser/ui/views/toolbar/app_menu_button.cc +++ b/chrome/browser/ui/views/toolbar/app_menu_button.cc
@@ -47,22 +47,16 @@ severity_(AppMenuIconController::Severity::NONE), type_(AppMenuIconController::IconType::NONE), toolbar_view_(toolbar_view), + should_use_new_icon_(false), margin_trailing_(0), weak_factory_(this) { SetInkDropMode(InkDropMode::ON); SetFocusPainter(nullptr); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kAppMenuIcon)) { - std::string flag = - command_line->GetSwitchValueASCII(switches::kAppMenuIcon); - if (flag == switches::kAppMenuIconPersistentClosedState) { - Browser* browser = toolbar_view_->browser(); - browser->tab_strip_model()->AddObserver(this); - animation_ = base::MakeUnique<AppMenuAnimation>(this, true); - } else if (flag == switches::kAppMenuIconPersistentOpenedState) { - animation_ = base::MakeUnique<AppMenuAnimation>(this, false); - } + if (command_line->HasSwitch(switches::kEnableNewAppMenuIcon)) { + toolbar_view_->browser()->tab_strip_model()->AddObserver(this); + should_use_new_icon_ = true; } } @@ -109,8 +103,7 @@ base::TimeTicks::Now() - menu_open_time); } - if (animation_ && severity_ != AppMenuIconController::Severity::NONE) - animation_->StartAnimation(); + AnimateIconIfPossible(); } void AppMenuButton::CloseMenu() { @@ -162,8 +155,7 @@ content::WebContents* contents, int index, bool foreground) { - if (severity_ != AppMenuIconController::Severity::NONE) - animation_->StartAnimation(); + AnimateIconIfPossible(); } void AppMenuButton::UpdateIcon(bool should_animate) { @@ -189,10 +181,14 @@ break; } - if (animation_) { - animation_->SetIconColors(toolbar_icon_color, severity_color); + if (should_use_new_icon_) { + if (!animation_) + animation_ = base::MakeUnique<AppMenuAnimation>(this, toolbar_icon_color); + + animation_->set_target_color(severity_color); if (should_animate) - animation_->StartAnimation(); + AnimateIconIfPossible(); + return; } @@ -221,6 +217,15 @@ InvalidateLayout(); } +void AppMenuButton::AnimateIconIfPossible() { + if (!animation_ || !should_use_new_icon_ || + severity_ == AppMenuIconController::Severity::NONE) { + return; + } + + animation_->StartAnimation(); +} + void AppMenuButton::AppMenuAnimationStarted() { SetPaintToLayer(); layer()->SetFillsBoundsOpaquely(false);
diff --git a/chrome/browser/ui/views/toolbar/app_menu_button.h b/chrome/browser/ui/views/toolbar/app_menu_button.h index 06167a7..96290b4 100644 --- a/chrome/browser/ui/views/toolbar/app_menu_button.h +++ b/chrome/browser/ui/views/toolbar/app_menu_button.h
@@ -72,6 +72,10 @@ // to make the focus rectangle centered. void SetTrailingMargin(int margin); + // Animates the icon if possible. The icon will not animate if the severity + // level is none, |animation_| is nullptr or |should_use_new_icon_| is false. + void AnimateIconIfPossible(); + // Methods called by AppMenuAnimation when the animation has started/ended. // The layer is managed inside these methods. void AppMenuAnimationStarted(); @@ -115,6 +119,9 @@ // Used for animating and drawing the app menu icon. std::unique_ptr<AppMenuAnimation> animation_; + // True if the app menu should use the new animated icon. + bool should_use_new_icon_; + // Any trailing margin to be applied. Used when the browser is in // a maximized state to extend to the full window width. int margin_trailing_;
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 53bcd053..c8a32d02 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc
@@ -1139,12 +1139,7 @@ #if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || \ defined(OS_WIN) -extern const char kAppMenuIcon[] = "app-menu-icon"; -extern const char kAppMenuIconOldBehavior[] = "old-behavior"; -extern const char kAppMenuIconPersistentOpenedState[] = - "persistent-opened-state"; -extern const char kAppMenuIconPersistentClosedState[] = - "persistent-closed-state"; +extern const char kEnableNewAppMenuIcon[] = "enable-new-app-menu-icon"; #endif bool ExtensionsDisabled(const base::CommandLine& command_line) {
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 66adeb74..a8b2b351 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h
@@ -355,10 +355,7 @@ #if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || \ defined(OS_WIN) -extern const char kAppMenuIcon[]; -extern const char kAppMenuIconOldBehavior[]; -extern const char kAppMenuIconPersistentOpenedState[]; -extern const char kAppMenuIconPersistentClosedState[]; +extern const char kEnableNewAppMenuIcon[]; #endif bool ExtensionsDisabled(const base::CommandLine& command_line);
diff --git a/components/nacl/renderer/ppb_nacl_private_impl.cc b/components/nacl/renderer/ppb_nacl_private_impl.cc index 0d50484f..8d8faeb 100644 --- a/components/nacl/renderer/ppb_nacl_private_impl.cc +++ b/components/nacl/renderer/ppb_nacl_private_impl.cc
@@ -461,8 +461,7 @@ if (nexe_file_info->handle != PP_kInvalidFileHandle) nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true); #elif defined(OS_WIN) - nexe_for_transit = IPC::PlatformFileForTransit(nexe_file_info->handle, - base::GetCurrentProcId()); + nexe_for_transit = IPC::PlatformFileForTransit(nexe_file_info->handle); #else # error Unsupported target platform. #endif
diff --git a/components/variations/variations_seed_simulator.cc b/components/variations/variations_seed_simulator.cc index ea56739..59b56643 100644 --- a/components/variations/variations_seed_simulator.cc +++ b/components/variations/variations_seed_simulator.cc
@@ -43,7 +43,7 @@ scoped_refptr<base::FieldTrial> trial( base::FieldTrial::CreateSimulatedFieldTrial( study.name(), processed_study.total_probability(), - study.default_experiment_name(), entropy_value)); + processed_study.GetDefaultExperimentName(), entropy_value)); for (int i = 0; i < study.experiment_size(); ++i) { const Study_Experiment& experiment = study.experiment(i);
diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc index 5108aa8..49def8d1 100644 --- a/content/renderer/media_recorder/video_track_recorder.cc +++ b/content/renderer/media_recorder/video_track_recorder.cc
@@ -109,6 +109,11 @@ return; #endif +#if defined(OS_ANDROID) + // See https://crbug.com/653864. + return; +#endif + content::RenderThreadImpl* const render_thread_impl = content::RenderThreadImpl::current(); if (!render_thread_impl) { @@ -127,17 +132,12 @@ gpu_factories->GetVideoEncodeAcceleratorSupportedProfiles(); for (const auto& supported_profile : vea_supported_profiles) { for (auto& codec_id_and_profile : kPreferredCodecIdAndVEAProfiles) { - const media::VideoCodecProfile codec = supported_profile.profile; -#if defined(OS_ANDROID) - // TODO(mcasas): enable other codecs, https://crbug.com/653864. - if (codec < media::VP8PROFILE_MIN || codec > media::VP8PROFILE_MAX) - continue; -#endif - if (codec >= codec_id_and_profile.min_profile && - codec <= codec_id_and_profile.max_profile) { - DVLOG(2) << "Accelerated codec found: " << media::GetProfileName(codec); - codec_id_to_profile_.insert( - std::make_pair(codec_id_and_profile.codec_id, codec)); + if (supported_profile.profile >= codec_id_and_profile.min_profile && + supported_profile.profile <= codec_id_and_profile.max_profile) { + DVLOG(2) << "Accelerated codec found: " + << media::GetProfileName(supported_profile.profile); + codec_id_to_profile_.insert(std::make_pair( + codec_id_and_profile.codec_id, supported_profile.profile)); } } }
diff --git a/content/renderer/media_recorder/video_track_recorder.h b/content/renderer/media_recorder/video_track_recorder.h index e53cef4..53a871f 100644 --- a/content/renderer/media_recorder/video_track_recorder.h +++ b/content/renderer/media_recorder/video_track_recorder.h
@@ -32,13 +32,8 @@ } // namespace media namespace video_track_recorder { -#if defined(OS_ANDROID) -const int kVEAEncoderMinResolutionWidth = 176; -const int kVEAEncoderMinResolutionHeight = 144; -#else const int kVEAEncoderMinResolutionWidth = 640; const int kVEAEncoderMinResolutionHeight = 480; -#endif } // namespace video_track_recorder namespace content {
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py index 1bc789e..4ec332b 100644 --- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py +++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -256,8 +256,6 @@ ['mac'], bug=1832) # khronos WebGL issue # Mac Retina NVIDIA - self.Fail('conformance/textures/misc/cube-map-uploads-out-of-order.html', - ['mac', ('nvidia', 0xfe9)], bug=473739) self.Fail('deqp/functional/gles3/fbomultisample*', ['mac', ('nvidia', 0xfe9)], bug=641209) self.Fail('deqp/functional/gles3/framebufferblit/' +
diff --git a/device/usb/usb_descriptors.cc b/device/usb/usb_descriptors.cc index 645ee56..ae25c1b7 100644 --- a/device/usb/usb_descriptors.cc +++ b/device/usb/usb_descriptors.cc
@@ -41,7 +41,7 @@ const uint8_t kEndpointDescriptorLength = 7; const uint8_t kInterfaceAssociationDescriptorLength = 8; -const int kControlTransferTimeout = 60000; // 1 minute +const int kControlTransferTimeoutMs = 2000; // 2 seconds struct UsbInterfaceAssociationDescriptor { UsbInterfaceAssociationDescriptor(uint8_t first_interface, @@ -123,7 +123,7 @@ UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD, UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest, kConfigurationDescriptorType << 8 | index, 0, buffer, total_length, - kControlTransferTimeout, + kControlTransferTimeoutMs, base::Bind(&OnReadConfigDescriptor, desc, closure)); } else { LOG(ERROR) << "Failed to read length for configuration " @@ -169,7 +169,7 @@ UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD, UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest, kConfigurationDescriptorType << 8 | i, 0, header, header->size(), - kControlTransferTimeout, + kControlTransferTimeoutMs, base::Bind(&OnReadConfigDescriptorHeader, device_handle, desc_ptr, i, closure)); } @@ -208,7 +208,7 @@ UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD, UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest, kStringDescriptorType << 8 | index, language_id, buffer, buffer->size(), - kControlTransferTimeout, base::Bind(&OnReadStringDescriptor, callback)); + kControlTransferTimeoutMs, base::Bind(&OnReadStringDescriptor, callback)); } void OnReadLanguageIds(scoped_refptr<UsbDeviceHandle> device_handle, @@ -503,7 +503,7 @@ UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD, UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest, kDeviceDescriptorType << 8, 0, buffer, buffer->size(), - kControlTransferTimeout, + kControlTransferTimeoutMs, base::Bind(&OnReadDeviceDescriptor, device_handle, callback)); }
diff --git a/device/usb/webusb_descriptors.cc b/device/usb/webusb_descriptors.cc index e2ebc14..3df67eb 100644 --- a/device/usb/webusb_descriptors.cc +++ b/device/usb/webusb_descriptors.cc
@@ -45,7 +45,7 @@ 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65}; -const int kControlTransferTimeout = 60000; // 1 minute +const int kControlTransferTimeoutMs = 2000; // 2 seconds using ReadWebUsbDescriptorsCallback = base::Callback<void(std::unique_ptr<WebUsbAllowedOrigins> allowed_origins, @@ -218,7 +218,7 @@ device_handle->ControlTransfer( UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR, UsbControlTransferRecipient::DEVICE, vendor_code, index, kGetUrlRequest, - buffer, buffer->size(), kControlTransferTimeout, + buffer, buffer->size(), kControlTransferTimeoutMs, base::Bind(&OnReadUrlDescriptor, url_map, index, callback)); } @@ -301,7 +301,7 @@ UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR, UsbControlTransferRecipient::DEVICE, vendor_code, 0, kGetAllowedOriginsRequest, new_buffer, new_buffer->size(), - kControlTransferTimeout, + kControlTransferTimeoutMs, base::Bind(&OnReadWebUsbAllowedOrigins, callback)); } @@ -314,7 +314,7 @@ UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR, UsbControlTransferRecipient::DEVICE, vendor_code, 0, kGetAllowedOriginsRequest, buffer, buffer->size(), - kControlTransferTimeout, + kControlTransferTimeoutMs, base::Bind(&OnReadWebUsbAllowedOriginsHeader, device_handle, callback, vendor_code)); } @@ -361,7 +361,7 @@ UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD, UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest, kBosDescriptorType << 8, 0, new_buffer, new_buffer->size(), - kControlTransferTimeout, + kControlTransferTimeoutMs, base::Bind(&OnReadBosDescriptor, device_handle, callback)); } @@ -574,7 +574,7 @@ UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD, UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest, kBosDescriptorType << 8, 0, buffer, buffer->size(), - kControlTransferTimeout, + kControlTransferTimeoutMs, base::Bind(&OnReadBosDescriptorHeader, device_handle, callback)); }
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc index db36c0f..a36f51b12 100644 --- a/ipc/ipc_message_utils.cc +++ b/ipc/ipc_message_utils.cc
@@ -35,6 +35,7 @@ #if defined(OS_WIN) #include <tchar.h> #include "ipc/handle_win.h" +#include "ipc/ipc_platform_file.h" #endif namespace IPC { @@ -842,6 +843,48 @@ } #endif // defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_WIN) +void ParamTraits<PlatformFileForTransit>::GetSize(base::PickleSizer* s, + const param_type& p) { + GetParamSize(s, p.IsValid()); + if (p.IsValid()) + GetParamSize(s, p.GetHandle()); +} + +void ParamTraits<PlatformFileForTransit>::Write(base::Pickle* m, + const param_type& p) { + m->WriteBool(p.IsValid()); + if (p.IsValid()) { + HandleWin handle_win(p.GetHandle(), HandleWin::DUPLICATE); + ParamTraits<HandleWin>::Write(m, handle_win); + ::CloseHandle(p.GetHandle()); + } +} + +bool ParamTraits<PlatformFileForTransit>::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r) { + bool is_valid; + if (!iter->ReadBool(&is_valid)) + return false; + if (!is_valid) { + *r = PlatformFileForTransit(); + return true; + } + + HandleWin handle_win; + if (!ParamTraits<HandleWin>::Read(m, iter, &handle_win)) + return false; + *r = PlatformFileForTransit(handle_win.get_handle()); + return true; +} + +void ParamTraits<PlatformFileForTransit>::Log(const param_type& p, + std::string* l) { + LogParam(p.GetHandle(), l); +} +#endif // defined(OS_WIN) + void ParamTraits<base::FilePath>::GetSize(base::PickleSizer* sizer, const param_type& p) { p.GetSizeForPickle(sizer);
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index 847a617..dd6755e 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h
@@ -49,6 +49,10 @@ struct ChannelHandle; +#if defined(OS_WIN) +class PlatformFileForTransit; +#endif + // ----------------------------------------------------------------------------- // How we send IPC message logs across channels. struct IPC_EXPORT LogData { @@ -593,6 +597,19 @@ static void Log(const param_type& p, std::string* l); }; +#if defined(OS_WIN) +template <> +struct IPC_EXPORT ParamTraits<PlatformFileForTransit> { + typedef PlatformFileForTransit param_type; + static void GetSize(base::PickleSizer* sizer, const param_type& p); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; +#endif // defined(OS_WIN) + template <> struct IPC_EXPORT ParamTraits<base::FilePath> { typedef base::FilePath param_type;
diff --git a/ipc/ipc_platform_file.cc b/ipc/ipc_platform_file.cc index dbcfddc..db5f8e5 100644 --- a/ipc/ipc_platform_file.cc +++ b/ipc/ipc_platform_file.cc
@@ -11,6 +11,32 @@ namespace IPC { +#if defined(OS_WIN) +PlatformFileForTransit::PlatformFileForTransit() : handle_(nullptr) {} + +PlatformFileForTransit::PlatformFileForTransit(HANDLE handle) + : handle_(handle) {} + +bool PlatformFileForTransit::operator==( + const PlatformFileForTransit& platform_file) const { + return handle_ == platform_file.handle_; +} + +bool PlatformFileForTransit::operator!=( + const PlatformFileForTransit& platform_file) const { + return !(*this == platform_file); +} + +HANDLE PlatformFileForTransit::GetHandle() const { + return handle_; +} + +bool PlatformFileForTransit::IsValid() const { + return handle_ != nullptr; +} + +#endif // defined(OS_WIN) + PlatformFileForTransit GetPlatformFileForTransit(base::PlatformFile handle, bool close_source_handle) { #if defined(OS_WIN) @@ -24,10 +50,7 @@ return IPC::InvalidPlatformFileForTransit(); } - IPC::PlatformFileForTransit out_handle = IPC::PlatformFileForTransit( - raw_handle, base::GetCurrentProcId()); - out_handle.SetOwnershipPassesToIPC(true); - return out_handle; + return IPC::PlatformFileForTransit(raw_handle); #elif defined(OS_POSIX) // If asked to close the source, we can simply re-use the source fd instead of // dup()ing and close()ing.
diff --git a/ipc/ipc_platform_file.h b/ipc/ipc_platform_file.h index 15807f6..ea295af8 100644 --- a/ipc/ipc_platform_file.h +++ b/ipc/ipc_platform_file.h
@@ -14,17 +14,32 @@ #include "base/file_descriptor_posix.h" #endif -#if defined(OS_WIN) -#include "base/memory/shared_memory_handle.h" -#endif - namespace IPC { #if defined(OS_WIN) -// The semantics for IPC transfer of a SharedMemoryHandle are exactly the same -// as for a PlatformFileForTransit. The object wraps a HANDLE, and has some -// metadata that indicates the process to which the HANDLE belongs. -using PlatformFileForTransit = base::SharedMemoryHandle; +class IPC_EXPORT PlatformFileForTransit { + public: + // Creates an invalid platform file. + PlatformFileForTransit(); + + // Creates a platform file that takes unofficial ownership of |handle|. Note + // that ownership is not handled by a Scoped* class due to usage patterns of + // this class and its POSIX counterpart [base::FileDescriptor]. When this + // class is used as an input to an IPC message, the IPC subsystem will close + // |handle|. When this class is used as the output from an IPC message, the + // receiver is expected to take ownership of |handle|. + explicit PlatformFileForTransit(HANDLE handle); + + // Comparison operators. + bool operator==(const PlatformFileForTransit& platform_file) const; + bool operator!=(const PlatformFileForTransit& platform_file) const; + + HANDLE GetHandle() const; + bool IsValid() const; + + private: + HANDLE handle_; +}; #elif defined(OS_POSIX) typedef base::FileDescriptor PlatformFileForTransit; #endif
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java index bf6f1bd9..80b233fb 100644 --- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java +++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
@@ -533,13 +533,10 @@ } builder.setFocusMode(jniFocusMode); - // Auto Exposure is the usual capability and state, unless AE is not available at all, which - // is signalled by the absence of Metering Areas. Exposure Compensation can also support or - // be locked, this is equivalent to AndroidMeteringMode.FIXED. + // Auto Exposure is understood to be supported always; besides that, only "locked" + // (equivalent to AndroidMeteringMode.FIXED) may be supported and/or configured. ArrayList<Integer> jniExposureModes = new ArrayList<Integer>(2); - if (parameters.getMaxNumMeteringAreas() > 0) { - jniExposureModes.add(AndroidMeteringMode.CONTINUOUS); - } + jniExposureModes.add(AndroidMeteringMode.CONTINUOUS); if (parameters.isAutoExposureLockSupported()) { jniExposureModes.add(AndroidMeteringMode.FIXED); }
diff --git a/mojo/edk/js/tests/OWNERS b/mojo/edk/js/tests/OWNERS new file mode 100644 index 0000000..6c1f2fb --- /dev/null +++ b/mojo/edk/js/tests/OWNERS
@@ -0,0 +1,6 @@ +per-file *.mojom=file://mojo/OWNERS + +# These mojom test files don't really require security review, but we need to +# add these to please PRESUBMIT. +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/mojo/edk/js/tests/js_to_cpp.mojom b/mojo/edk/js/tests/js_to_cpp.mojom index 688b22b3..3e4bd1a2 100644 --- a/mojo/edk/js/tests/js_to_cpp.mojom +++ b/mojo/edk/js/tests/js_to_cpp.mojom
@@ -28,6 +28,8 @@ EchoArgs? item; }; +interface ForTesting {}; + // Note: For messages which control test flow, pick numbers that are unlikely // to be hit as a result of our deliberate corruption of response messages. interface CppSide { @@ -40,7 +42,11 @@ // Responses from specific tests. PingResponse(); EchoResponse(EchoArgsList list); - BitFlipResponse(EchoArgsList arg); + + // Having an associated interface pointer in the message makes sure the + // message header version 2 is tested. + BitFlipResponse(EchoArgsList arg, associated ForTesting? not_used); + BackPointerResponse(EchoArgsList arg); };
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc index b6b74e31..d9fe22d 100644 --- a/mojo/edk/js/tests/js_to_cpp_tests.cc +++ b/mojo/edk/js/tests/js_to_cpp_tests.cc
@@ -239,7 +239,9 @@ mishandled_messages_ += 1; } - void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override { + void BitFlipResponse( + js_to_cpp::EchoArgsListPtr list, + js_to_cpp::ForTestingAssociatedPtrInfo not_used) override { mishandled_messages_ += 1; } @@ -332,7 +334,9 @@ // js_to_cpp::CppSide: void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); } - void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override { + void BitFlipResponse( + js_to_cpp::EchoArgsListPtr list, + js_to_cpp::ForTestingAssociatedPtrInfo not_used) override { CheckCorruptedEchoArgsList(list); }
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/mojo/edk/js/tests/js_to_cpp_tests.js index 58592f8..6ffce09 100644 --- a/mojo/edk/js/tests/js_to_cpp_tests.js +++ b/mojo/edk/js/tests/js_to_cpp_tests.js
@@ -114,7 +114,7 @@ writeMessagePipe(messagePipe, sampleMessage); arg.message_handle = messagePipe.handle1; - this.cppSide_.bitFlipResponse(createEchoArgsList(arg)); + this.cppSide_.bitFlipResponse(createEchoArgsList(arg), null); core.close(messagePipe.handle0); iteration += 1;
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc index e5f3808..50d6d67 100644 --- a/mojo/public/cpp/bindings/lib/message.cc +++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -80,6 +80,7 @@ if (version() < 2) return data() + header()->num_bytes; + DCHECK(!header_v2()->payload.is_null()); return static_cast<const uint8_t*>(header_v2()->payload.Get()); } @@ -89,17 +90,14 @@ if (version() < 2) { num_bytes = data_num_bytes() - header()->num_bytes; } else { - auto payload = reinterpret_cast<uintptr_t>(header_v2()->payload.Get()); - if (!payload) { - num_bytes = 0; - } else { - auto payload_end = - reinterpret_cast<uintptr_t>(header_v2()->payload_interface_ids.Get()); - if (!payload_end) - payload_end = reinterpret_cast<uintptr_t>(data() + data_num_bytes()); - DCHECK_GE(payload_end, payload); - num_bytes = payload_end - payload; - } + auto payload_begin = + reinterpret_cast<uintptr_t>(header_v2()->payload.Get()); + auto payload_end = + reinterpret_cast<uintptr_t>(header_v2()->payload_interface_ids.Get()); + if (!payload_end) + payload_end = reinterpret_cast<uintptr_t>(data() + data_num_bytes()); + DCHECK_GE(payload_end, payload_begin); + num_bytes = payload_end - payload_begin; } DCHECK_LE(num_bytes, std::numeric_limits<uint32_t>::max()); return static_cast<uint32_t>(num_bytes);
diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.cc b/mojo/public/cpp/bindings/lib/message_header_validator.cc index 9f8c6278..1f23256 100644 --- a/mojo/public/cpp/bindings/lib/message_header_validator.cc +++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -73,9 +73,11 @@ // payload size). // - Validation of the payload contents will be done separately based on the // payload type. - if (!header_v2->payload.is_null() && - (!internal::ValidatePointer(header_v2->payload, validation_context) || - !validation_context->ClaimMemory(header_v2->payload.Get(), 1))) { + if (!internal::ValidatePointerNonNullable(header_v2->payload, + "null payload in message header", + validation_context) || + !internal::ValidatePointer(header_v2->payload, validation_context) || + !validation_context->ClaimMemory(header_v2->payload.Get(), 1)) { return false; }
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc index 0701f6a1..bdf9326 100644 --- a/net/quic/chromium/quic_chromium_client_session.cc +++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -321,17 +321,9 @@ CloseAllStreams(ERR_UNEXPECTED); DCHECK(observers_.empty()); CloseAllObservers(ERR_UNEXPECTED); + CancelAllRequests(ERR_UNEXPECTED); connection()->set_debug_visitor(nullptr); - - UMA_HISTOGRAM_COUNTS_1000("Net.QuicSession.AbortedPendingStreamRequests", - stream_requests_.size()); - - while (!stream_requests_.empty()) { - StreamRequest* request = stream_requests_.front(); - stream_requests_.pop_front(); - request->OnRequestCompleteFailure(ERR_ABORTED); - } } if (connection()->connected()) { @@ -1013,6 +1005,7 @@ DCHECK(dynamic_streams().empty()); CloseAllStreams(ERR_UNEXPECTED); CloseAllObservers(ERR_UNEXPECTED); + CancelAllRequests(ERR_CONNECTION_CLOSED); NotifyFactoryOfSessionClosedLater(); } @@ -1270,6 +1263,17 @@ } } +void QuicChromiumClientSession::CancelAllRequests(int net_error) { + UMA_HISTOGRAM_COUNTS_1000("Net.QuicSession.AbortedPendingStreamRequests", + stream_requests_.size()); + + while (!stream_requests_.empty()) { + StreamRequest* request = stream_requests_.front(); + stream_requests_.pop_front(); + request->OnRequestCompleteFailure(net_error); + } +} + std::unique_ptr<base::Value> QuicChromiumClientSession::GetInfoAsValue( const std::set<HostPortPair>& aliases) { std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
diff --git a/net/quic/chromium/quic_chromium_client_session.h b/net/quic/chromium/quic_chromium_client_session.h index a4c07d1..2622653 100644 --- a/net/quic/chromium/quic_chromium_client_session.h +++ b/net/quic/chromium/quic_chromium_client_session.h
@@ -351,6 +351,7 @@ void CloseAllStreams(int net_error); void CloseAllObservers(int net_error); + void CancelAllRequests(int net_error); // Notifies the factory that this session is going away and no more streams // should be created from it. This needs to be called before closing any
diff --git a/net/quic/chromium/quic_chromium_client_session_test.cc b/net/quic/chromium/quic_chromium_client_session_test.cc index ffae007..3d85ddd 100644 --- a/net/quic/chromium/quic_chromium_client_session_test.cc +++ b/net/quic/chromium/quic_chromium_client_session_test.cc
@@ -318,6 +318,64 @@ EXPECT_TRUE(quic_data.AllWriteDataConsumed()); } +TEST_P(QuicChromiumClientSessionTest, ConnectionCloseBeforeStreamRequest) { + MockQuicData quic_data; + quic_data.AddWrite(client_maker_.MakeInitialSettingsPacket(1, nullptr)); + quic_data.AddRead(server_maker_.MakeConnectionClosePacket(1)); + quic_data.AddSocketDataToFactory(&socket_factory_); + + Initialize(); + CompleteCryptoHandshake(); + + // Pump the message loop to read the connection close packet. + base::RunLoop().RunUntilIdle(); + + // Request a stream and verify that it failed. + std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request = + session_->CreateStreamRequest(); + TestCompletionCallback callback; + ASSERT_EQ(ERR_CONNECTION_CLOSED, + stream_request->StartRequest(callback.callback())); + + EXPECT_TRUE(quic_data.AllReadDataConsumed()); + EXPECT_TRUE(quic_data.AllWriteDataConsumed()); +} + +TEST_P(QuicChromiumClientSessionTest, ConnectionCloseWithPendingStreamRequest) { + MockQuicData quic_data; + quic_data.AddWrite(client_maker_.MakeInitialSettingsPacket(1, nullptr)); + quic_data.AddRead(ASYNC, ERR_IO_PENDING); + quic_data.AddRead(server_maker_.MakeConnectionClosePacket(1)); + quic_data.AddSocketDataToFactory(&socket_factory_); + + Initialize(); + CompleteCryptoHandshake(); + + // Open the maximum number of streams so that a subsequent request + // can not proceed immediately. + const size_t kMaxOpenStreams = session_->max_open_outgoing_streams(); + for (size_t i = 0; i < kMaxOpenStreams; i++) { + session_->CreateOutgoingDynamicStream(kDefaultPriority); + } + EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams()); + + // Request a stream and verify that it's pending. + std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request = + session_->CreateStreamRequest(); + TestCompletionCallback callback; + ASSERT_EQ(ERR_IO_PENDING, stream_request->StartRequest(callback.callback())); + + // Close the connection and verify that the StreamRequest completes with + // an error. + quic_data.Resume(); + base::RunLoop().RunUntilIdle(); + + EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_CLOSED)); + + EXPECT_TRUE(quic_data.AllReadDataConsumed()); + EXPECT_TRUE(quic_data.AllWriteDataConsumed()); +} + TEST_P(QuicChromiumClientSessionTest, MaxNumStreams) { MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; std::unique_ptr<QuicEncryptedPacket> settings_packet(
diff --git a/net/quic/core/crypto/quic_compressed_certs_cache.cc b/net/quic/core/crypto/quic_compressed_certs_cache.cc index c9ba5301..84a58fa 100644 --- a/net/quic/core/crypto/quic_compressed_certs_cache.cc +++ b/net/quic/core/crypto/quic_compressed_certs_cache.cc
@@ -19,7 +19,9 @@ } // namespace QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts() - : chain(nullptr) {} + : chain(nullptr), + client_common_set_hashes(nullptr), + client_cached_cert_hashes(nullptr) {} QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts( const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
diff --git a/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt b/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt index da9344f..4fb5fe1 100644 --- a/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt
@@ -80,6 +80,7 @@ perf_ui persistence platform + product_registry protocol sdk services @@ -118,6 +119,7 @@ perf_ui persistence platform + product_registry protocol quick_open sdk @@ -159,6 +161,7 @@ perf_ui persistence platform + product_registry protocol quick_open sdk
diff --git a/third_party/WebKit/LayoutTests/inspector/sha1.html b/third_party/WebKit/LayoutTests/inspector/sha1.html index 8c0e2425..d018787 100644 --- a/third_party/WebKit/LayoutTests/inspector/sha1.html +++ b/third_party/WebKit/LayoutTests/inspector/sha1.html
@@ -5,18 +5,18 @@ <script> function initialize_ProductRegistry() { - InspectorTest.preloadModule('product_registry'); + InspectorTest.preloadModule('product_registry_impl'); } function test() { - InspectorTest.addResult('foobar : ' + ProductRegistry.sha1('foobar')); - InspectorTest.addResult('hello : ' + ProductRegistry.sha1('hello')); - InspectorTest.addResult('abcdefghijklmnopqrstuvwxyz : ' + ProductRegistry.sha1('abcdefghijklmnopqrstuvwxyz')); - InspectorTest.addResult('ABCDEFGHIJKLMNOPQRSTUVWXYZ : ' + ProductRegistry.sha1('ABCDEFGHIJKLMNOPQRSTUVWXYZ')); - InspectorTest.addResult('a : ' + ProductRegistry.sha1('a')); - InspectorTest.addResult('A : ' + ProductRegistry.sha1('A')); - InspectorTest.addResult('A1 : ' + ProductRegistry.sha1('A1')); + InspectorTest.addResult('foobar : ' + ProductRegistryImpl.sha1('foobar')); + InspectorTest.addResult('hello : ' + ProductRegistryImpl.sha1('hello')); + InspectorTest.addResult('abcdefghijklmnopqrstuvwxyz : ' + ProductRegistryImpl.sha1('abcdefghijklmnopqrstuvwxyz')); + InspectorTest.addResult('ABCDEFGHIJKLMNOPQRSTUVWXYZ : ' + ProductRegistryImpl.sha1('ABCDEFGHIJKLMNOPQRSTUVWXYZ')); + InspectorTest.addResult('a : ' + ProductRegistryImpl.sha1('a')); + InspectorTest.addResult('A : ' + ProductRegistryImpl.sha1('A')); + InspectorTest.addResult('A1 : ' + ProductRegistryImpl.sha1('A1')); InspectorTest.completeTest(); }
diff --git a/third_party/WebKit/Source/core/dom/DOMImplementation.cpp b/third_party/WebKit/Source/core/dom/DOMImplementation.cpp index 54d9e626..9a23b1c 100644 --- a/third_party/WebKit/Source/core/dom/DOMImplementation.cpp +++ b/third_party/WebKit/Source/core/dom/DOMImplementation.cpp
@@ -175,7 +175,7 @@ if (mime_type.StartsWith("application/json", kTextCaseASCIIInsensitive)) return true; if (mime_type.StartsWith("application/", kTextCaseASCIIInsensitive)) { - size_t subtype = mime_type.Find("+json", 12, kTextCaseASCIIInsensitive); + size_t subtype = mime_type.FindIgnoringASCIICase("+json", 12); if (subtype != kNotFound) { // Just check that a parameter wasn't matched. size_t parameter_marker = mime_type.Find(";");
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserIdioms.cpp b/third_party/WebKit/Source/core/html/parser/HTMLParserIdioms.cpp index 29e888e..59c3063 100644 --- a/third_party/WebKit/Source/core/html/parser/HTMLParserIdioms.cpp +++ b/third_party/WebKit/Source/core/html/parser/HTMLParserIdioms.cpp
@@ -358,7 +358,7 @@ unsigned length = value.length(); while (pos < length) { - pos = value.Find(kCharsetString, pos, kTextCaseASCIIInsensitive); + pos = value.FindIgnoringASCIICase(kCharsetString, pos); if (pos == kNotFound) break;
diff --git a/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp b/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp index 0093c10..cc72791 100644 --- a/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp +++ b/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp
@@ -907,14 +907,12 @@ bool XSSAuditor::IsContainedInRequest(const String& decoded_snippet) { if (decoded_snippet.IsEmpty()) return false; - if (decoded_url_.Find(decoded_snippet, 0, kTextCaseUnicodeInsensitive) != - kNotFound) + if (decoded_url_.FindIgnoringCase(decoded_snippet, 0) != kNotFound) return true; if (decoded_http_body_suffix_tree_ && !decoded_http_body_suffix_tree_->MightContain(decoded_snippet)) return false; - return decoded_http_body_.Find(decoded_snippet, 0, - kTextCaseUnicodeInsensitive) != kNotFound; + return decoded_http_body_.FindIgnoringCase(decoded_snippet, 0) != kNotFound; } bool XSSAuditor::IsLikelySafeResource(const String& url) {
diff --git a/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp index a1eb1ff5..ed98379 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp
@@ -1009,14 +1009,13 @@ AttributeCollection attributes = element->Attributes(); for (auto& attribute : attributes) { // Add attribute pair - if (attribute.LocalName().Find(whitespace_trimmed_query, 0, - kTextCaseUnicodeInsensitive) != - kNotFound) { + if (attribute.LocalName().FindIgnoringCase(whitespace_trimmed_query, + 0) != kNotFound) { result_collector.insert(node); break; } - size_t found_position = attribute.Value().Find( - attribute_query, 0, kTextCaseUnicodeInsensitive); + size_t found_position = + attribute.Value().FindIgnoringCase(attribute_query, 0); if (found_position != kNotFound) { if (!exact_attribute_match || (!found_position &&
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h index c726bbd..bf835100 100644 --- a/third_party/WebKit/Source/core/layout/LayoutObject.h +++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -1810,7 +1810,9 @@ // The following non-const functions for ObjectPaintProperties should only // be called from PaintPropertyTreeBuilder. ObjectPaintProperties& EnsurePaintProperties() { - return layout_object_.EnsureRarePaintData().EnsurePaintProperties(); + return layout_object_.EnsureRarePaintData() + .EnsureFragment() + .EnsurePaintProperties(); } ObjectPaintProperties* PaintProperties() { if (auto* paint_data = layout_object_.GetRarePaintData()) @@ -1818,8 +1820,10 @@ return nullptr; } void ClearPaintProperties() { - if (auto* paint_data = layout_object_.GetRarePaintData()) - paint_data->ClearPaintProperties(); + if (auto* paint_data = layout_object_.GetRarePaintData()) { + if (auto* fragment = paint_data->Fragment()) + fragment->ClearPaintProperties(); + } } // The following non-const functions for local border box properties should
diff --git a/third_party/WebKit/Source/core/paint/BUILD.gn b/third_party/WebKit/Source/core/paint/BUILD.gn index c5dad31..b460868 100644 --- a/third_party/WebKit/Source/core/paint/BUILD.gn +++ b/third_party/WebKit/Source/core/paint/BUILD.gn
@@ -56,6 +56,8 @@ "FirstMeaningfulPaintDetector.h", "FloatClipRecorder.cpp", "FloatClipRecorder.h", + "FragmentData.cpp", + "FragmentData.h", "FramePainter.cpp", "FramePainter.h", "FrameSetPainter.cpp",
diff --git a/third_party/WebKit/Source/core/paint/FragmentData.cpp b/third_party/WebKit/Source/core/paint/FragmentData.cpp new file mode 100644 index 0000000..43e3e74 --- /dev/null +++ b/third_party/WebKit/Source/core/paint/FragmentData.cpp
@@ -0,0 +1,20 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/paint/FragmentData.h" +#include "core/paint/ObjectPaintProperties.h" + +namespace blink { + +ObjectPaintProperties& FragmentData::EnsurePaintProperties() { + if (!paint_properties_) + paint_properties_ = ObjectPaintProperties::Create(); + return *paint_properties_.get(); +} + +void FragmentData::ClearPaintProperties() { + paint_properties_.reset(nullptr); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/FragmentData.h b/third_party/WebKit/Source/core/paint/FragmentData.h new file mode 100644 index 0000000..8951c32 --- /dev/null +++ b/third_party/WebKit/Source/core/paint/FragmentData.h
@@ -0,0 +1,41 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FragmentData_h +#define FragmentData_h + +#include "core/paint/ObjectPaintProperties.h" + +namespace blink { + +class ObjectPaintProperties; + +// Represents the data for a particular fragment of a LayoutObject. +// Only LayoutObjects with a self-painting PaintLayer may have more than one +// FragmentDeta, and even then only when they are inside of multicol. +// See README.md. +class CORE_EXPORT FragmentData { + public: + static std::unique_ptr<FragmentData> Create() { + return WTF::WrapUnique(new FragmentData()); + } + + ObjectPaintProperties* PaintProperties() const { + return paint_properties_.get(); + } + ObjectPaintProperties& EnsurePaintProperties(); + void ClearPaintProperties(); + + FragmentData* NextFragment() { return next_fragment_.get(); } + + private: + // Holds references to the paint property nodes created by this object. + std::unique_ptr<ObjectPaintProperties> paint_properties_; + + std::unique_ptr<FragmentData> next_fragment_; +}; + +} // namespace blink + +#endif // FragmentData_h
diff --git a/third_party/WebKit/Source/core/paint/README.md b/third_party/WebKit/Source/core/paint/README.md index 34d5d57..b30e782 100644 --- a/third_party/WebKit/Source/core/paint/README.md +++ b/third_party/WebKit/Source/core/paint/README.md
@@ -11,14 +11,14 @@ ### Stacked elements and stacking contexts -This chapter is basically a clarification of [CSS 2.1 appendix E. Elaborate description -of Stacking Contexts](http://www.w3.org/TR/CSS21/zindex.html). +This chapter is basically a clarification of [CSS 2.1 appendix E. Elaborate +description of Stacking Contexts](http://www.w3.org/TR/CSS21/zindex.html). -Note: we use 'element' instead of 'object' in this chapter to keep consistency with -the spec. We use 'object' in other places in this document. +Note: we use 'element' instead of 'object' in this chapter to keep consistency +with the spec. We use 'object' in other places in this document. -According to the documentation, we can have the following types of elements that are -treated in different ways during painting: +According to the documentation, we can have the following types of elements that +are treated in different ways during painting: * Stacked objects: objects that are z-ordered in stacking contexts, including: @@ -30,27 +30,30 @@ managed by real stacking contexts. They are positioned elements with `z-index: auto` (E.2.8 in the documentation). - They must be managed by the enclosing stacking context as stacked elements - because `z-index:auto` and `z-index:0` are considered equal for stacking - context sorting and they may interleave by DOM order. + They must be managed by the enclosing stacking context as stacked + elements because `z-index:auto` and `z-index:0` are considered equal for + stacking context sorting and they may interleave by DOM order. - The difference of a stacked element of this type from a real stacking context - is that it doesn't manage z-ordering of stacked descendants. These descendants - are managed by the parent stacking context of this stacked element. + The difference of a stacked element of this type from a real stacking + context is that it doesn't manage z-ordering of stacked descendants. + These descendants are managed by the parent stacking context of this + stacked element. - "Stacked element" is not defined as a formal term in the documentation, but we found - it convenient to use this term to refer to any elements participating z-index ordering - in stacking contexts. + "Stacked element" is not defined as a formal term in the documentation, but + we found it convenient to use this term to refer to any elements + participating z-index ordering in stacking contexts. - A stacked element is represented by a `PaintLayerStackingNode` associated with a - `PaintLayer`. It's painted as self-painting `PaintLayer`s by `PaintLayerPainter` - by executing all of the steps of the painting algorithm explained in the documentation - for the element. When painting a stacked element of the second type, we don't - paint its stacked descendants which are managed by the parent stacking context. + A stacked element is represented by a `PaintLayerStackingNode` associated + with a `PaintLayer`. It's painted as self-painting `PaintLayer`s by + `PaintLayerPainter` + by executing all of the steps of the painting algorithm explained in the + documentation for the element. When painting a stacked element of the second + type, we don't paint its stacked descendants which are managed by the parent + stacking context. -* Non-stacked pseudo stacking contexts: elements that are not stacked, but paint - their descendants (excluding any stacked contents) as if they created stacking - contexts. This includes +* Non-stacked pseudo stacking contexts: elements that are not stacked, but + paint their descendants (excluding any stacked contents) as if they created + stacking contexts. This includes * inline blocks, inline tables, inline-level replaced elements (E.2.7.2.1.4 in the documentation) @@ -59,52 +62,52 @@ * [grid items](http://www.w3.org/TR/css-grid-1/#z-order) * custom scrollbar parts - They are painted by `ObjectPainter::paintAllPhasesAtomically()` which executes - all of the steps of the painting algorithm explained in the documentation, except - ignores any descendants which are positioned or have non-auto z-index (which is - achieved by skipping descendants with self-painting layers). + They are painted by `ObjectPainter::paintAllPhasesAtomically()` which + executes all of the steps of the painting algorithm explained in the + documentation, except ignores any descendants which are positioned or have + non-auto z-index (which is achieved by skipping descendants with + self-painting layers). * Other normal elements. ### Other glossaries -* Paint container: the parent of an object for painting, as defined by [CSS2.1 spec - for painting]((http://www.w3.org/TR/CSS21/zindex.html)). For regular objects, - this is the parent in the DOM. For stacked objects, it's the containing stacking - context-inducing object. +* Paint container: the parent of an object for painting, as defined by + [CSS2.1 spec for painting]((http://www.w3.org/TR/CSS21/zindex.html)). For + regular objects, this is the parent in the DOM. For stacked objects, it's + the containing stacking context-inducing object. -* Paint container chain: the chain of paint ancestors between an element and the - root of the page. +* Paint container chain: the chain of paint ancestors between an element and + the root of the page. * Compositing container: an implementation detail of Blink, which uses - `PaintLayer`s to represent some layout objects. It is the ancestor along the paint - ancestor chain which has a PaintLayer. Implemented in - `PaintLayer::compositingContainer()`. Think of it as skipping intermediate normal - objects and going directly to the containing stacked object. + `PaintLayer`s to represent some layout objects. It is the ancestor along the + paint ancestor chain which has a PaintLayer. Implemented in + `PaintLayer::compositingContainer()`. Think of it as skipping intermediate + normal objects and going directly to the containing stacked object. -* Compositing container chain: same as paint chain, but for compositing container. +* Compositing container chain: same as paint chain, but for compositing + container. -* Paint invalidation container: the nearest object on the compositing container - chain which is composited. +* Paint invalidation container: the nearest object on the compositing + container chain which is composited. * Visual rect: the bounding box of all pixels that will be painted by a display item client. ## Paint invalidation -Paint invalidation marks anything that need to be painted differently from the original -cached painting. +Paint invalidation marks anything that need to be painted differently from the +original cached painting. ### Slimming paint v1 -Though described in this document, most of the actual paint invalidation code is under -`Source/core/layout`. - -Paint invalidation is a document cycle stage after compositing update and before paint. -During the previous stages, objects are marked for needing paint invalidation checking -if needed by style change, layout change, compositing change, etc. In paint invalidation stage, -we traverse the layout tree in pre-order, crossing frame boundaries, for marked subtrees -and objects and send the following information to `GraphicsLayer`s and `PaintController`s: +Paint invalidation is a document cycle stage after compositing update and before +paint. During the previous stages, objects are marked for needing paint +invalidation checking if needed by style change, layout change, compositing +change, etc. In paint invalidation stage, we traverse the layout tree in +pre-order, crossing frame boundaries, for marked subtrees and objects and send +the following information to `GraphicsLayer`s and `PaintController`s: * invalidated display item clients: must invalidate all display item clients that will generate different display items. @@ -115,163 +118,214 @@ #### `PaintInvalidationState` -`PaintInvalidationState` is an optimization used during the paint invalidation phase. Before -the paint invalidation tree walk, a root `PaintInvalidationState` is created for the root -`LayoutView`. During the tree walk, one `PaintInvalidationState` is created for each visited -object based on the `PaintInvalidationState` passed from the parent object. -It tracks the following information to provide O(1) complexity access to them if possible: +`PaintInvalidationState` is an optimization used during the paint invalidation +phase. Before the paint invalidation tree walk, a root `PaintInvalidationState` +is created for the root `LayoutView`. During the tree walk, one +`PaintInvalidationState` is created for each visited object based on the +`PaintInvalidationState` passed from the parent object. It tracks the following +information to provide O(1) complexity access to them if possible: -* Paint invalidation container: Since as indicated by the definitions in [Glossaries](#Other glossaries), - the paint invalidation container for stacked objects can differ from normal objects, we - have to track both separately. Here is an example: +* Paint invalidation container: Since as indicated by the definitions in + [Glossaries](#Other glossaries), the paint invalidation container for + stacked objects can differ from normal objects, we have to track both + separately. Here is an example: <div style="overflow: scroll"> <div id=A style="position: absolute"></div> <div id=B></div> </div> - If the scroller is composited (for high-DPI screens for example), it is the paint invalidation - container for div B, but not A. + If the scroller is composited (for high-DPI screens for example), it is the + paint invalidation container for div B, but not A. -* Paint offset and clip rect: if possible, `PaintInvalidationState` accumulates paint offsets - and overflow clipping rects from the paint invalidation container to provide O(1) complexity to - map a point or a rect in current object's local space to paint invalidation container's space. - Because locations of objects are determined by their containing blocks, and the containing block - for absolute-position objects differs from non-absolute, we track paint offsets and overflow - clipping rects for absolute-position objects separately. +* Paint offset and clip rect: if possible, `PaintInvalidationState` + accumulates paint offsets and overflow clipping rects from the paint + invalidation container to provide O(1) complexity to map a point or a rect + in current object's local space to paint invalidation container's space. + Because locations of objects are determined by their containing blocks, and + the containing block for absolute-position objects differs from + non-absolute, we track paint offsets and overflow clipping rects for + absolute-position objects separately. -In cases that accurate accumulation of paint offsets and clipping rects is impossible, -we will fall back to slow-path using `LayoutObject::localToAncestorPoint()` or -`LayoutObject::mapToVisualRectInAncestorSpace()`. This includes the following cases: +In cases that accurate accumulation of paint offsets and clipping rects is +impossible, we will fall back to slow-path using +`LayoutObject::localToAncestorPoint()` or +`LayoutObject::mapToVisualRectInAncestorSpace()`. This includes the following +cases: -* An object has transform related property, is multi-column or has flipped blocks writing-mode, - causing we can't simply accumulate paint offset for mapping a local rect to paint invalidation - container; +* An object has transform related property, is multi-column or has flipped + blocks writing-mode, causing we can't simply accumulate paint offset for + mapping a local rect to paint invalidation container; * An object has has filter (including filter induced by reflection), which needs to expand visual rect for descendants, because currently we don't include and filter extents into visual overflow; -* For a fixed-position object we calculate its offset using `LayoutObject::localToAncestorPoint()`, - but map for its descendants in fast-path if no other things prevent us from doing this; +* For a fixed-position object we calculate its offset using + `LayoutObject::localToAncestorPoint()`, but map for its descendants in + fast-path if no other things prevent us from doing this; -* Because we track paint offset from the normal paint invalidation container only, if we are going - to use `m_paintInvalidationContainerForStackedContents` and it's different from the normal paint - invalidation container, we have to force slow-path because the accumulated paint offset is not - usable; +* Because we track paint offset from the normal paint invalidation container + only, if we are going to use + `m_paintInvalidationContainerForStackedContents` and it's different from the + normal paint invalidation container, we have to force slow-path because the + accumulated paint offset is not usable; -* We also stop to track paint offset and clipping rect for absolute-position objects when - `m_paintInvalidationContainerForStackedContents` becomes different from `m_paintInvalidationContainer`. +* We also stop to track paint offset and clipping rect for absolute-position + objects when `m_paintInvalidationContainerForStackedContents` becomes + different from `m_paintInvalidationContainer`. ### Paint invalidation of texts -Texts are painted by `InlineTextBoxPainter` using `InlineTextBox` as display item client. -Text backgrounds and masks are painted by `InlineTextFlowPainter` using `InlineFlowBox` -as display item client. We should invalidate these display item clients when their painting -will change. +Texts are painted by `InlineTextBoxPainter` using `InlineTextBox` as display +item client. Text backgrounds and masks are painted by `InlineTextFlowPainter` +using `InlineFlowBox` as display item client. We should invalidate these display +item clients when their painting will change. -`LayoutInline`s and `LayoutText`s are marked for full paint invalidation if needed when -new style is set on them. During paint invalidation, we invalidate the `InlineFlowBox`s -directly contained by the `LayoutInline` in `LayoutInline::invalidateDisplayItemClients()` and -`InlineTextBox`s contained by the `LayoutText` in `LayoutText::invalidateDisplayItemClients()`. -We don't need to traverse into the subtree of `InlineFlowBox`s in `LayoutInline::invalidateDisplayItemClients()` -because the descendant `InlineFlowBox`s and `InlineTextBox`s will be handled by their -owning `LayoutInline`s and `LayoutText`s, respectively, when changed style is propagated. +`LayoutInline`s and `LayoutText`s are marked for full paint invalidation if +needed when new style is set on them. During paint invalidation, we invalidate +the `InlineFlowBox`s directly contained by the `LayoutInline` in +`LayoutInline::invalidateDisplayItemClients()` and `InlineTextBox`s contained by +the `LayoutText` in `LayoutText::invalidateDisplayItemClients()`. We don't need +to traverse into the subtree of `InlineFlowBox`s in +`LayoutInline::invalidateDisplayItemClients()` because the descendant +`InlineFlowBox`s and `InlineTextBox`s will be handled by their owning +`LayoutInline`s and `LayoutText`s, respectively, when changed style is propagated. ### Specialty of `::first-line` -`::first-line` pseudo style dynamically applies to all `InlineBox`'s in the first line in the -block having `::first-line` style. The actual applied style is computed from the `::first-line` -style and other applicable styles. +`::first-line` pseudo style dynamically applies to all `InlineBox`'s in the +first line in the block having `::first-line` style. The actual applied style is +computed from the `::first-line` style and other applicable styles. -If the first line contains any `LayoutInline`, we compute the style from the `::first-line` style -and the style of the `LayoutInline` and apply the computed style to the first line part of the -`LayoutInline`. In blink's style implementation, the combined first line style of `LayoutInline` -is identified with `FIRST_LINE_INHERITED` pseudo ID. +If the first line contains any `LayoutInline`, we compute the style from the +`::first-line` style and the style of the `LayoutInline` and apply the computed +style to the first line part of the `LayoutInline`. In Blink's style +implementation, the combined first line style of `LayoutInline` is identified +with `FIRST_LINE_INHERITED` pseudo ID. The normal paint invalidation of texts doesn't work for first line because -* `ComputedStyle::visualInvalidationDiff()` can't detect first line style changes; -* The normal paint invalidation is based on whole LayoutObject's, not aware of the first line. +* `ComputedStyle::visualInvalidationDiff()` can't detect first line style + changes; +* The normal paint invalidation is based on whole LayoutObject's, not aware of + the first line. -We have a special path for first line style change: the style system informs the layout system -when the computed first-line style changes through `LayoutObject::firstLineStyleDidChange()`. -When this happens, we invalidate all `InlineBox`es in the first line. +We have a special path for first line style change: the style system informs the +layout system when the computed first-line style changes through +`LayoutObject::firstLineStyleDidChange()`. When this happens, we invalidate all +`InlineBox`es in the first line. ### Slimming paint v2 TODO(wangxianzhu): add details -## [`PrePaintTreeWalk`](PrePaintTreeWalk.h) (Slimming paint v2 only) +## [`PrePaintTreeWalk`](PrePaintTreeWalk.h) (Slimming Paint invalidation/v2 only) -During `InPrePaint` document lifecycle state, this class is called to walk the whole -layout tree, beginning from the root FrameView, across frame boundaries. We do the -following during the tree walk: +During `InPrePaint` document lifecycle state, this class is called to walk the +whole layout tree, beginning from the root FrameView, across frame boundaries. +We do the following during the tree walk: -* Building paint property tree: creates paint property tree nodes for special - things in the layout tree, including but not limit to: overflow clip, transform, - fixed-pos, animation, mask, filter, etc. Also sets direct compositing reasons to be - used later for compositing. +### Building paint property trees +[`PaintPropertyTreeBuilder`](PaintPropertyTreeBuilder.h) -* Paint invalidation: Not implemented yet. TODO(wangxianzhu): add details after - it's implemented. +This class is responsible for building property trees +(see [the platform paint README file](../../platform/graphics/paint/README.md)). + +Each `PaintLayer`'s `LayoutObject` has one or more `FragmentData` objects (see +below for more on fragments). Every `FragmentData` has an +`ObjectPaintProperties` object if any property nodes are induced by it. For +example, if the object has a transform, its `ObjectPaintProperties::Transform()` +field points at the `TransformPaintPropertyNode` representing that transform. + +The `NeedsPaintPropertyUpdate`, `SubtreeNeedsPaintPropertyUpdate` and +`DescendantNeedsPaintPropertyUpdate` dirty bits on `LayoutObject` control how +much of the layout tree is traversed during each `PrePaintTreeWalk`. + +### Fragments + +In the absence of multicolumn/pagination, there is a 1:1 correspondence between +self-painting `PaintLayer`s and `FragmentData`. If there is +multicolumn/pagination, there may be more `FragmentData`s.. If a `PaintLayer` +has a property node, each of its fragments will have one. The parent of a +fragment's property node is the property node that belongs to the ancestor +`PaintLayer` which is part of the same column. For example, if there are 3 +columns and both a parent and child `PaintLayer` have a transform, there will be +3 `FragmentData` objects for the parent, 3 for the child, each `FragmentData` +will have its own `TransformPaintPropertyNode`, and the child's ith fragment's +transform will point to the ith parent's transform. + +See [`LayoutMultiColumnFlowThread.h`](../layout/LayoutMultiColumnFlowThread.h) +for a much more detail about multicolumn/pagination. + +### Paint invalidation: `PaintInvalidator` implements a tree walk that +performs paint invalidation. TODO(wangxianzhu): expand on this. + +### [`PaintPropertyTreeBuilder`](PaintPropertyTreeBuilder.h) (Slimming Paint invalidation only) ## Paint result caching -`PaintController` holds the previous painting result as a cache of display items. -If some painter would generate results same as those of the previous painting, -we'll skip the painting and reuse the display items from cache. +`PaintController` holds the previous painting result as a cache of display +items. If some painter would generate results same as those of the previous +painting, we'll skip the painting and reuse the display items from cache. ### Display item caching -When a painter would create a `DrawingDisplayItem` exactly the same as the display item -created in the previous painting, we'll reuse the previous one instead of repainting it. +When a painter would create a `DrawingDisplayItem` exactly the same as the +display item created in the previous painting, we'll reuse the previous one +instead of repainting it. ### Subsequence caching -When possible, we enclose the display items that `PaintLayerPainter::paintContents()` generates -(including display items generated by sublayers) in a pair of `BeginSubsequence/EndSubsequence` -display items. +When possible, we enclose the display items that +`PaintLayerPainter::paintContents()` generates (including display items +generated by sublayers) in a pair of `BeginSubsequence/EndSubsequence` display +items. -In a subsequence paint, if the layer would generate exactly the same display items, we'll get -the whole subsequence from the cache instead of repainting them. +In a subsequence paint, if the layer would generate exactly the same display +items, we'll get the whole subsequence from the cache instead of repainting +them. There are many conditions affecting * whether we need to generate subsequence for a PaintLayer; * whether we can use cached subsequence for a PaintLayer. -See `shouldCreateSubsequence()` and `shouldRepaintSubsequence()` in `PaintLayerPainter.cpp` for -the conditions. +See `shouldCreateSubsequence()` and `shouldRepaintSubsequence()` in +`PaintLayerPainter.cpp` for the conditions. ## Empty paint phase optimization -During painting, we walk the layout tree multiple times for multiple paint phases. Sometimes -a layer contain nothing needing a certain paint phase and we can skip tree walk for such -empty phases. Now we have optimized `PaintPhaseDescendantBlockBackgroundsOnly`, -`PaintPhaseDescendantOutlinesOnly` and `PaintPhaseFloat` for empty paint phases. +During painting, we walk the layout tree multiple times for multiple paint +phases. Sometimes a layer contain nothing needing a certain paint phase and we +can skip tree walk for such empty phases. Now we have optimized +`PaintPhaseDescendantBlockBackgroundsOnly`, `PaintPhaseDescendantOutlinesOnly` +and `PaintPhaseFloat` for empty paint phases. -During paint invalidation, we set the containing self-painting layer's `needsPaintPhaseXXX` -flag if the object has something needing to be painted in the paint phase. +During paint invalidation, we set the containing self-painting layer's +`needsPaintPhaseXXX` flag if the object has something needing to be painted in +the paint phase. -During painting, we check the flag before painting a paint phase and skip the tree walk if -the flag is not set. +During painting, we check the flag before painting a paint phase and skip the +tree walk if the flag is not set. -It's hard to clear a `needsPaintPhaseXXX` flag when a layer no longer needs the paint phase, -so we never clear the flags. Instead, we use another set of flags (`previousPaintPhaseXXXWasEmpty`) -to record if a painting of a phase actually produced nothing. We'll skip the next -painting of the phase if the flag is set, regardless of the corresponding -`needsPaintPhaseXXX` flag. We will clear the `previousPaintPhaseXXXWasEmpty` flags when -we paint with different clipping, scroll offset or interest rect from the previous paint. +It's hard to clear a `needsPaintPhaseXXX` flag when a layer no longer needs the +paint phase, so we never clear the flags. Instead, we use another set of flags +(`previousPaintPhaseXXXWasEmpty`) to record if a painting of a phase actually +produced nothing. We'll skip the next painting of the phase if the flag is set, +regardless of the corresponding `needsPaintPhaseXXX` flag. We will clear the +`previousPaintPhaseXXXWasEmpty` flags when we paint with different clipping, +scroll offset or interest rect from the previous paint. -We don't clear the `previousPaintPhaseXXXWasEmpty` flags when the layer is marked `needsRepaint`. -Instead we clear the flag when the corresponding `needsPaintPhaseXXX` is set. This ensures that -we won't clear `previousPaintPhaseXXXWasEmpty` flags when unrelated things changed which won't +We don't clear the `previousPaintPhaseXXXWasEmpty` flags when the layer is +marked `needsRepaint`. Instead we clear the flag when the corresponding +`needsPaintPhaseXXX` is set. This ensures that we won't clear +`previousPaintPhaseXXXWasEmpty` flags when unrelated things changed which won't cause the paint phases to become non-empty. -When layer structure changes, and we are not invalidate paint of the changed subtree, -we need to manually update the `needsPaintPhaseXXX` flags. For example, if an object changes -style and creates a self-painting-layer, we copy the flags from its containing self-painting -layer to this layer, assuming that this layer needs all paint phases that its container -self-painting layer needs. +When layer structure changes, and we are not invalidate paint of the changed +subtree, we need to manually update the `needsPaintPhaseXXX` flags. For example, +if an object changes style and creates a self-painting-layer, we copy the flags +from its containing self-painting layer to this layer, assuming that this layer +needs all paint phases that its container self-painting layer needs. -We could update the `needsPaintPhaseXXX` flags in a separate tree walk, but that would regress -performance of the first paint. For slimming paint v2, we can update the flags during the -pre-painting tree walk to simplify the logics. +We could update the `needsPaintPhaseXXX` flags in a separate tree walk, but that +would regress performance of the first paint. For slimming paint v2, we can +update the flags during the pre-painting tree walk to simplify the logics.
diff --git a/third_party/WebKit/Source/core/paint/RarePaintData.cpp b/third_party/WebKit/Source/core/paint/RarePaintData.cpp index 896be512..1a47a61 100644 --- a/third_party/WebKit/Source/core/paint/RarePaintData.cpp +++ b/third_party/WebKit/Source/core/paint/RarePaintData.cpp
@@ -4,7 +4,7 @@ #include "core/paint/RarePaintData.h" -#include "core/paint/ObjectPaintProperties.h" +#include "core/paint/FragmentData.h" #include "core/paint/PaintLayer.h" namespace blink { @@ -17,14 +17,10 @@ layer_ = std::move(layer); }; -ObjectPaintProperties& RarePaintData::EnsurePaintProperties() { - if (!paint_properties_) - paint_properties_ = ObjectPaintProperties::Create(); - return *paint_properties_.get(); -} - -void RarePaintData::ClearPaintProperties() { - paint_properties_.reset(nullptr); +FragmentData& RarePaintData::EnsureFragment() { + if (!fragment_data_) + fragment_data_ = FragmentData::Create(); + return *fragment_data_.get(); } void RarePaintData::ClearLocalBorderBoxProperties() { @@ -41,13 +37,15 @@ PropertyTreeState RarePaintData::ContentsProperties() const { DCHECK(local_border_box_properties_); PropertyTreeState contents(*local_border_box_properties_); - if (paint_properties_) { - if (paint_properties_->ScrollTranslation()) - contents.SetTransform(paint_properties_->ScrollTranslation()); - if (paint_properties_->OverflowClip()) - contents.SetClip(paint_properties_->OverflowClip()); - else if (paint_properties_->CssClip()) - contents.SetClip(paint_properties_->CssClip()); + if (fragment_data_) { + if (auto* properties = fragment_data_->PaintProperties()) { + if (properties->ScrollTranslation()) + contents.SetTransform(properties->ScrollTranslation()); + if (properties->OverflowClip()) + contents.SetClip(properties->OverflowClip()); + else if (properties->CssClip()) + contents.SetClip(properties->CssClip()); + } } // TODO(chrishtr): cssClipFixedPosition needs to be handled somehow.
diff --git a/third_party/WebKit/Source/core/paint/RarePaintData.h b/third_party/WebKit/Source/core/paint/RarePaintData.h index a80ac6d..9353473 100644 --- a/third_party/WebKit/Source/core/paint/RarePaintData.h +++ b/third_party/WebKit/Source/core/paint/RarePaintData.h
@@ -6,13 +6,13 @@ #define RarePaintData_h #include "core/CoreExport.h" +#include "core/paint/FragmentData.h" #include "platform/wtf/Allocator.h" #include "platform/wtf/Noncopyable.h" namespace blink { class PropertyTreeState; -class ObjectPaintProperties; class PaintLayer; // This is for paint-related data on LayoutObject that is not needed on all @@ -28,11 +28,15 @@ PaintLayer* Layer() { return layer_.get(); } void SetLayer(std::unique_ptr<PaintLayer>); + FragmentData* Fragment() const { return fragment_data_.get(); } + + FragmentData& EnsureFragment(); + ObjectPaintProperties* PaintProperties() const { - return paint_properties_.get(); + if (fragment_data_) + return fragment_data_->PaintProperties(); + return nullptr; } - ObjectPaintProperties& EnsurePaintProperties(); - void ClearPaintProperties(); PropertyTreeState* LocalBorderBoxProperties() const { return local_border_box_properties_.get(); @@ -52,8 +56,7 @@ // depending on the return value of LayoutBoxModelObject::layerTypeRequired(). std::unique_ptr<PaintLayer> layer_; - // Holds references to the paint property nodes created by this object. - std::unique_ptr<ObjectPaintProperties> paint_properties_; + std::unique_ptr<FragmentData> fragment_data_; // This is a complete set of property nodes that should be used as a // starting point to paint a LayoutObject. This data is cached because some
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn index 2edd250da..f350f94 100644 --- a/third_party/WebKit/Source/devtools/BUILD.gn +++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -313,6 +313,7 @@ "front_end/network/networkLogView.css", "front_end/network/NetworkLogView.js", "front_end/network/NetworkLogViewColumns.js", + "front_end/network/NetworkGroupers.js", "front_end/network/networkManageCustomHeadersView.css", "front_end/network/NetworkManageCustomHeadersView.js", "front_end/network/NetworkOverview.js", @@ -339,8 +340,6 @@ "front_end/network_conditions/module.json", "front_end/network_conditions/NetworkConditionsSelector.js", "front_end/network_conditions/networkConditionsSettingsTab.css", - "front_end/network_group_lookup/module.json", - "front_end/network_group_lookup/NetworkProductGroupLookup.js", "front_end/network_log/HAREntry.js", "front_end/network_log/module.json", "front_end/network_log/NetworkLog.js", @@ -378,9 +377,11 @@ "front_end/platform/module.json", "front_end/platform/utilities.js", "front_end/product_registry/module.json", - "front_end/product_registry/ProductNameForURL.js", - "front_end/product_registry/ProductRegistryData.js", - "front_end/product_registry/sha1/sha1.js", + "front_end/product_registry/ProductRegistry.js", + "front_end/product_registry_impl/module.json", + "front_end/product_registry_impl/ProductRegistryImpl.js", + "front_end/product_registry_impl/ProductRegistryData.js", + "front_end/product_registry_impl/sha1/sha1.js", "front_end/profiler/BottomUpProfileDataGrid.js", "front_end/profiler/CPUProfileFlameChart.js", "front_end/profiler/CPUProfileView.js", @@ -870,6 +871,7 @@ "$resources_out_dir/network/network_module.js", "$resources_out_dir/object_ui/object_ui_module.js", "$resources_out_dir/perf_ui/perf_ui_module.js", + "$resources_out_dir/product_registry/product_registry_module.js", "$resources_out_dir/profiler/profiler_module.js", "$resources_out_dir/quick_open/quick_open_module.js", "$resources_out_dir/resources/resources_module.js", @@ -892,8 +894,7 @@ "$resources_out_dir/cm_modes/cm_modes_module.js", "$resources_out_dir/emulated_devices/emulated_devices_module.js", "$resources_out_dir/gonzales/gonzales_module.js", - "$resources_out_dir/network_group_lookup/network_group_lookup_module.js", - "$resources_out_dir/product_registry/product_registry_module.js", + "$resources_out_dir/product_registry_impl/product_registry_impl_module.js", "$resources_out_dir/screencast/screencast_module.js", ]
diff --git a/third_party/WebKit/Source/devtools/front_end/inspector.json b/third_party/WebKit/Source/devtools/front_end/inspector.json index 6977ad6..597a0bf 100644 --- a/third_party/WebKit/Source/devtools/front_end/inspector.json +++ b/third_party/WebKit/Source/devtools/front_end/inspector.json
@@ -17,11 +17,11 @@ { "name": "services", "type": "autostart" }, { "name": "elements", "condition": "!v8only" }, { "name": "network", "condition": "!v8only" }, - { "name": "network_group_lookup", "condition": "!v8only", "type": "remote" }, { "name": "sources" }, { "name": "timeline", "condition": "!v8only" }, { "name": "timeline_model", "condition": "!v8only" }, - { "name": "product_registry", "condition": "!v8only", "type": "remote" }, + { "name": "product_registry", "condition": "!v8only" }, + { "name": "product_registry_impl", "condition": "!v8only", "type": "remote" }, { "name": "profiler" }, { "name": "resources", "condition": "!v8only" }, { "name": "audits", "condition": "!v8only" },
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js index a602a1f..5936f76e 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js +++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
@@ -38,14 +38,10 @@ constructor(parentView) { super({}); this._parentView = parentView; - /** @type {!Map<string, ?Network.NetworkColumnExtensionInterface>} */ - this._columnExtensions = new Map(); this._isHovered = false; this._showingInitiatorChain = false; /** @type {?SDK.NetworkRequest} */ this._requestOrFirstKnownChildRequest = null; - /** @type {!Map<string, !UI.Icon>} */ - this._columnIcons = new Map(); /** @type {?Common.Color} */ this._backgroundColor = null; } @@ -88,14 +84,6 @@ return /** @type {string} */ (color.asString(Common.Color.Format.HEX)); } - /** - * @param {?Common.Color} color - */ - setBackgroundColor(color) { - this._backgroundColor = color; - this._updateBackgroundColor(); - } - _updateBackgroundColor() { var element = this.existingElement(); if (!element) @@ -143,13 +131,6 @@ } /** - * @param {!Map<string, ?Network.NetworkColumnExtensionInterface>} columnExtensions - */ - setColumnExtensions(columnExtensions) { - this._columnExtensions = columnExtensions; - } - - /** * @param {boolean} hovered * @param {boolean} showInitiatorChain */ @@ -351,6 +332,22 @@ } /** + * @param {!ProductRegistry.Registry} productRegistry + * @param {!Network.NetworkNode} a + * @param {!Network.NetworkNode} b + * @return {number} + */ + static ProductComparator(productRegistry, a, b) { + var aRequest = a.request(); + var bRequest = b.request(); + if (!aRequest || !bRequest) + return !aRequest ? -1 : 1; + var aName = productRegistry.nameForUrl(aRequest.parsedURL) || ''; + var bName = productRegistry.nameForUrl(bRequest.parsedURL) || ''; + return aName.localeCompare(bName) || aRequest.indentityCompare(bRequest); + } + + /** * @param {!Network.NetworkNode} a * @param {!Network.NetworkNode} b * @return {number} @@ -566,24 +563,6 @@ } /** - * @param {!Map<string, ?Network.NetworkColumnExtensionInterface>} extensionsMap - * @param {string} extensionId - * @param {!Network.NetworkNode} a - * @param {!Network.NetworkNode} b - * @return {number} - */ - static ExtensionColumnComparator(extensionsMap, extensionId, a, b) { - var aRequest = a.requestOrFirstKnownChildRequest(); - var bRequest = b.requestOrFirstKnownChildRequest(); - if (!aRequest || !bRequest) - return !aRequest ? -1 : 1; - var instance = extensionsMap.get(extensionId); - if (!instance) - return aRequest.indentityCompare(bRequest); - return instance.requestComparator(aRequest, bRequest); - } - - /** * @override */ showingInitiatorChainChanged() { @@ -706,21 +685,11 @@ element.classList.toggle('network-error-row', this._isFailed()); element.classList.toggle('network-navigation-row', this._isNavigationRequest); - for (var rowDecorator of this._parentView.rowDecorators()) - rowDecorator.decorate(this); super.createCells(element); this._updateBackgroundColor(); } /** - * @param {string} columnId - * @param {!UI.Icon} icon - */ - setIconForColumn(columnId, icon) { - this._columnIcons.set(columnId, icon); - } - - /** * @param {!Element} element * @param {string} text */ @@ -736,21 +705,18 @@ */ createCell(columnIdentifier) { var cell = this.createTD(columnIdentifier); - var icon = this._columnIcons.get(columnIdentifier); - if (icon) - cell.appendChild(icon); - // If the key exists but the value is null it means the extension instance has not resolved yet. - // The view controller will force all rows to update when extension is resolved. - if (this._columnExtensions.has(columnIdentifier)) { - var instance = this._columnExtensions.get(columnIdentifier); - if (instance) - this._setTextAndTitle(cell, instance.lookupColumnValue(this._request)); - return cell; - } switch (columnIdentifier) { case 'name': this._renderNameCell(cell); break; + case 'product': + if (!Runtime.experiments.isEnabled('networkGroupingRequests')) { + this._setTextAndTitle(cell, this._request.responseHeaderValue(columnIdentifier) || ''); + break; + } + if (this.request()) + ProductRegistry.instance().then(this._renderProductCell.bind(this, cell)); + break; case 'method': this._setTextAndTitle(cell, this._request.requestMethod); break; @@ -900,6 +866,42 @@ /** * @param {!Element} cell + * @param {!ProductRegistry.Registry} productRegistry + */ + _renderProductCell(cell, productRegistry) { + var request = this.request(); + if (!request) + return; + var entry = productRegistry.entryForUrl(request.parsedURL); + if (!entry) + return; + this._setTextAndTitle(cell, entry.name); + if (entry.type !== null) { + var element = this.existingElement(); + if (!element) + return; + switch (entry.type) { + case 0: + this._backgroundColor = Common.Color.fromRGBA([224, 247, 250, .6]); + cell.classList.add('product-ad'); + break; + case 1: + this._backgroundColor = Common.Color.fromRGBA([255, 252, 225, .6]); + cell.classList.add('product-tracking'); + break; + case 2: + this._backgroundColor = Common.Color.fromRGBA([211, 253, 211, .6]); + cell.classList.add('product-cdn'); + break; + default: + this._backgroundColor = null; + } + this._updateBackgroundColor(); + } + } + + /** + * @param {!Element} cell */ _renderStatusCell(cell) { cell.classList.toggle( @@ -1100,8 +1102,6 @@ */ createCell(columnIdentifier) { var cell = this.createTD(columnIdentifier); - if (this._columnExtensions.has(columnIdentifier)) - return cell; if (columnIdentifier === 'name') { var leftPadding = this.leftPadding ? this.leftPadding + 'px' : ''; cell.style.setProperty('padding-left', leftPadding);
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkGroupers.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkGroupers.js new file mode 100644 index 0000000..3f78ea0 --- /dev/null +++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkGroupers.js
@@ -0,0 +1,90 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @implements {Network.GroupLookupInterface} + */ +Network.ProductGrouper = class { + constructor() { + /** @type {?ProductRegistry.Registry} */ + this._productRegistry = null; + } + + /** + * @override + * @return {!Promise} + */ + initialize() { + return ProductRegistry.instance().then(productRegistry => this._productRegistry = productRegistry); + } + + /** + * @override + * @param {!SDK.NetworkRequest} request + * @return {?*} + */ + groupForRequest(request) { + if (!this._productRegistry) + return null; + var productName = this._productRegistry.nameForUrl(request.parsedURL); + if (!productName) + return null; + return productName; + } + + /** + * @override + * @param {!*} key + * @return {string} + */ + groupName(key) { + return /** @type {string} */ (key); + } +}; + +/** + * @implements {Network.GroupLookupInterface} + */ +Network.FrameGrouper = class { + constructor() { + /** @type {?ProductRegistry.Registry} */ + this._productRegistry = null; + } + + /** + * @override + * @return {!Promise} + */ + initialize() { + return ProductRegistry.instance().then(productRegistry => this._productRegistry = productRegistry); + } + + /** + * @override + * @param {!SDK.NetworkRequest} request + * @return {?*} + */ + groupForRequest(request) { + var resourceTreeModel = request.networkManager().target().model(SDK.ResourceTreeModel); + if (!resourceTreeModel) + return null; + var frame = resourceTreeModel.frameForId(request.frameId); + if (!frame || frame.isMainFrame()) + return null; + return frame; + } + + /** + * @override + * @param {!*} frameArg + * @return {string} + */ + groupName(frameArg) { + var frame = /** @type {!SDK.ResourceTreeFrame} */ (frameArg); + var entry = this._productRegistry ? this._productRegistry.entryForFrame(frame) : null; + if (entry) + return entry.name; + return (new Common.ParsedURL(frame.url)).host || frame.name || '<iframe>'; + } +};
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js index 4ffbd80..9b9910b5 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js +++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
@@ -57,9 +57,6 @@ this._durationCalculator = new Network.NetworkTransferDurationCalculator(); this._calculator = this._timeCalculator; - /** @type {?Network.NetworkGroupLookupInterface} */ - this._activeGroupLookup = null; - /** * @this {Network.NetworkLogView} */ @@ -76,9 +73,6 @@ this._nodesByRequestId = new Map(); /** @type {!Map<*, !Network.NetworkGroupNode>} */ this._nodeGroups = new Map(); - /** @type {!Set<!Network.NetworkRowDecorator>} */ - this._rowDecorators = new Set(); - /** @type {!Object.<string, boolean>} */ this._staleRequestIds = {}; /** @type {number} */ @@ -106,6 +100,14 @@ this._headerHeight = 0; + /** @type {!Map<string, !Network.GroupLookupInterface>} */ + this._groupLookups = new Map(); + this._groupLookups.set('Product', new Network.ProductGrouper()); + this._groupLookups.set('Frame', new Network.FrameGrouper()); + + /** @type {?Network.GroupLookupInterface} */ + this._activeGroupLookup = null; + this._addFilters(); this._resetSuggestionBuilder(); this._initializeView(); @@ -352,19 +354,29 @@ } /** - * @param {?Network.NetworkGroupLookupInterface} grouping + * @return {!Map<string, !Network.GroupLookupInterface>} */ - setGrouping(grouping) { - this._activeGroupLookup = grouping; - this._nodeGroups.clear(); - this._invalidateAllItems(); + groupLookups() { + return this._groupLookups; } /** - * @return {!Set<!Network.NetworkRowDecorator>} + * @param {string} groupKey */ - rowDecorators() { - return this._rowDecorators; + setGrouping(groupKey) { + var groupLookup = this._groupLookups.get(groupKey) || null; + this._activeGroupLookup = groupLookup; + if (!groupLookup) { + this._nodeGroups.clear(); + this._invalidateAllItems(); + return; + } + groupLookup.initialize().then(() => { + if (this._activeGroupLookup !== groupLookup) + return; + this._nodeGroups.clear(); + this._invalidateAllItems(); + }); } /** @@ -498,12 +510,6 @@ this.element.id = 'network-container'; this._setupDataGrid(); - self.runtime.allInstances(Network.NetworkRowDecorator).then(instances => { - for (var instance of instances) - this._rowDecorators.add(instance); - this._invalidateAllItems(true); - }); - this._columns.show(this.element); this._summaryBarElement = this.element.createChild('div', 'network-summary-bar'); @@ -901,7 +907,6 @@ if (group) return group; group = new Network.NetworkGroupNode(this, this._activeGroupLookup.groupName(groupKey)); - group.setColumnExtensions(this._columns.columnExtensions()); this._nodeGroups.set(groupKey, group); return group; } @@ -963,7 +968,6 @@ */ _appendRequest(request) { var node = new Network.NetworkRequestNode(this, request); - node.setColumnExtensions(this._columns.columnExtensions()); node[Network.NetworkLogView._isFilteredOutSymbol] = true; node[Network.NetworkLogView._isMatchingSearchQuerySymbol] = false; @@ -1820,30 +1824,23 @@ /** * @interface */ -Network.NetworkGroupLookupInterface = function() {}; +Network.GroupLookupInterface = function() {}; -Network.NetworkGroupLookupInterface.prototype = { +Network.GroupLookupInterface.prototype = { + /** + * @return {!Promise} + */ + initialize: function() {}, + /** * @param {!SDK.NetworkRequest} request * @return {?*} */ - groupForRequest(request) {}, + groupForRequest: function(request) {}, /** * @param {!*} key * @return {string} */ - groupName(key) {} -}; - -/** - * @interface - */ -Network.NetworkRowDecorator = function() {}; - -Network.NetworkRowDecorator.prototype = { - /** - * @param {!Network.NetworkNode} node - */ - decorate(node) {} + groupName: function(key) {} };
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js index e206ff06..96d19c5a 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js +++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
@@ -29,12 +29,12 @@ /** @type {!Array.<!Network.NetworkLogViewColumns.Descriptor>} */ this._columns = []; - /** @type {!Map<string, ?Network.NetworkColumnExtensionInterface>} */ - this._columnExtensions = new Map(); - this._waterfallRequestsAreStale = false; this._waterfallScrollerWidthIsStale = true; + /** @type {?ProductRegistry.Registry} */ + this._productRegistryInstance = null; + /** @type {!Components.Linkifier} */ this._popupLinkifier = new Components.Linkifier(); @@ -78,6 +78,15 @@ _setupDataGrid() { var defaultColumns = Network.NetworkLogViewColumns._defaultColumns; + + if (Runtime.experiments.isEnabled('networkGroupingRequests')) { + defaultColumns.splice(1, 0, /** @type {!Network.NetworkLogViewColumns.Descriptor} */ ({ + id: 'product', + title: Common.UIString('Product'), + visible: true + })); + } + var defaultColumnConfig = Network.NetworkLogViewColumns._defaultColumnConfig; this._columns = /** @type {!Array<!Network.NetworkLogViewColumns.Descriptor>} */ ([]); @@ -89,7 +98,6 @@ columnConfig.titleDOMFragment = this._makeHeaderFragment(columnConfig.title, columnConfig.subtitle); this._columns.push(columnConfig); } - this._loadColumnExtensions(); this._loadCustomColumnsAndSettings(); this._popoverHelper = new UI.PopoverHelper(this._networkLogView.element, this._getPopoverRequest.bind(this)); @@ -279,6 +287,17 @@ this._dataGrid.sortNodes(sortFunction, !this._dataGrid.isSortOrderAscending()); this._networkLogView.dataGridSorted(); return; + } else if (columnId === 'product' && !this._productRegistryInstance) { + ProductRegistry.instance().then(productRegistry => { + this._productRegistryInstance = productRegistry; + var columnConfig = this._columns.find(columnConfig => columnConfig.id === columnId); + if (!columnConfig) + return; + columnConfig.sortingFunction = Network.NetworkRequestNode.ProductComparator.bind(null, productRegistry); + if (this._dataGrid.sortColumnId() === 'product') + this._sortHandler(); + }); + return; } this._waterfallColumnSortIcon.setIconType(''); @@ -329,13 +348,6 @@ } /** - * @return {!Map<string, ?Network.NetworkColumnExtensionInterface>} - */ - columnExtensions() { - return this._columnExtensions; - } - - /** * @param {!Network.NetworkLogViewColumns.Descriptor} columnConfig */ _toggleColumnVisibility(columnConfig) { @@ -353,43 +365,6 @@ this._persistantSettings.set(saveableSettings); } - _loadColumnExtensions() { - var extensions = self.runtime.extensions(Network.NetworkColumnExtensionInterface); - for (var i = 0; i < extensions.length; i++) { - var extension = extensions[i]; - var title = extension.title(); - var columnId = title.toLowerCase() + '-extension'; - - this._columnExtensions.set(columnId, null); - extension.instance().then(extensionInstanceResolved.bind(this, columnId)); - - var columnConfig = /** @type {!Network.NetworkLogViewColumns.Descriptor} */ ( - Object.assign({}, Network.NetworkLogViewColumns._defaultColumnConfig, { - id: columnId, - title: title, - isResponseHeader: false, - isCustomHeader: false, - visible: true, - sortingFunction: - Network.NetworkRequestNode.ExtensionColumnComparator.bind(null, this._columnExtensions, columnId) - })); - const columnIndex = i + 1; - this._columns.splice(columnIndex, 0, columnConfig); - if (this._dataGrid) - this._dataGrid.addColumn(Network.NetworkLogViewColumns._convertToDataGridDescriptor(columnConfig), columnIndex); - } - - /** - * @param {string} columnId - * @param {!Network.NetworkColumnExtensionInterface} instance - * @this {Network.NetworkLogViewColumns} - */ - function extensionInstanceResolved(columnId, instance) { - this._columnExtensions.set(columnId, instance); - this._networkLogView.columnExtensionResolved(); - } - } - _loadCustomColumnsAndSettings() { var savedSettings = this._persistantSettings.get(); var columnIds = Object.keys(savedSettings); @@ -642,6 +617,8 @@ }; Network.NetworkLogViewColumns._initialSortColumn = 'waterfall'; +/** @type {?ProductRegistry.Registry} */ +Network.NetworkRequestNode._productRegistryInstance = null; /** * @typedef {{ @@ -854,23 +831,3 @@ Duration: 'duration', Latency: 'latency' }; - -/** - * @interface - */ -Network.NetworkColumnExtensionInterface = function() {}; - -Network.NetworkColumnExtensionInterface.prototype = { - /** - * @param {!SDK.NetworkRequest} request - * @return {string} - */ - lookupColumnValue(request) {}, - - /** - * @param {!SDK.NetworkRequest} aRequest - * @param {!SDK.NetworkRequest} bRequest - * @return {number} - */ - requestComparator(aRequest, bRequest) {} -};
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js index d7b9ae8..a37bc9a3 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js +++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
@@ -40,8 +40,7 @@ this._networkLogShowOverviewSetting = Common.settings.createSetting('networkLogShowOverview', true); this._networkLogLargeRowsSetting = Common.settings.createSetting('networkLogLargeRows', false); this._networkRecordFilmStripSetting = Common.settings.createSetting('networkRecordFilmStripSetting', false); - this._toggleRecordAction = - /** @type {!UI.Action }*/ (UI.actionRegistry.action('network.toggle-recording')); + this._toggleRecordAction = /** @type {!UI.Action }*/ (UI.actionRegistry.action('network.toggle-recording')); /** @type {?PerfUI.FilmStripView} */ this._filmStripView = null; @@ -89,8 +88,6 @@ this._networkLogLargeRowsSetting.addChangeListener(this._toggleLargerRequests, this); this._networkRecordFilmStripSetting.addChangeListener(this._toggleRecordFilmStrip, this); - /** @type {!Map<string, !Runtime.Extension>} */ - this._groupingExtensions = new Map(); this._createToolbarButtons(); this._toggleRecord(true); @@ -188,35 +185,16 @@ } _setupGroupingCombo() { - var extensions = self.runtime.extensions(Network.NetworkGroupLookupInterface); - if (!extensions.length) + if (!Runtime.experiments.isEnabled('networkGroupingRequests')) return; - - var setting = Common.settings.createSetting('networkGrouping', ''); /** @type {!Array<!{value: string, label: string, title: string}>} */ var options = [{value: '', label: Common.UIString('No grouping'), title: Common.UIString('No grouping')}]; + for (var name of this._networkLogView.groupLookups().keys()) + options.push({value: name, label: Common.UIString(name), title: Common.UIString(name)}); - extensions.forEach(extension => { - var identifier = extension.descriptor()['id']; - this._groupingExtensions.set(identifier, extension); - options.push({value: identifier, label: extension.title(), title: extension.title()}); - }); + var setting = Common.settings.createSetting('networkGrouping', ''); this._panelToolbar.appendToolbarItem(new UI.ToolbarSettingComboBox(options, setting, Common.UIString('Group by'))); - setting.addChangeListener(event => this._groupingChanged(/** @type {string} */ (event.data))); - this._groupingChanged(setting.get()); - } - - /** - * @param {string} identifier - */ - _groupingChanged(identifier) { - var extension = this._groupingExtensions.get(identifier); - if (extension) { - extension.instance().then( - grouping => this._networkLogView.setGrouping(/** @type {?Network.NetworkGroupLookupInterface} */ (grouping))); - } else { - this._networkLogView.setGrouping(null); - } + setting.addChangeListener(event => this._networkLogView.setGrouping(/** @type {string} */ (event.data))); } /**
diff --git a/third_party/WebKit/Source/devtools/front_end/network/module.json b/third_party/WebKit/Source/devtools/front_end/network/module.json index d8b62a36..6827c70 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/module.json +++ b/third_party/WebKit/Source/devtools/front_end/network/module.json
@@ -92,6 +92,20 @@ "order": 40, "className": "Network.NetworkConfigView", "tags": "disk cache, network throttling, useragent, user agent" + }, + { + "type": "@Network.GroupLookupInterface", + "className": "Network.ProductGrouper", + "experiment": "networkGroupingRequests", + "title": "Product", + "id": "product" + }, + { + "type": "@Network.GroupLookupInterface", + "className": "Network.FrameGrouper", + "experiment": "networkGroupingRequests", + "title": "Frame", + "id": "frame" } ], "dependencies": [ @@ -102,7 +116,8 @@ "data_grid", "network_conditions", "object_ui", - "network_log" + "network_log", + "product_registry" ], "scripts": [ "BlockedURLsPane.js", @@ -117,6 +132,7 @@ "NetworkTimeCalculator.js", "NetworkLogView.js", "NetworkLogViewColumns.js", + "NetworkGroupers.js", "NetworkManageCustomHeadersView.js", "NetworkOverview.js", "NetworkWaterfallColumn.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css b/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css index 37842b76..e46ae42 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css +++ b/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css
@@ -161,6 +161,29 @@ .network-header-subtitle { color: gray; } +.network-log-grid.data-grid .product-ad::before, +.network-log-grid.data-grid .product-tracking::before, +.network-log-grid.data-grid .product-cdn::before { + content: ""; + border-radius: 5px; + height: 5px; + width: 5px; + margin-right: 3px; + display: inline-block; + vertical-align: middle; +} + +.network-log-grid.data-grid .product-ad::before { + background-color: #00BCD4; +} + +.network-log-grid.data-grid .product-tracking::before { + background-color: #FF9800; +} + +.network-log-grid.data-grid .product-cdn::before { + background-color: #4CAF50; +} .network-log-grid.data-grid.small .network-cell-subtitle, .network-log-grid.data-grid.small .network-header-subtitle {
diff --git a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js deleted file mode 100644 index 1262dd9..0000000 --- a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js +++ /dev/null
@@ -1,148 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -/** - * @implements {Network.NetworkColumnExtensionInterface} - * @implements {Network.NetworkGroupLookupInterface} - */ -NetworkGroupLookup.NetworkProductGroupLookup = class { - /** - * @override - * @param {!SDK.NetworkRequest} request - * @return {?*} - */ - groupForRequest(request) { - var productName = ProductRegistry.nameForUrl(request.parsedURL); - if (!productName) - return null; - return productName; - } - - /** - * @override - * @param {!*} key - * @return {string} - */ - groupName(key) { - return /** @type {string} */ (key); - } - - /** - * @override - * @param {!SDK.NetworkRequest} request - * @return {string} - */ - lookupColumnValue(request) { - return ProductRegistry.nameForUrl(request.parsedURL) || ''; - } - - /** - * @override - * @param {!SDK.NetworkRequest} aRequest - * @param {!SDK.NetworkRequest} bRequest - * @return {number} - */ - requestComparator(aRequest, bRequest) { - var aValue = this.lookupColumnValue(aRequest); - var bValue = this.lookupColumnValue(bRequest); - if (aValue === bValue) - return aRequest.indentityCompare(bRequest); - return aValue > bValue ? 1 : -1; - } -}; - -/** - * @implements {Network.NetworkGroupLookupInterface} - */ -NetworkGroupLookup.NetworkProductFrameGroupLookup = class { - /** - * @override - * @param {!SDK.NetworkRequest} request - * @return {?*} - */ - groupForRequest(request) { - var resourceTreeModel = request.networkManager().target().model(SDK.ResourceTreeModel); - if (!resourceTreeModel) - return null; - var frame = resourceTreeModel.frameForId(request.frameId); - if (!frame || frame.isMainFrame()) - return null; - return frame; - } - - /** - * @override - * @param {!*} frameArg - * @return {string} - */ - groupName(frameArg) { - var frame = /** @type {!SDK.ResourceTreeFrame} */ (frameArg); - var name; - var frameParsedURL = new Common.ParsedURL(frame.url); - if (frame.url) - name = ProductRegistry.nameForUrl(frameParsedURL); - if (name) - return name; - // We are not caching the frame url result because it may change. - var symbol = NetworkGroupLookup.NetworkProductFrameGroupLookup._productFrameGroupNameSymbol; - if (frame[symbol]) - return frame[symbol]; - frame[symbol] = this._lookupFrameStacktraceName(frame) || frameParsedURL.host || frame.name || '<iframe>'; - return frame[symbol]; - } - - /** - * @param {!SDK.ResourceTreeFrame} frame - * @return {?string} - */ - _lookupFrameStacktraceName(frame) { - // TODO(allada) This probably belongs in another shared module with some lookup that console will use for execution - // context name lookup. - var stackTrace = frame.creationStackTrace(); - var name; - while (stackTrace) { - for (var stack of stackTrace.callFrames) { - if (stack.url) - name = ProductRegistry.nameForUrl(new Common.ParsedURL(stack.url)); - if (name) - return name; - } - stackTrace = frame.parent; - } - return null; - } -}; - -/** - * @implements {Network.NetworkRowDecorator} - */ -NetworkGroupLookup.NetworkProductTypeGroupLookup = class { - /** - * @override - * @param {!Network.NetworkNode} node - */ - decorate(node) { - var request = node.request(); - var element = node.existingElement(); - if (!request || !element) - return; - var typeName = ProductRegistry.typeForUrl(request.parsedURL); - if (typeName === null) - return; - var icon = UI.Icon.create('smallicon-network-product'); - var color; - if (typeName === 1) { - color = Common.Color.fromRGBA([255, 252, 225, .6]); - icon.style.filter = 'hue-rotate(220deg) brightness(1.5)'; - } else if (typeName === 2) { - color = Common.Color.fromRGBA([211, 253, 211, .6]); - icon.style.filter = 'hue-rotate(-90deg) brightness(1.5)'; - } else { - color = Common.Color.fromRGBA([224, 247, 250, .6]); - } - node.setIconForColumn('product-extension', icon); - node.setBackgroundColor(color); - } -}; - -NetworkGroupLookup.NetworkProductFrameGroupLookup._productFrameGroupNameSymbol = Symbol('ProductFrameGroupName');
diff --git a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json deleted file mode 100644 index 9753bb9..0000000 --- a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json +++ /dev/null
@@ -1,34 +0,0 @@ -{ - "extensions": [ - { - "type": "@Network.NetworkGroupLookupInterface", - "className": "NetworkGroupLookup.NetworkProductGroupLookup", - "title": "Product", - "id": "product" - }, - { - "type": "@Network.NetworkGroupLookupInterface", - "className": "NetworkGroupLookup.NetworkProductFrameGroupLookup", - "title": "Frame", - "id": "frame" - }, - { - "type": "@Network.NetworkColumnExtensionInterface", - "className": "NetworkGroupLookup.NetworkProductGroupLookup", - "title": "Product" - }, - { - "type": "@Network.NetworkRowDecorator", - "className": "NetworkGroupLookup.NetworkProductTypeGroupLookup" - } - ], - "dependencies": [ - "network", - "product_registry", - "sdk" - ], - "experiment": "networkGroupingRequests", - "scripts": [ - "NetworkProductGroupLookup.js" - ] -}
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js deleted file mode 100644 index 271157a..0000000 --- a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js +++ /dev/null
@@ -1,95 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -/** - * @param {!Common.ParsedURL} parsedUrl - * @return {?string} - */ -ProductRegistry.nameForUrl = function(parsedUrl) { - var entry = ProductRegistry.entryForUrl(parsedUrl); - if (entry) - return entry.name; - return null; -}; - -/** - * @param {!Common.ParsedURL} parsedUrl - * @return {?ProductRegistry.ProductEntry} - */ -ProductRegistry.entryForUrl = function(parsedUrl) { - if (parsedUrl.isDataURL()) - return null; - // TODO(allada) This should be expanded to allow paths as as well as domain to find a product. - var productsByDomainHash = ProductRegistry._productsByDomainHash; - // Remove leading www. if it is the only subdomain. - var domain = parsedUrl.domain().replace(/^www\.(?=[^.]+\.[^.]+$)/, ''); - - var previousIndex = -1; - var index = -1; - // Ensure we loop with full domain first, but do not loop over last part (ie: ".com"). - for (var nextIndex = domain.indexOf('.'); nextIndex !== -1; nextIndex = domain.indexOf('.', nextIndex + 1)) { - var previousSubdomain = domain.substring(previousIndex + 1, index); - var subDomain = domain.substring(index + 1); - var prefixes = productsByDomainHash.get(ProductRegistry._hashForDomain(subDomain)); - previousIndex = index; - index = nextIndex; - if (!prefixes) - continue; - // Exact match domains are always highest priority. - if ('' in prefixes && domain === subDomain) - return prefixes['']; - if (previousSubdomain) { - for (var prefix in prefixes) { - var domainPrefix = previousSubdomain.substr(0, prefix.length); - if (domainPrefix === prefix && prefix !== '') - return prefixes[prefix]; - } - } - // Process wildcard subdomain if no better match found. - if (prefixes && '*' in prefixes) - return prefixes['*']; - } - return null; -}; - -/** - * @param {!Common.ParsedURL} parsedUrl - * @return {?number} - */ -ProductRegistry.typeForUrl = function(parsedUrl) { - var entry = ProductRegistry.entryForUrl(parsedUrl); - if (entry) - return entry.type; - return null; -}; - -/** - * @param {string} domain - * @return {string} - */ -ProductRegistry._hashForDomain = function(domain) { - return ProductRegistry.sha1(domain).substr(0, 16); -}; - -/** - * @param {!Array<string>} productNames - * @param {!Array<!{hash: string, prefixes: !Object<string, !{product: number, type: (number|undefined)}>}>} data - */ -ProductRegistry.register = function(productNames, data) { - for (var i = 0; i < data.length; i++) { - var entry = data[i]; - var prefixes = {}; - for (var prefix in entry.prefixes) { - var prefixEntry = entry.prefixes[prefix]; - var type = prefixEntry.type !== undefined ? prefixEntry.type : null; - prefixes[prefix] = {name: productNames[prefixEntry.product], type: type}; - } - ProductRegistry._productsByDomainHash.set(entry.hash, prefixes); - } -}; - -/** @typedef {!{name: string, type: ?number}} */ -ProductRegistry.ProductEntry; - -/** @type {!Map<string, !Object<string, !ProductRegistry.ProductEntry>>}} */ -ProductRegistry._productsByDomainHash = new Map();
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistry.js b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistry.js new file mode 100644 index 0000000..66f431c8 --- /dev/null +++ b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistry.js
@@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +/** + * @return {!Promise<!ProductRegistry.Registry>} + */ +ProductRegistry.instance = function() { + return self.runtime.extension(ProductRegistry.Registry).instance(); +}; + +/** + * @interface + */ +ProductRegistry.Registry = function() {}; + +ProductRegistry.Registry.prototype = { + /** + * @param {!Common.ParsedURL} parsedUrl + * @return {?string} + */ + nameForUrl: function(parsedUrl) {}, + + /** + * @param {!Common.ParsedURL} parsedUrl + * @return {?ProductRegistry.Registry.ProductEntry} + */ + entryForUrl: function(parsedUrl) {}, + + /** + * @param {!Common.ParsedURL} parsedUrl + * @return {?number} + */ + typeForUrl: function(parsedUrl) {}, + + /** + * @param {!SDK.ResourceTreeFrame} frame + * @return {?ProductRegistry.Registry.ProductEntry} + */ + entryForFrame: function(frame) {} +}; + +/** @typedef {!{name: string, type: ?number}} */ +ProductRegistry.Registry.ProductEntry;
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/module.json b/third_party/WebKit/Source/devtools/front_end/product_registry/module.json index e02f9bd..3f3ce5c 100644 --- a/third_party/WebKit/Source/devtools/front_end/product_registry/module.json +++ b/third_party/WebKit/Source/devtools/front_end/product_registry/module.json
@@ -2,11 +2,10 @@ "extensions": [ ], "dependencies": [ - "common" + "common", + "sdk" ], "scripts": [ - "ProductNameForURL.js", - "ProductRegistryData.js", - "sha1/sha1.js" + "ProductRegistry.js" ] }
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryData.js similarity index 99% rename from third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js rename to third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryData.js index ed6e469..4b93b182 100644 --- a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js +++ b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryData.js
@@ -3,7 +3,7 @@ // found in the LICENSE file. // clang-format off /* eslint-disable */ -ProductRegistry.register([ +ProductRegistryImpl.register([ "1&1 Internet AG", "A9", "AdGenie",
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryImpl.js b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryImpl.js new file mode 100644 index 0000000..977720e5 --- /dev/null +++ b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryImpl.js
@@ -0,0 +1,143 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @implements {ProductRegistry.Registry} + */ +ProductRegistryImpl.Registry = class { + constructor() { + } + + /** + * @override + * @param {!Common.ParsedURL} parsedUrl + * @return {?string} + */ + nameForUrl(parsedUrl) { + var entry = this.entryForUrl(parsedUrl); + if (entry) + return entry.name; + return null; + } + + /** + * @override + * @param {!Common.ParsedURL} parsedUrl + * @return {?ProductRegistry.Registry.ProductEntry} + */ + entryForUrl(parsedUrl) { + if (parsedUrl.isDataURL()) + return null; + // TODO(allada) This should be expanded to allow paths as as well as domain to find a product. + var productsByDomainHash = ProductRegistryImpl._productsByDomainHash; + // Remove leading www. if it is the only subdomain. + var domain = parsedUrl.domain().replace(/^www\.(?=[^.]+\.[^.]+$)/, ''); + + var previousIndex = -1; + var index = -1; + // Ensure we loop with full domain first, but do not loop over last part (ie: ".com"). + for (var nextIndex = domain.indexOf('.'); nextIndex !== -1; nextIndex = domain.indexOf('.', nextIndex + 1)) { + var previousSubdomain = domain.substring(previousIndex + 1, index); + var subDomain = domain.substring(index + 1); + var prefixes = productsByDomainHash.get(ProductRegistryImpl._hashForDomain(subDomain)); + previousIndex = index; + index = nextIndex; + if (!prefixes) + continue; + // Exact match domains are always highest priority. + if ('' in prefixes && domain === subDomain) + return prefixes['']; + if (previousSubdomain) { + for (var prefix in prefixes) { + var domainPrefix = previousSubdomain.substr(0, prefix.length); + if (domainPrefix === prefix && prefix !== '') + return prefixes[prefix]; + } + } + // Process wildcard subdomain if no better match found. + if (prefixes && '*' in prefixes) + return prefixes['*']; + } + return null; + } + + /** + * @override + * @param {!Common.ParsedURL} parsedUrl + * @return {?number} + */ + typeForUrl(parsedUrl) { + var entry = this.entryForUrl(parsedUrl); + if (entry) + return entry.type; + return null; + } + + /** + * @override + * @param {!SDK.ResourceTreeFrame} frame + * @return {?ProductRegistry.Registry.ProductEntry} + */ + entryForFrame(frame) { + var entry; + if (frame.url) + entry = this.entryForUrl(new Common.ParsedURL(frame.url)); + if (entry) + return entry; + // We are not caching the frame url result because it may change. + var symbol = ProductRegistryImpl.Registry._productEntryForFrameSymbol; + if (!(symbol in frame)) + frame[symbol] = this._lookupStackTraceEntryForFrame(frame); + return frame[symbol]; + } + + /** + * @param {!SDK.ResourceTreeFrame} frame + * @return {?ProductRegistry.Registry.ProductEntry} + */ + _lookupStackTraceEntryForFrame(frame) { + var stackTrace = frame.creationStackTrace(); + var entry; + while (stackTrace) { + for (var stack of stackTrace.callFrames) { + if (stack.url) + entry = this.entryForUrl(new Common.ParsedURL(stack.url)); + if (entry) + return entry; + } + stackTrace = frame.parent; + } + return null; + } +}; + +ProductRegistryImpl.Registry._productEntryForFrameSymbol = Symbol('ProductEntryForFrame'); + +/** + * @param {string} domain + * @return {string} + */ +ProductRegistryImpl._hashForDomain = function(domain) { + return ProductRegistryImpl.sha1(domain).substr(0, 16); +}; + +/** + * @param {!Array<string>} productNames + * @param {!Array<!{hash: string, prefixes: !Object<string, !{product: number, type: (number|undefined)}>}>} data + */ +ProductRegistryImpl.register = function(productNames, data) { + for (var i = 0; i < data.length; i++) { + var entry = data[i]; + var prefixes = {}; + for (var prefix in entry.prefixes) { + var prefixEntry = entry.prefixes[prefix]; + var type = prefixEntry.type !== undefined ? prefixEntry.type : null; + prefixes[prefix] = {name: productNames[prefixEntry.product], type: type}; + } + ProductRegistryImpl._productsByDomainHash.set(entry.hash, prefixes); + } +}; + +/** @type {!Map<string, !Object<string, !ProductRegistry.Registry.ProductEntry>>}} */ +ProductRegistryImpl._productsByDomainHash = new Map();
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry_impl/module.json b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/module.json new file mode 100644 index 0000000..39e1190 --- /dev/null +++ b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/module.json
@@ -0,0 +1,18 @@ +{ + "extensions": [ + { + "type": "@ProductRegistry.Registry", + "className": "ProductRegistryImpl.Registry" + } + ], + "dependencies": [ + "common", + "product_registry", + "sdk" + ], + "scripts": [ + "ProductRegistryImpl.js", + "ProductRegistryData.js", + "sha1/sha1.js" + ] +}
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/sha1/README.chromium b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/README.chromium similarity index 100% rename from third_party/WebKit/Source/devtools/front_end/product_registry/sha1/README.chromium rename to third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/README.chromium
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/sha1/sha1.js b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/sha1.js similarity index 98% rename from third_party/WebKit/Source/devtools/front_end/product_registry/sha1/sha1.js rename to third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/sha1.js index 5c7133e4..5385d0e 100644 --- a/third_party/WebKit/Source/devtools/front_end/product_registry/sha1/sha1.js +++ b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/sha1.js
@@ -12,7 +12,7 @@ * @param {string} str * @return {string} */ -ProductRegistry.sha1 = function(str) { +ProductRegistryImpl.sha1 = function(str) { return rstr2hex(rstr_sha1(str2rstr_utf8(str))); /**
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp index 083e595..0baaac0 100644 --- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp +++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
@@ -403,7 +403,7 @@ } void ImageCapture::GetMediaTrackSettings(MediaTrackSettings& settings) const { - // Merge any present |m_settings| members into |settings|. + // Merge any present |settings_| members into |settings|. if (settings_.hasWhiteBalanceMode()) settings.setWhiteBalanceMode(settings_.whiteBalanceMode()); @@ -412,8 +412,10 @@ if (settings_.hasFocusMode()) settings.setFocusMode(settings_.focusMode()); - if (settings_.hasPointsOfInterest()) + if (settings_.hasPointsOfInterest() && + !settings_.pointsOfInterest().IsEmpty()) { settings.setPointsOfInterest(settings_.pointsOfInterest()); + } if (settings_.hasExposureCompensation()) settings.setExposureCompensation(settings_.exposureCompensation()); @@ -504,7 +506,9 @@ caps->SetImageWidth( MediaSettingsRange::Create(std::move(capabilities->width))); } - caps->SetFillLightMode(capabilities->fill_light_mode); + + if (!capabilities->fill_light_mode.IsEmpty()) + caps->SetFillLightMode(capabilities->fill_light_mode); resolver->Resolve(caps); }
diff --git a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp index 52fb12ca..9721bf6c 100644 --- a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp +++ b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
@@ -305,8 +305,7 @@ ++pos; SkipWhiteSpace(refresh, pos, matcher); unsigned url_start_pos = pos; - if (refresh.Find("url", url_start_pos, kTextCaseASCIIInsensitive) == - url_start_pos) { + if (refresh.FindIgnoringASCIICase("url", url_start_pos) == url_start_pos) { url_start_pos += 3; SkipWhiteSpace(refresh, url_start_pos, matcher); if (refresh[url_start_pos] == '=') { @@ -406,7 +405,7 @@ unsigned length = media_type.length(); while (pos < length) { - pos = media_type.Find("charset", pos, kTextCaseASCIIInsensitive); + pos = media_type.FindIgnoringASCIICase("charset", pos); if (pos == kNotFound || !pos) { charset_len = 0; return;
diff --git a/third_party/WebKit/Source/platform/network/mime/ContentType.cpp b/third_party/WebKit/Source/platform/network/mime/ContentType.cpp index b02c9819..b34d1a5d 100644 --- a/third_party/WebKit/Source/platform/network/mime/ContentType.cpp +++ b/third_party/WebKit/Source/platform/network/mime/ContentType.cpp
@@ -41,7 +41,7 @@ size_t semi = stripped_type.find(';'); if (semi != kNotFound) { size_t start = - stripped_type.Find(parameter_name, semi + 1, kTextCaseASCIIInsensitive); + stripped_type.FindIgnoringASCIICase(parameter_name, semi + 1); if (start != kNotFound) { start = stripped_type.find('=', start + parameter_name.length()); if (start != kNotFound) {
diff --git a/third_party/android_platform/development/scripts/stack b/third_party/android_platform/development/scripts/stack index 010488c6..8d09461 100755 --- a/third_party/android_platform/development/scripts/stack +++ b/third_party/android_platform/development/scripts/stack
@@ -145,6 +145,7 @@ "output-directory=", "symbols-dir=", "symbols-zip=", + "packed-lib=", "arch=", "fallback-monochrome", "verbose", @@ -154,9 +155,9 @@ zip_arg = None more_info = False - packed_relocation_adjustments = "unknown" fallback_monochrome = False arch_defined = False + packed_libs = [] for option, value in options: if option == "--help": PrintUsage() @@ -171,10 +172,8 @@ symbol.CHROME_SYMBOLS_DIR = os.path.join(symbol.CHROME_SRC, value) elif option == "--output-directory": constants.SetOutputDirectory(value) - elif option == "--packed-relocation-adjustments": - packed_relocation_adjustments = True - elif option == "--no-packed-relocation-adjustments": - packed_relocation_adjustments = False + elif option == "--packed-lib": + packed_libs.append(os.path.expanduser(value)) elif option == "--more-info": more_info = True elif option == "--less-info": @@ -183,6 +182,11 @@ fallback_monochrome = True elif option == "--verbose": logging.basicConfig(level=logging.DEBUG) + elif option in ( + '--packed-relocation-adjustments', + '--no-packed-relocation-adjustments'): + print ('--[no-]packed-relocation-adjustments options are deprecated. ' + 'Specify packed libs directory instead.') if len(arguments) > 1: PrintUsage() @@ -205,25 +209,16 @@ if zip_arg: rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg) - if packed_relocation_adjustments == "unknown": - version = stack_libs.GetTargetAndroidVersionNumber(lines) - if version == None: - packed_relocation_adjustments = False - print ("Unknown Android release, " - + "consider --[no-]packed-relocation-adjustments options") - elif version >= _ANDROID_M_MAJOR_VERSION: - packed_relocation_adjustments = False - else: - packed_relocation_adjustments = True - print ("Pre-M Android release detected, " - + "added --packed-relocation-adjustments option") - else: - packed_relocation_adjustments = False + version = stack_libs.GetTargetAndroidVersionNumber(lines) + if version is None: + print ("Unknown Android release, " + "consider passing --packed-lib.") + elif version < _ANDROID_M_MAJOR_VERSION and not packed_libs: + print ("Pre-M Android release detected, " + "but --packed-lib not specified. Stack symbolization may fail.") - if packed_relocation_adjustments: - constants.CheckOutputDirectory() - stripped_libs_dir = constants.GetOutDirectory() - load_vaddrs = stack_libs.GetLoadVaddrs(stripped_libs_dir) + if (version is None or version < _ANDROID_M_MAJOR_VERSION) and packed_libs: + load_vaddrs = stack_libs.GetLoadVaddrs(stripped_libs=packed_libs) else: load_vaddrs = {}
diff --git a/third_party/android_platform/development/scripts/stack_libs.py b/third_party/android_platform/development/scripts/stack_libs.py index 6cc1a86..8f8dd423 100755 --- a/third_party/android_platform/development/scripts/stack_libs.py +++ b/third_party/android_platform/development/scripts/stack_libs.py
@@ -9,12 +9,15 @@ import glob import os.path +import re import subprocess _BASE_APK = 'base.apk' _LIBCHROME_SO = 'libchrome.so' +_BUILD_FINGERPRINT_RE = re.compile('.*Build fingerprint: (.*)$') + def GetTargetAndroidVersionNumber(lines): """Return the Android major version number from the build fingerprint. @@ -26,13 +29,15 @@ """ # For example, "Build fingerprint: 'Android/aosp_flo/flo:5.1.1/...'" is 5. for line in lines: - if line.startswith('Build fingerprint: '): - fingerprint = line.split()[2] + m = _BUILD_FINGERPRINT_RE.match(line) + if not m: + continue + fingerprint = m.group(1) + try: version = fingerprint.split('/')[2].split(':')[1].split('.')[0] - try: - return int(version) - except ValueError: - return None + return int(version) + except Exception: + pass return None @@ -90,7 +95,7 @@ return 0 -def GetLoadVaddrs(stripped_libs_dir): +def GetLoadVaddrs(stripped_libs=None, stripped_libs_dir=None): """Return a dict of minimum VirtAddr for libraries in the given directory. The dictionary returned may be passed to stack_core.ConvertTrace(). In @@ -103,8 +108,11 @@ Returns: {'libchrome.so': 12345, ...} """ - libs = glob.glob(os.path.join(stripped_libs_dir, '*.so')) - libs = [l for l in libs if _HasElfHeader(l)] + if not stripped_libs: + stripped_libs = [] + if stripped_libs_dir: + stripped_libs.extend(glob.glob(os.path.join(stripped_libs_dir, '*.so'))) + libs = [l for l in stripped_libs if _HasElfHeader(l)] load_vaddrs = {} for lib in libs:
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 233d600..943e801 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -103577,6 +103577,7 @@ <int value="-909641013" label="DataReductionProxySiteBreakdown:enabled"/> <int value="-908421850" label="PointerEvent:enabled"/> <int value="-907234795" label="NewAudioRenderingMixingStrategy:disabled"/> + <int value="-899393472" label="enable-new-app-menu-icon"/> <int value="-899334103" label="disable-fast-text-autosizing"/> <int value="-898594349" label="ash-enable-stable-overview-order"/> <int value="-898499262" label="ImprovedA2HS:enabled"/>