diff --git a/BUILD.gn b/BUILD.gn index f1af528..deb2f340 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -403,6 +403,7 @@ "//base:base_junit_tests", "//base/android/linker:chromium_android_linker", "//build/android/gyp/test:hello_world", + "//build/android/rezip", "//chrome/android/webapk/shell_apk:webapk", "//components/invalidation/impl:components_invalidation_impl_junit_tests", "//components/policy/android:components_policy_junit_tests",
diff --git a/DEPS b/DEPS index b116334..40e836b 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': 'ddff43aa5e696dad75c823ebcf8258923c0d0257', + 'skia_revision': '578f52c6cf6372b88a88a05dee0efc5b67aa9a9c', # 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': '97a4bb81ea73f7640508764e71abc49f381f00ea', + 'v8_revision': 'f8f09ec50b66b07307fca903e3b8108910f92b02', # 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. @@ -52,7 +52,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': 'd90198385c2ec0429039558b3f2f6b968e5172f4', + 'angle_revision': 'b8a580a3b2d66ebbf43c7c1c9be703d3af432e4b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling build tools # and whatever else without interference from each other. @@ -96,7 +96,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '489a5bc40e983273304af2375580900819b1389d', + 'catapult_revision': '810f05af27d2dedd11d05055a792d1f0dd7cd637', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -228,7 +228,7 @@ Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067', 'src/third_party/webrtc': - Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'ba5e7e291afb87d7eccf524fa26f0ed81f8a34cd', # commit position 15707 + Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '4f48669246d80eca01be30540878ffeccc374c0c', # commit position 15753 'src/third_party/openmax_dl': Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' + Var('openmax_dl_revision'), @@ -427,7 +427,7 @@ # Userspace interface to kernel DRM services. 'src/third_party/libdrm/src': - Var('chromium_git') + '/chromiumos/third_party/libdrm.git' + '@' + '4c4778123b7b11f750577199644f5d6820851dbb', + Var('chromium_git') + '/chromiumos/third_party/libdrm.git' + '@' + '0ce18bedd3e62d4784fa755403801934ba171084', # Display server protocol for Linux. 'src/third_party/wayland/src':
diff --git a/WATCHLISTS b/WATCHLISTS index 0982bc27..a5330aa 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -1492,7 +1492,6 @@ 'piman+watch@chromium.org'], 'auto_bisect': ['auto-bisect-reviews@chromium.org'], 'autofill': ['estade+watch@chromium.org', - 'jdonnelly+autofillwatch@chromium.org', 'rouslan+autofill@chromium.org', 'vabr+watchlistautofill@chromium.org', 'mathp+autofillwatch@chromium.org', @@ -1647,6 +1646,7 @@ 'blink_paint' : ['blink-reviews-paint@chromium.org', 'dongseong.hwang@intel.com'], 'blink_permissions': ['mlamouri+watch-blink@chromium.org'], + 'blink_platform': ['kinuko+watch@chromium.org'], 'blink_platform_graphics': ['ajuma+watch@chromium.org', 'blink-reviews-platform-graphics@chromium.org', 'cabanier@adobe.com', @@ -1822,8 +1822,7 @@ 'developer_recommended_flags': ['scheib+watch@chromium.org'], 'devtools': ['devtools-reviews@chromium.org', 'pfeldman@chromium.org'], - 'disk_cache': ['kinuko+cache@chromium.org', - 'gavinp+disk@chromium.org'], + 'disk_cache': ['gavinp+disk@chromium.org'], 'download': ['asanka@chromium.org'], 'downloads_ui': ['asanka@chromium.org', 'dbeam+watch-downloads@chromium.org'],
diff --git a/android_webview/tools/apk_merger.py b/android_webview/tools/apk_merger.py index 713a58d..74eac37 100755 --- a/android_webview/tools/apk_merger.py +++ b/android_webview/tools/apk_merger.py
@@ -135,7 +135,8 @@ def SignAndAlignApk(tmp_apk, signed_tmp_apk, new_apk, zipalign_path, - keystore_path, key_name, key_password): + keystore_path, key_name, key_password, + page_align_shared_libraries): try: finalize_apk.JarSigner( keystore_path, @@ -148,6 +149,7 @@ try: finalize_apk.AlignApk(zipalign_path, + page_align_shared_libraries, signed_tmp_apk, new_apk) except build_utils.CalledProcessError as e: @@ -222,8 +224,7 @@ parser.add_argument('--key_name', required=True) parser.add_argument('--key_password', required=True) parser.add_argument('--shared_library') - parser.add_argument('--page-align-shared-libraries', action='store_true', - help='Obsolete, but remains for backwards compatibility') + parser.add_argument('--page-align-shared-libraries', action='store_true') parser.add_argument('--uncompress-shared-libraries', action='store_true') parser.add_argument('--debug', action='store_true') # This option shall only used in debug build, see http://crbug.com/631494. @@ -249,7 +250,8 @@ MergeApk(args, tmp_apk, tmp_dir_32, tmp_dir_64) SignAndAlignApk(tmp_apk, signed_tmp_apk, new_apk, args.zipalign_path, - args.keystore_path, args.key_name, args.key_password) + args.keystore_path, args.key_name, args.key_password, + args.page_align_shared_libraries) except ApkMergeFailure as e: print e
diff --git a/ash/common/system/tray/actionable_view.cc b/ash/common/system/tray/actionable_view.cc index 2dd90ed05..bc97b678 100644 --- a/ash/common/system/tray/actionable_view.cc +++ b/ash/common/system/tray/actionable_view.cc
@@ -42,14 +42,10 @@ } void ActionableView::OnPaintFocus(gfx::Canvas* canvas) { - gfx::RectF rect(GetFocusBounds()); + gfx::RectF rect(GetLocalBounds()); canvas->DrawSolidFocusRect(rect, kFocusBorderColor, kFocusBorderThickness); } -gfx::Rect ActionableView::GetFocusBounds() { - return GetLocalBounds(); -} - void ActionableView::HandlePerformActionResult(bool action_performed, const ui::Event& event) { AnimateInkDrop(action_performed ? views::InkDropState::ACTION_TRIGGERED
diff --git a/ash/common/system/tray/actionable_view.h b/ash/common/system/tray/actionable_view.h index bcb71349..9181912 100644 --- a/ash/common/system/tray/actionable_view.h +++ b/ash/common/system/tray/actionable_view.h
@@ -48,9 +48,6 @@ // color. Subclasses can override to change the default settings. virtual void OnPaintFocus(gfx::Canvas* canvas); - // Returns the bounds to paint the focus rectangle in. - virtual gfx::Rect GetFocusBounds(); - // Performs an action when user clicks on the view (on mouse-press event), or // presses a key when this view is in focus. Returns true if the event has // been handled and an action was performed. Returns false otherwise.
diff --git a/ash/common/system/tray/tray_background_view.cc b/ash/common/system/tray/tray_background_view.cc index 59858820..17ac4b59 100644 --- a/ash/common/system/tray/tray_background_view.cc +++ b/ash/common/system/tray/tray_background_view.cc
@@ -560,16 +560,18 @@ ActionableView::HandlePerformActionResult(action_performed, event); } -gfx::Rect TrayBackgroundView::GetFocusBounds() { +void TrayBackgroundView::OnPaintFocus(gfx::Canvas* canvas) { // The tray itself expands to the right and bottom edge of the screen to make // sure clicking on the edges brings up the popup. However, the focus border // should be only around the container. - return GetContentsBounds(); -} - -void TrayBackgroundView::OnPaintFocus(gfx::Canvas* canvas) { - gfx::RectF paint_bounds(GetFocusBounds()); - paint_bounds.Inset(gfx::Insets(2, -2)); + gfx::RectF paint_bounds; + if (MaterialDesignController::IsShelfMaterial()) { + paint_bounds = gfx::RectF(GetBackgroundBounds()); + paint_bounds.Inset(gfx::Insets(-kFocusBorderThickness)); + } else { + paint_bounds = gfx::RectF(GetContentsBounds()); + paint_bounds.Inset(gfx::Insets(1)); + } canvas->DrawSolidFocusRect(paint_bounds, kFocusBorderColor, kFocusBorderThickness); }
diff --git a/ash/common/system/tray/tray_background_view.h b/ash/common/system/tray/tray_background_view.h index e56fb78f..4788f23 100644 --- a/ash/common/system/tray/tray_background_view.h +++ b/ash/common/system/tray/tray_background_view.h
@@ -142,7 +142,6 @@ bool PerformAction(const ui::Event& event) override; void HandlePerformActionResult(bool action_performed, const ui::Event& event) override; - gfx::Rect GetFocusBounds() override; void OnPaintFocus(gfx::Canvas* canvas) override; private:
diff --git a/base/bit_cast.h b/base/bit_cast.h index c9514bc..90dd925 100644 --- a/base/bit_cast.h +++ b/base/bit_cast.h
@@ -9,6 +9,7 @@ #include <type_traits> #include "base/compiler_specific.h" +#include "base/template_util.h" #include "build/build_config.h" // bit_cast<Dest,Source> is a template function that implements the equivalent @@ -63,34 +64,10 @@ inline Dest bit_cast(const Source& source) { static_assert(sizeof(Dest) == sizeof(Source), "bit_cast requires source and destination to be the same size"); - -#if (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1) || \ - (defined(__clang__) && defined(_LIBCPP_VERSION))) - // GCC 5.1 contains the first libstdc++ with is_trivially_copyable. - // Assume libc++ Just Works: is_trivially_copyable added on May 13th 2011. - // However, with libc++ when GCC is the compiler the trait is buggy, see - // crbug.com/607158, so fall back to the less strict variant for non-clang. - static_assert(std::is_trivially_copyable<Dest>::value, - "non-trivially-copyable bit_cast is undefined"); - static_assert(std::is_trivially_copyable<Source>::value, - "non-trivially-copyable bit_cast is undefined"); -#elif HAS_FEATURE(is_trivially_copyable) - // The compiler supports an equivalent intrinsic. - static_assert(__is_trivially_copyable(Dest), - "non-trivially-copyable bit_cast is undefined"); - static_assert(__is_trivially_copyable(Source), - "non-trivially-copyable bit_cast is undefined"); -#elif COMPILER_GCC - // Fallback to compiler intrinsic on GCC and clang (which pretends to be - // GCC). This isn't quite the same as is_trivially_copyable but it'll do for - // our purpose. - static_assert(__has_trivial_copy(Dest), - "non-trivially-copyable bit_cast is undefined"); - static_assert(__has_trivial_copy(Source), - "non-trivially-copyable bit_cast is undefined"); -#else - // Do nothing, let the bots handle it. -#endif + static_assert(base::is_trivially_copyable<Dest>::value, + "bit_cast requires the destination type to be copyable"); + static_assert(base::is_trivially_copyable<Source>::value, + "bit_cast requires the source type to be copyable"); Dest dest; memcpy(&dest, &source, sizeof(dest));
diff --git a/base/bit_cast_unittest.cc b/base/bit_cast_unittest.cc index 757b0c65..f36d3fe 100644 --- a/base/bit_cast_unittest.cc +++ b/base/bit_cast_unittest.cc
@@ -27,6 +27,5 @@ EXPECT_EQ(1, b); } - } // namespace } // namespace base
diff --git a/base/supports_user_data.cc b/base/supports_user_data.cc index 6ba3ff6..d511055 100644 --- a/base/supports_user_data.cc +++ b/base/supports_user_data.cc
@@ -9,12 +9,13 @@ namespace base { SupportsUserData::SupportsUserData() { - // Harmless to construct on a different thread to subsequent usage. - thread_checker_.DetachFromThread(); + // Harmless to construct on a different execution sequence to subsequent + // usage. + sequence_checker_.DetachFromSequence(); } SupportsUserData::Data* SupportsUserData::GetUserData(const void* key) const { - DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(sequence_checker_.CalledOnValidSequence()); DataMap::const_iterator found = user_data_.find(key); if (found != user_data_.end()) return found->second.get(); @@ -22,21 +23,21 @@ } void SupportsUserData::SetUserData(const void* key, Data* data) { - DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(sequence_checker_.CalledOnValidSequence()); user_data_[key] = WrapUnique(data); } void SupportsUserData::RemoveUserData(const void* key) { - DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(sequence_checker_.CalledOnValidSequence()); user_data_.erase(key); } -void SupportsUserData::DetachUserDataThread() { - thread_checker_.DetachFromThread(); +void SupportsUserData::DetachFromSequence() { + sequence_checker_.DetachFromSequence(); } SupportsUserData::~SupportsUserData() { - DCHECK(thread_checker_.CalledOnValidThread() || user_data_.empty()); + DCHECK(sequence_checker_.CalledOnValidSequence() || user_data_.empty()); DataMap local_user_data; user_data_.swap(local_user_data); // Now this->user_data_ is empty, and any destructors called transitively from
diff --git a/base/supports_user_data.h b/base/supports_user_data.h index a9f1c93..a4c7c9f 100644 --- a/base/supports_user_data.h +++ b/base/supports_user_data.h
@@ -11,6 +11,10 @@ #include "base/base_export.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" + +// TODO(gab): Removing this include causes IWYU failures in other headers, +// remove it in a follow- up CL. #include "base/threading/thread_checker.h" namespace base { @@ -26,7 +30,7 @@ // class to any class with a virtual destructor. class BASE_EXPORT Data { public: - virtual ~Data() {} + virtual ~Data() = default; }; // The user data allows the clients to associate data with this object. @@ -38,10 +42,10 @@ void RemoveUserData(const void* key); // SupportsUserData is not thread-safe, and on debug build will assert it is - // only used on one thread. Calling this method allows the caller to hand - // the SupportsUserData instance across threads. Use only if you are taking - // full control of the synchronization of that hand over. - void DetachUserDataThread(); + // only used on one execution sequence. Calling this method allows the caller + // to hand the SupportsUserData instance across execution sequences. Use only + // if you are taking full control of the synchronization of that hand over. + void DetachFromSequence(); protected: virtual ~SupportsUserData(); @@ -52,7 +56,7 @@ // Externally-defined data accessible by key. DataMap user_data_; // Guards usage of |user_data_| - ThreadChecker thread_checker_; + SequenceChecker sequence_checker_; DISALLOW_COPY_AND_ASSIGN(SupportsUserData); };
diff --git a/base/template_util.h b/base/template_util.h index 1bfc1ac..0dc6c95 100644 --- a/base/template_util.h +++ b/base/template_util.h
@@ -23,6 +23,23 @@ #define CR_USE_FALLBACKS_FOR_OLD_GLIBCXX #endif +// Some chromeos bots are using experimental 5.0 for some reason +// which has partial support for type_traits, but misses a smaller subset +// while removing some of the older non-standard stuff. +#define CR_GLIBCXX_5_0_0 20150123 +#if defined(__GLIBCXX__) && (__GLIBCXX__ == CR_GLIBCXX_5_0_0) +#define CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX +#endif + +// This hacks around using gcc with libc++ which has some incompatibilies. +// - is_trivially_* doesn't work: https://llvm.org/bugs/show_bug.cgi?id=27538 +// TODO(danakj): Remove this when android builders are all using a newer version +// of gcc, or the android ndk is updated to a newer libc++ that works with older +// gcc versions. +#if !defined(__clang__) && defined(_LIBCPP_VERSION) +#define CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX +#endif + namespace base { template <class T> struct is_non_const_reference : std::false_type {}; @@ -126,8 +143,53 @@ using is_trivially_destructible = std::is_trivially_destructible<T>; #endif +// is_trivially_copyable is especially hard to get right. +// - Older versions of libstdc++ will fail to have it like they do for other +// type traits. In this case we should provide it based on compiler +// intrinsics. This is covered by the CR_USE_FALLBACKS_FOR_OLD_GLIBCXX define. +// - An experimental release of gcc includes most of type_traits but misses +// is_trivially_copyable, so we still have to avoid using libstdc++ in this +// case, which is covered by CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX. +// - When compiling libc++ from before r239653, with a gcc compiler, the +// std::is_trivially_copyable can fail. So we need to work around that by not +// using the one in libc++ in this case. This is covered by the +// CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX define, and is discussed in +// https://llvm.org/bugs/show_bug.cgi?id=27538#c1 where they point out that +// in libc++'s commit r239653 this is fixed by libc++ checking for gcc 5.1. +// - In both of the above cases we are using the gcc compiler. When defining +// this ourselves on compiler intrinsics, the __is_trivially_copyable() +// intrinsic is not available on gcc before version 5.1 (see the discussion in +// https://llvm.org/bugs/show_bug.cgi?id=27538#c1 again), so we must check for +// that version. +// - When __is_trivially_copyable() is not available because we are on gcc older +// than 5.1, we need to fall back to something, so we use __has_trivial_copy() +// instead based on what was done one-off in bit_cast() previously. + +// TODO(crbug.com/554293): Remove this when all platforms have this in the std +// namespace and it works with gcc as needed. +#if defined(CR_USE_FALLBACKS_FOR_OLD_GLIBCXX) || \ + defined(CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX) || \ + defined(CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX) +template <typename T> +struct is_trivially_copyable { +// TODO(danakj): Remove this when android builders are all using a newer version +// of gcc, or the android ndk is updated to a newer libc++ that does this for +// us. +#if _GNUC_VER >= 501 + static constexpr bool value = __is_trivially_copyable(T); +#else + static constexpr bool value = __has_trivial_copy(T); +#endif +}; +#else +template <class T> +using is_trivially_copyable = std::is_trivially_copyable<T>; +#endif + } // namespace base #undef CR_USE_FALLBACKS_FOR_OLD_GLIBCXX +#undef CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX +#undef CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX #endif // BASE_TEMPLATE_UTIL_H_
diff --git a/build/android/gyp/apkbuilder.py b/build/android/gyp/apkbuilder.py index 5d87625..82ac496ed 100755 --- a/build/android/gyp/apkbuilder.py +++ b/build/android/gyp/apkbuilder.py
@@ -163,15 +163,12 @@ """Add native libraries to APK.""" for path in native_libs: basename = os.path.basename(path) + apk_path = 'lib/%s/%s' % (android_abi, basename) compress = None - if (uncompress and os.path.splitext(basename)[1] == '.so' - and 'android_linker' not in basename): + if (uncompress and os.path.splitext(basename)[1] == '.so'): compress = False - # Add prefix to prevent android install from extracting upon install. - basename = 'crazy.' + basename - apk_path = 'lib/%s/%s' % (android_abi, basename) build_utils.AddToZipHermetic(out_apk, apk_path, src_path=path, @@ -268,9 +265,8 @@ options.uncompress_shared_libraries) for name in sorted(options.native_lib_placeholders): - # Note: Empty libs files are ignored by md5check (can cause issues - # with stale builds when the only change is adding/removing - # placeholders). + # Empty libs files are ignored by md5check, but rezip requires them + # to be empty in order to identify them as placeholders. apk_path = 'lib/%s/%s' % (options.android_abi, name) build_utils.AddToZipHermetic(out_apk, apk_path, data='')
diff --git a/build/android/gyp/finalize_apk.py b/build/android/gyp/finalize_apk.py index ecb5ebfe..532d001f 100755 --- a/build/android/gyp/finalize_apk.py +++ b/build/android/gyp/finalize_apk.py
@@ -21,6 +21,32 @@ from util import build_utils +def RenameInflateAndAddPageAlignment( + rezip_apk_jar_path, in_zip_file, out_zip_file): + rezip_apk_cmd = [ + 'java', + '-classpath', + rezip_apk_jar_path, + 'RezipApk', + 'renamealign', + in_zip_file, + out_zip_file, + ] + build_utils.CheckOutput(rezip_apk_cmd) + + +def ReorderAndAlignApk(rezip_apk_jar_path, in_zip_file, out_zip_file): + rezip_apk_cmd = [ + 'java', + '-classpath', + rezip_apk_jar_path, + 'RezipApk', + 'reorder', + in_zip_file, + out_zip_file, + ] + build_utils.CheckOutput(rezip_apk_cmd) + def JarSigner(key_path, key_name, key_passwd, unsigned_path, signed_path): shutil.copy(unsigned_path, signed_path) @@ -36,15 +62,14 @@ build_utils.CheckOutput(sign_cmd) -def AlignApk(zipalign_path, unaligned_path, final_path): - # Note -p will page align native libraries (files ending with .so), but - # only those that are stored uncompressed. +def AlignApk(zipalign_path, package_align, unaligned_path, final_path): align_cmd = [ zipalign_path, - '-p', - '-f', + '-f' ] + if package_align: + align_cmd += ['-p'] align_cmd += [ '4', # 4 bytes @@ -60,13 +85,23 @@ parser = optparse.OptionParser() build_utils.AddDepfileOption(parser) + parser.add_option('--rezip-apk-jar-path', + help='Path to the RezipApk jar file.') parser.add_option('--zipalign-path', help='Path to the zipalign tool.') + parser.add_option('--page-align-shared-libraries', + action='store_true', + help='Page align shared libraries.') parser.add_option('--unsigned-apk-path', help='Path to input unsigned APK.') parser.add_option('--final-apk-path', help='Path to output signed and aligned APK.') parser.add_option('--key-path', help='Path to keystore for signing.') parser.add_option('--key-passwd', help='Keystore password') parser.add_option('--key-name', help='Keystore name') + parser.add_option('--stamp', help='Path to touch on success.') + parser.add_option('--load-library-from-zip', type='int', + help='If non-zero, build the APK such that the library can be loaded ' + + 'directly from the zip file using the crazy linker. The library ' + + 'will be renamed, uncompressed and page aligned.') options, _ = parser.parse_args() @@ -75,9 +110,14 @@ options.key_path, ] + if options.load_library_from_zip: + input_paths.append(options.rezip_apk_jar_path) + input_strings = [ + options.load_library_from_zip, options.key_name, options.key_passwd, + options.page_align_shared_libraries, ] build_utils.CallAndWriteDepfileIfStale( @@ -89,34 +129,57 @@ output_paths=[options.final_apk_path]) -def _NormalizeZip(path): - with tempfile.NamedTemporaryFile(suffix='.zip') as hermetic_signed_apk: - with zipfile.ZipFile(path, 'r') as zi: - with zipfile.ZipFile(hermetic_signed_apk, 'w') as zo: - for info in zi.infolist(): - # Ignore 'extended local file headers'. Python doesn't write them - # properly (see https://bugs.python.org/issue1742205) which causes - # zipalign to miscalculate alignment. Since we don't use them except - # for alignment anyway, we write a stripped file here and let - # zipalign add them properly later. eLFHs are controlled by 'general - # purpose bit flag 03' (0x08) so we mask that out. - info.flag_bits = info.flag_bits & 0xF7 - - info.date_time = build_utils.HERMETIC_TIMESTAMP - zo.writestr(info, zi.read(info.filename)) - - shutil.copy(hermetic_signed_apk.name, path) - - def FinalizeApk(options): - with tempfile.NamedTemporaryFile() as signed_apk_path_tmp: + with tempfile.NamedTemporaryFile() as signed_apk_path_tmp, \ + tempfile.NamedTemporaryFile() as apk_to_sign_tmp: + + if options.load_library_from_zip: + # We alter the name of the library so that the Android Package Manager + # does not extract it into a separate file. This must be done before + # signing, as the filename is part of the signed manifest. At the same + # time we uncompress the library, which is necessary so that it can be + # loaded directly from the APK. + # Move the library to a page boundary by adding a page alignment file. + apk_to_sign = apk_to_sign_tmp.name + RenameInflateAndAddPageAlignment( + options.rezip_apk_jar_path, options.unsigned_apk_path, apk_to_sign) + else: + apk_to_sign = options.unsigned_apk_path + signed_apk_path = signed_apk_path_tmp.name JarSigner(options.key_path, options.key_name, options.key_passwd, - options.unsigned_apk_path, signed_apk_path) - # Make the newly added signing files hermetic. - _NormalizeZip(signed_apk_path) + apk_to_sign, signed_apk_path) - AlignApk(options.zipalign_path, signed_apk_path, options.final_apk_path) + # Make the signing files hermetic. + with tempfile.NamedTemporaryFile(suffix='.zip') as hermetic_signed_apk: + with zipfile.ZipFile(signed_apk_path, 'r') as zi: + with zipfile.ZipFile(hermetic_signed_apk, 'w') as zo: + for info in zi.infolist(): + # Ignore 'extended local file headers'. Python doesn't write them + # properly (see https://bugs.python.org/issue1742205) which causes + # zipalign to miscalculate alignment. Since we don't use them except + # for alignment anyway, we write a stripped file here and let + # zipalign add them properly later. eLFHs are controlled by 'general + # purpose bit flag 03' (0x08) so we mask that out. + info.flag_bits = info.flag_bits & 0xF7 + + info.date_time = build_utils.HERMETIC_TIMESTAMP + zo.writestr(info, zi.read(info.filename)) + + shutil.copy(hermetic_signed_apk.name, signed_apk_path) + + if options.load_library_from_zip: + # Reorder the contents of the APK. This re-establishes the canonical + # order which means the library will be back at its page aligned location. + # This step also aligns uncompressed items to 4 bytes. + ReorderAndAlignApk( + options.rezip_apk_jar_path, signed_apk_path, options.final_apk_path) + else: + # Align uncompressed items to 4 bytes + AlignApk(options.zipalign_path, + options.page_align_shared_libraries, + signed_apk_path, + options.final_apk_path) if __name__ == '__main__':
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py index 08ae4f3..0205b7c0 100644 --- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py +++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -373,7 +373,7 @@ @classmethod def _GetTimeoutScaleFromAnnotations(cls, annotations): try: - return int(annotations.get('TimeoutScale', 1)) + return int(annotations.get('TimeoutScale', {}).get('value', 1)) except ValueError as e: logging.warning("Non-integer value of TimeoutScale ignored. (%s)", str(e)) return 1
diff --git a/build/android/pylib/local/device/local_device_perf_test_run.py b/build/android/pylib/local/device/local_device_perf_test_run.py index 7bd9d9b3..b34c360 100644 --- a/build/android/pylib/local/device/local_device_perf_test_run.py +++ b/build/android/pylib/local/device/local_device_perf_test_run.py
@@ -81,6 +81,7 @@ def _TestSetUp(self, test): if (self._test_instance.collect_chartjson_data + or self._test_instance.collect_json_data or self._tests[test].get('archive_output_dir')): self._output_dir = tempfile.mkdtemp() @@ -105,7 +106,11 @@ exit_code, output = cmd_helper.GetCmdStatusAndOutputWithTimeout( cmd, timeout, cwd=cwd, shell=True) end_time = time.time() - json_output = self._test_instance.ReadChartjsonOutput(self._output_dir) + chart_json_output = self._test_instance.ReadChartjsonOutput( + self._output_dir) + json_output = '' + if self._test_instance.collect_json_data: + json_output = self._test_instance.ReadJsonOutput(self._output_dir) if exit_code == 0: result_type = base_test_result.ResultType.PASS else: @@ -114,10 +119,12 @@ end_time = time.time() exit_code = -1 output = e.output + chart_json_output = '' json_output = '' result_type = base_test_result.ResultType.TIMEOUT return self._ProcessTestResult(test, cmd, start_time, end_time, exit_code, - output, json_output, result_type) + output, chart_json_output, json_output, + result_type) def _CreateCmd(self, test): cmd = [] @@ -126,6 +133,8 @@ cmd.append(self._tests[test]['cmd']) if self._output_dir: cmd.append('--output-dir=%s' % self._output_dir) + if self._test_instance.collect_json_data: + cmd.append('--output-format=json') return ' '.join(self._ExtendCmd(cmd)) def _ExtendCmd(self, cmd): # pylint: disable=no-self-use @@ -142,7 +151,7 @@ raise NotImplementedError def _ProcessTestResult(self, test, cmd, start_time, end_time, exit_code, - output, json_output, result_type): + output, chart_json_output, json_output, result_type): if exit_code is None: exit_code = -1 @@ -158,7 +167,8 @@ persisted_result = { 'name': test, 'output': [output], - 'chartjson': json_output, + 'chartjson': chart_json_output, + 'json': json_output, 'archive_bytes': archive_bytes, 'exit_code': exit_code, 'actual_exit_code': actual_exit_code,
diff --git a/build/android/pylib/perf/perf_test_instance.py b/build/android/pylib/perf/perf_test_instance.py index 3121d8d..3ccc8d3 100644 --- a/build/android/pylib/perf/perf_test_instance.py +++ b/build/android/pylib/perf/perf_test_instance.py
@@ -61,6 +61,7 @@ super(PerfTestInstance, self).__init__() self._collect_chartjson_data = args.collect_chartjson_data + self._collect_json_data = args.collect_json_data self._dry_run = args.dry_run self._flaky_steps = args.flaky_steps self._output_dir_archive_path = args.output_dir_archive_path @@ -73,6 +74,7 @@ self._min_battery_level = args.min_battery_level self._no_timeout = args.no_timeout self._output_chartjson_data = args.output_chartjson_data + self._output_json_data = args.output_json_data self._output_json_list = args.output_json_list self._print_step = args.print_step self._single_step = ( @@ -107,7 +109,7 @@ data['has_archive'] = persisted_result['archive_bytes'] is not None step_values.append(data) - with file(self._output_json_list, 'w') as o: + with file(self.output_json_list, 'w') as o: o.write(json.dumps(step_values)) return base_test_result.ResultType.PASS except KeyError: @@ -137,13 +139,17 @@ i, persisted_outputs[i]) print output_formatted - if self._output_chartjson_data: - with file(self._output_chartjson_data, 'w') as f: + if self.output_json_data: + with file(self.output_json_data, 'w') as f: + f.write(persisted_result['json']) + + if self.output_chartjson_data: + with file(self.output_chartjson_data, 'w') as f: f.write(persisted_result['chartjson']) - if self._output_dir_archive_path: + if self.output_dir_archive_path: if persisted_result['archive_bytes'] is not None: - with file(self._output_dir_archive_path, 'wb') as f: + with file(self.output_dir_archive_path, 'wb') as f: f.write(persisted_result['archive_bytes']) else: logging.error('The output dir was not archived.') @@ -170,6 +176,21 @@ ' the test.') return '' + @staticmethod + def ReadJsonOutput(output_dir): + if not output_dir: + return '' + json_output_path = os.path.join(output_dir, 'results.json') + try: + with open(json_output_path) as f: + return f.read() + except IOError: + logging.exception('Exception when reading results.json.') + logging.error('This usually means that telemetry did not run, so it could' + ' not generate the file. Please check the device running' + ' the test.') + return '' + def WriteBuildBotJson(self, output_dir): """Write metadata about the buildbot environment to the output dir.""" if not output_dir or not self._write_buildbot_json: @@ -186,6 +207,10 @@ return self._collect_chartjson_data @property + def collect_json_data(self): + return self._collect_json_data + + @property def dry_run(self): return self._dry_run @@ -218,6 +243,10 @@ return self._output_dir_archive_path @property + def output_json_data(self): + return self._output_json_data + + @property def output_json_list(self): return self._output_json_list
diff --git a/build/android/rezip/BUILD.gn b/build/android/rezip/BUILD.gn new file mode 100644 index 0000000..1ace37c --- /dev/null +++ b/build/android/rezip/BUILD.gn
@@ -0,0 +1,10 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +java_library("rezip") { + jar_path = "$root_build_dir/lib.java/rezip_apk.jar" + java_files = [ "RezipApk.java" ] +}
diff --git a/build/android/rezip/RezipApk.java b/build/android/rezip/RezipApk.java new file mode 100644 index 0000000..43d75447 --- /dev/null +++ b/build/android/rezip/RezipApk.java
@@ -0,0 +1,448 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.regex.Pattern; +import java.util.zip.CRC32; + +/** + * Command line tool used to build APKs which support loading the native code library + * directly from the APK file. To construct the APK we rename the native library by + * adding the prefix "crazy." to the filename. This is done to prevent the Android + * Package Manager from extracting the library. The native code must be page aligned + * and uncompressed. The page alignment is implemented by adding a zero filled file + * in front of the the native code library. This tool is designed so that running + * SignApk and/or zipalign on the resulting APK does not break the page alignment. + * This is achieved by outputing the filenames in the same canonical order used + * by SignApk and adding the same alignment fields added by zipalign. + */ +class RezipApk { + // Alignment to use for non-compressed files (must match zipalign). + private static final int ALIGNMENT = 4; + + // Alignment to use for non-compressed *.so files + private static final int LIBRARY_ALIGNMENT = 4096; + + // Files matching this pattern are not copied to the output when adding alignment. + // When reordering and verifying the APK they are copied to the end of the file. + private static Pattern sMetaFilePattern = + Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA)|com/android/otacert))|(" + + Pattern.quote(JarFile.MANIFEST_NAME) + ")$"); + + // Pattern for matching a shared library in the APK + private static Pattern sLibraryPattern = Pattern.compile("^lib/[^/]*/lib.*[.]so$"); + // Pattern for match the crazy linker in the APK + private static Pattern sCrazyLinkerPattern = + Pattern.compile("^lib/[^/]*/libchromium_android_linker.so$"); + // Pattern for matching a crazy loaded shared library in the APK + private static Pattern sCrazyLibraryPattern = Pattern.compile("^lib/[^/]*/crazy.lib.*[.]so$"); + + private static boolean isLibraryFilename(String filename) { + return sLibraryPattern.matcher(filename).matches() + && !sCrazyLinkerPattern.matcher(filename).matches(); + } + + private static boolean isCrazyLibraryFilename(String filename) { + return sCrazyLibraryPattern.matcher(filename).matches(); + } + + private static String renameLibraryForCrazyLinker(String filename) { + int lastSlash = filename.lastIndexOf('/'); + // We rename the library, so that the Android Package Manager + // no longer extracts the library. + return filename.substring(0, lastSlash + 1) + "crazy." + filename.substring(lastSlash + 1); + } + + /** + * Wraps another output stream, counting the number of bytes written. + */ + private static class CountingOutputStream extends OutputStream { + private long mCount = 0; + private OutputStream mOut; + + public CountingOutputStream(OutputStream out) { + this.mOut = out; + } + + /** Returns the number of bytes written. */ + public long getCount() { + return mCount; + } + + @Override public void write(byte[] b, int off, int len) throws IOException { + mOut.write(b, off, len); + mCount += len; + } + + @Override public void write(int b) throws IOException { + mOut.write(b); + mCount++; + } + + @Override public void close() throws IOException { + mOut.close(); + } + + @Override public void flush() throws IOException { + mOut.flush(); + } + } + + private static String outputName(JarEntry entry, boolean rename) { + String inName = entry.getName(); + if (rename && entry.getSize() > 0 && isLibraryFilename(inName)) { + return renameLibraryForCrazyLinker(inName); + } + return inName; + } + + /** + * Comparator used to sort jar entries from the input file. + * Sorting is done based on the output filename (which maybe renamed). + * Filenames are in natural string order, except that filenames matching + * the meta-file pattern are always after other files. This is so the manifest + * and signature are at the end of the file after any alignment file. + */ + private static class EntryComparator implements Comparator<JarEntry> { + private boolean mRename; + + public EntryComparator(boolean rename) { + mRename = rename; + } + + @Override + public int compare(JarEntry j1, JarEntry j2) { + String o1 = outputName(j1, mRename); + String o2 = outputName(j2, mRename); + boolean o1Matches = sMetaFilePattern.matcher(o1).matches(); + boolean o2Matches = sMetaFilePattern.matcher(o2).matches(); + if (o1Matches != o2Matches) { + return o1Matches ? 1 : -1; + } else { + return o1.compareTo(o2); + } + } + } + + // Build an ordered list of jar entries. The jar entries from the input are + // sorted based on the output filenames (which maybe renamed). If |omitMetaFiles| + // is true do not include the jar entries for the META-INF files. + // Entries are ordered in the deterministic order used by SignApk. + private static List<JarEntry> getOutputFileOrderEntries( + JarFile jar, boolean omitMetaFiles, boolean rename) { + List<JarEntry> entries = new ArrayList<JarEntry>(); + for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) { + JarEntry entry = e.nextElement(); + if (entry.isDirectory()) { + continue; + } + if (omitMetaFiles && sMetaFilePattern.matcher(entry.getName()).matches()) { + continue; + } + entries.add(entry); + } + + // We sort the input entries by name. When present META-INF files + // are sorted to the end. + Collections.sort(entries, new EntryComparator(rename)); + return entries; + } + + /** + * Add a zero filled alignment file at this point in the zip file, + * The added file will be added before |name| and after |prevName|. + * The size of the alignment file is such that the location of the + * file |name| will be on a LIBRARY_ALIGNMENT boundary. + * + * Note this arrangement is devised so that running SignApk and/or zipalign on the resulting + * file will not alter the alignment. + * + * @param offset number of bytes into the output file at this point. + * @param timestamp time in millis since the epoch to include in the header. + * @param name the name of the library filename. + * @param prevName the name of the previous file in the archive (or null). + * @param out jar output stream to write the alignment file to. + * + * @throws IOException if the output file can not be written. + */ + private static void addAlignmentFile( + long offset, long timestamp, String name, String prevName, + JarOutputStream out) throws IOException { + + // Compute the start and alignment of the library, as if it was next. + int headerSize = JarFile.LOCHDR + name.length(); + long libOffset = offset + headerSize; + int libNeeded = LIBRARY_ALIGNMENT - (int) (libOffset % LIBRARY_ALIGNMENT); + if (libNeeded == LIBRARY_ALIGNMENT) { + // Already aligned, no need to added alignment file. + return; + } + + // Check that there is not another file between the library and the + // alignment file. + String alignName = name.substring(0, name.length() - 2) + "align"; + if (prevName != null && prevName.compareTo(alignName) >= 0) { + throw new UnsupportedOperationException( + "Unable to insert alignment file, because there is " + + "another file in front of the file to be aligned. " + + "Other file: " + prevName + " Alignment file: " + alignName + + " file: " + name); + } + + // Compute the size of the alignment file header. + headerSize = JarFile.LOCHDR + alignName.length(); + // We are going to add an alignment file of type STORED. This file + // will itself induce a zipalign alignment adjustment. + int extraNeeded = + (ALIGNMENT - (int) ((offset + headerSize) % ALIGNMENT)) % ALIGNMENT; + headerSize += extraNeeded; + + if (libNeeded < headerSize + 1) { + // The header was bigger than the alignment that we need, add another page. + libNeeded += LIBRARY_ALIGNMENT; + } + // Compute the size of the alignment file. + libNeeded -= headerSize; + + // Build the header for the alignment file. + byte[] zeroBuffer = new byte[libNeeded]; + JarEntry alignEntry = new JarEntry(alignName); + alignEntry.setMethod(JarEntry.STORED); + alignEntry.setSize(libNeeded); + alignEntry.setTime(timestamp); + CRC32 crc = new CRC32(); + crc.update(zeroBuffer); + alignEntry.setCrc(crc.getValue()); + + if (extraNeeded != 0) { + alignEntry.setExtra(new byte[extraNeeded]); + } + + // Output the alignment file. + out.putNextEntry(alignEntry); + out.write(zeroBuffer); + out.closeEntry(); + out.flush(); + } + + // Make a JarEntry for the output file which corresponds to the input + // file. The output file will be called |name|. The output file will always + // be uncompressed (STORED). If the input is not STORED it is necessary to inflate + // it to compute the CRC and size of the output entry. + private static JarEntry makeStoredEntry(String name, JarEntry inEntry, JarFile in) + throws IOException { + JarEntry outEntry = new JarEntry(name); + outEntry.setMethod(JarEntry.STORED); + + if (inEntry.getMethod() == JarEntry.STORED) { + outEntry.setCrc(inEntry.getCrc()); + outEntry.setSize(inEntry.getSize()); + } else { + // We are inflating the file. We need to compute the CRC and size. + byte[] buffer = new byte[4096]; + CRC32 crc = new CRC32(); + int size = 0; + int num; + InputStream data = in.getInputStream(inEntry); + while ((num = data.read(buffer)) > 0) { + crc.update(buffer, 0, num); + size += num; + } + data.close(); + outEntry.setCrc(crc.getValue()); + outEntry.setSize(size); + } + return outEntry; + } + + /** + * Copy the contents of the input APK file to the output APK file. If |rename| is + * true then non-empty libraries (*.so) in the input will be renamed by prefixing + * "crazy.". This is done to prevent the Android Package Manager extracting the + * library. Note the crazy linker itself is not renamed, for bootstrapping reasons. + * Empty libraries are not renamed (they are in the APK to workaround a bug where + * the Android Package Manager fails to delete old versions when upgrading). + * There must be exactly one "crazy" library in the output stream. The "crazy" + * library will be uncompressed and page aligned in the output stream. Page + * alignment is implemented by adding a zero filled file, regular alignment is + * implemented by adding a zero filled extra field to the zip file header. If + * |addAlignment| is true a page alignment file is added, otherwise the "crazy" + * library must already be page aligned. Care is taken so that the output is generated + * in the same way as SignApk. This is important so that running SignApk and + * zipalign on the output does not break the page alignment. The archive may not + * contain a "*.apk" as SignApk has special nested signing logic that we do not + * support. + * + * @param in The input APK File. + * @param out The output APK stream. + * @param countOut Counting output stream (to measure the current offset). + * @param addAlignment Whether to add the alignment file or just check. + * @param rename Whether to rename libraries to be "crazy". + * + * @throws IOException if the output file can not be written. + */ + private static void rezip( + JarFile in, JarOutputStream out, CountingOutputStream countOut, + boolean addAlignment, boolean rename) throws IOException { + + List<JarEntry> entries = getOutputFileOrderEntries(in, addAlignment, rename); + long timestamp = System.currentTimeMillis(); + byte[] buffer = new byte[4096]; + boolean firstEntry = true; + String prevName = null; + int numCrazy = 0; + for (JarEntry inEntry : entries) { + // Rename files, if specied. + String name = outputName(inEntry, rename); + if (name.endsWith(".apk")) { + throw new UnsupportedOperationException( + "Nested APKs are not supported: " + name); + } + + // Build the header. + JarEntry outEntry = null; + boolean isCrazy = isCrazyLibraryFilename(name); + if (isCrazy) { + // "crazy" libraries are alway output uncompressed (STORED). + outEntry = makeStoredEntry(name, inEntry, in); + numCrazy++; + if (numCrazy > 1) { + throw new UnsupportedOperationException( + "Found more than one library\n" + + "Multiple libraries are not supported for APKs that use " + + "'load_library_from_zip'.\n" + + "See crbug/388223.\n" + + "Note, check that your build is clean.\n" + + "An unclean build can incorrectly incorporate old " + + "libraries in the APK."); + } + } else if (inEntry.getMethod() == JarEntry.STORED) { + // Preserve the STORED method of the input entry. + outEntry = new JarEntry(inEntry); + outEntry.setExtra(null); + } else { + // Create a new entry so that the compressed len is recomputed. + outEntry = new JarEntry(name); + } + outEntry.setTime(timestamp); + + // Compute and add alignment + long offset = countOut.getCount(); + if (firstEntry) { + // The first entry in a jar file has an extra field of + // four bytes that you can't get rid of; any extra + // data you specify in the JarEntry is appended to + // these forced four bytes. This is JAR_MAGIC in + // JarOutputStream; the bytes are 0xfeca0000. + firstEntry = false; + offset += 4; + } + if (outEntry.getMethod() == JarEntry.STORED) { + if (isCrazy) { + if (addAlignment) { + addAlignmentFile(offset, timestamp, name, prevName, out); + } + // We check that we did indeed get to a page boundary. + offset = countOut.getCount() + JarFile.LOCHDR + name.length(); + if ((offset % LIBRARY_ALIGNMENT) != 0) { + throw new AssertionError( + "Library was not page aligned when verifying page alignment. " + + "Library name: " + name + " Expected alignment: " + + LIBRARY_ALIGNMENT + "Offset: " + offset + " Error: " + + (offset % LIBRARY_ALIGNMENT)); + } + } else { + // This is equivalent to zipalign. + offset += JarFile.LOCHDR + name.length(); + int needed = (ALIGNMENT - (int) (offset % ALIGNMENT)) % ALIGNMENT; + if (needed != 0) { + outEntry.setExtra(new byte[needed]); + } + } + } + out.putNextEntry(outEntry); + + // Copy the data from the input to the output + int num; + InputStream data = in.getInputStream(inEntry); + while ((num = data.read(buffer)) > 0) { + out.write(buffer, 0, num); + } + data.close(); + out.closeEntry(); + out.flush(); + prevName = name; + } + if (numCrazy == 0) { + throw new AssertionError("There was no crazy library in the archive"); + } + } + + private static void usage() { + System.err.println("Usage: prealignapk (addalignment|reorder) input.apk output.apk"); + System.err.println("\"crazy\" libraries are always inflated in the output"); + System.err.println( + " renamealign - rename libraries with \"crazy.\" prefix and add alignment file"); + System.err.println(" align - add alignment file"); + System.err.println(" reorder - re-creates canonical ordering and checks alignment"); + System.exit(2); + } + + public static void main(String[] args) throws IOException { + if (args.length != 3) usage(); + + boolean addAlignment = false; + boolean rename = false; + if (args[0].equals("renamealign")) { + // Normal case. Before signing we rename the library and add an alignment file. + addAlignment = true; + rename = true; + } else if (args[0].equals("align")) { + // LGPL compliance case. Before signing, we add an alignment file to a + // reconstructed APK which already contains the "crazy" library. + addAlignment = true; + rename = false; + } else if (args[0].equals("reorder")) { + // Normal case. After jarsigning we write the file in the canonical order and check. + addAlignment = false; + } else { + usage(); + } + + String inputFilename = args[1]; + String outputFilename = args[2]; + + JarFile inputJar = null; + FileOutputStream outputFile = null; + + try { + inputJar = new JarFile(new File(inputFilename), true); + outputFile = new FileOutputStream(outputFilename); + + CountingOutputStream outCount = new CountingOutputStream(outputFile); + JarOutputStream outputJar = new JarOutputStream(outCount); + + // Match the compression level used by SignApk. + outputJar.setLevel(9); + + rezip(inputJar, outputJar, outCount, addAlignment, rename); + outputJar.close(); + } finally { + if (inputJar != null) inputJar.close(); + if (outputFile != null) outputFile.close(); + } + } +}
diff --git a/build/android/test_runner.py b/build/android/test_runner.py index af1b07d..39e87bc 100755 --- a/build/android/test_runner.py +++ b/build/android/test_runner.py
@@ -502,15 +502,25 @@ group.add_argument( '--output-json-list', type=os.path.realpath, - help='Write a simple list of names from --steps into the given file.') + help='Writes a JSON list of information for each --steps into the given ' + 'file. Information includes runtime and device affinity for each ' + '--steps.') group.add_argument( '--collect-chartjson-data', action='store_true', - help='Cache the chartjson output from each step for later use.') + help='Cache the telemetry chartjson output from each step for later use.') group.add_argument( '--output-chartjson-data', type=os.path.realpath, - help='Write out chartjson into the given file.') + help='Writes telemetry chartjson formatted output into the given file.') + group.add_argument( + '--collect-json-data', + action='store_true', + help='Cache the telemetry JSON output from each step for later use.') + group.add_argument( + '--output-json-data', + type=os.path.realpath, + help='Writes telemetry JSON formatted output into the given file.') # TODO(rnephew): Remove this when everything moves to new option in platform # mode. group.add_argument(
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 6883600..f6052ae 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -32,6 +32,7 @@ # TODO(agrieve): Rename targets below to match above patterns. "*android_webview/glue:glue", + "//build/android/rezip:rezip", "//chrome/test/android/cast_emulator:cast_emulator", ] @@ -1384,6 +1385,7 @@ # keystore_path: Path to keystore to use for signing. # keystore_name: Key alias to use. # keystore_password: Keystore password. + # rezip_apk: Whether to add crazy-linker alignment. template("finalize_apk") { action(target_name) { deps = [] @@ -1426,6 +1428,20 @@ "--key-passwd", invoker.keystore_password, ] + if (defined(invoker.rezip_apk) && invoker.rezip_apk) { + deps += [ "//build/android/rezip" ] + _rezip_jar_path = "$root_build_dir/lib.java/rezip_apk.jar" + args += [ + "--load-library-from-zip=1", + "--rezip-apk-jar-path", + rebase_path(_rezip_jar_path, root_build_dir), + ] + } + + if (defined(invoker.page_align_shared_libraries) && + invoker.page_align_shared_libraries) { + args += [ "--page-align-shared-libraries" ] + } } } @@ -1668,9 +1684,6 @@ "uncompress_shared_libraries", "write_asset_list", ]) - if (!defined(uncompress_shared_libraries)) { - uncompress_shared_libraries = _load_library_from_apk - } deps = _deps + [ ":${_package_resources_target_name}" ] native_libs = _native_libs + _native_libs_even_when_incremental @@ -1691,9 +1704,6 @@ "secondary_native_libs", "uncompress_shared_libraries", ]) - if (!defined(uncompress_shared_libraries)) { - uncompress_shared_libraries = _load_library_from_apk - } _dex_target = "//build/android/incremental_install:bootstrap_java__dex" deps = _incremental_deps + [ ":${_incremental_package_resources_target_name}", @@ -1720,11 +1730,14 @@ _finalize_apk_rule_name = "${target_name}__finalize" finalize_apk(_finalize_apk_rule_name) { + forward_variables_from(invoker, [ "page_align_shared_libraries" ]) + input_apk_path = _packaged_apk_path output_apk_path = _final_apk_path keystore_path = _keystore_path keystore_name = _keystore_name keystore_password = _keystore_password + rezip_apk = _load_library_from_apk public_deps = [ # Generator of the _packaged_apk_path this target takes as input.
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 6d286fe..2a45db4 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -2007,7 +2007,15 @@ _extra_native_libs_deps = [] assert(_extra_native_libs_deps == []) # Mark as used. _extra_native_libs_even_when_incremental = [] + _extra_native_libs_even_when_incremental_deps = [] + assert(_extra_native_libs_even_when_incremental_deps == []) # Mark as used. if (_native_libs_deps != []) { + # zipalign can't align gdb_server, don't pack gdbserver temporarily. + if (is_debug && (!defined(invoker.page_align_shared_libraries) || + !invoker.page_align_shared_libraries)) { + _extra_native_libs_even_when_incremental = [ android_gdbserver ] + } + if (_use_chromium_linker) { _extra_native_libs = [ "$root_shlib_dir/libchromium_android_linker$shlib_extension" ] @@ -2029,6 +2037,7 @@ "deps", "extensions_to_not_compress", "language_splits", + "page_align_shared_libraries", "public_deps", "secondary_native_libs", "shared_resources", @@ -2078,6 +2087,7 @@ _extra_native_libs_even_when_incremental != []) && !_create_abi_split) { deps += _native_libs_deps + _extra_native_libs_deps + + _extra_native_libs_even_when_incremental_deps + [ _native_libs_file_arg_dep ] native_libs_filearg = _native_libs_file_arg native_libs = _extra_native_libs @@ -2138,7 +2148,9 @@ "public_deps", ]) - incremental_deps = deps + [ ":$_manifest_rule" ] + incremental_deps = + deps + _extra_native_libs_even_when_incremental_deps + + [ ":$_manifest_rule" ] deps = [] deps = incremental_deps + _native_libs_deps + _extra_native_libs_deps + [ _native_libs_file_arg_dep ]
diff --git a/cc/blimp/layer_tree_host_remote.cc b/cc/blimp/layer_tree_host_remote.cc index 466a4209..442be97 100644 --- a/cc/blimp/layer_tree_host_remote.cc +++ b/cc/blimp/layer_tree_host_remote.cc
@@ -11,7 +11,6 @@ #include "cc/blimp/engine_picture_cache.h" #include "cc/blimp/picture_data_conversions.h" #include "cc/blimp/remote_compositor_bridge.h" -#include "cc/output/begin_frame_args.h" #include "cc/output/compositor_frame_sink.h" #include "cc/proto/compositor_message.pb.h" #include "cc/proto/gfx_conversions.h" @@ -371,8 +370,10 @@ current_pipeline_stage_ = FramePipelineStage::ANIMATE; base::TimeTicks now = base::TimeTicks::Now(); client_->BeginMainFrame(BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, now, now + kDefaultFrameInterval, + BEGINFRAME_FROM_HERE, begin_frame_source_.source_id(), + begin_frame_number_, now, now + kDefaultFrameInterval, kDefaultFrameInterval, BeginFrameArgs::NORMAL)); + begin_frame_number_++; // We don't run any animations on the layer because threaded animations are // disabled. // TODO(khushalsagar): Revisit this when adding support for animations.
diff --git a/cc/blimp/layer_tree_host_remote.h b/cc/blimp/layer_tree_host_remote.h index e7c3c8b7..b1c1748 100644 --- a/cc/blimp/layer_tree_host_remote.h +++ b/cc/blimp/layer_tree_host_remote.h
@@ -10,6 +10,8 @@ #include "cc/base/cc_export.h" #include "cc/blimp/remote_compositor_bridge_client.h" #include "cc/debug/layer_tree_debug_state.h" +#include "cc/output/begin_frame_args.h" +#include "cc/scheduler/begin_frame_source.h" #include "cc/surfaces/surface_sequence_generator.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_settings.h" @@ -126,6 +128,10 @@ bool visible_ = false; bool defer_commits_ = false; + // Provides |source_id| for BeginFrameArgs we create. + StubBeginFrameSource begin_frame_source_; + uint64_t begin_frame_number_ = BeginFrameArgs::kStartingFrameNumber; + // In threaded/single-threaded mode, the LayerTree and Layers expect scroll/ // scale updates to come from the impl thread only during the main frame. // Since we synchronize state outside of that, this is set so we can
diff --git a/cc/ipc/begin_frame_args.mojom b/cc/ipc/begin_frame_args.mojom index 8b16c62a..ad53658 100644 --- a/cc/ipc/begin_frame_args.mojom +++ b/cc/ipc/begin_frame_args.mojom
@@ -18,6 +18,8 @@ mojo.common.mojom.TimeTicks frame_time; mojo.common.mojom.TimeTicks deadline; mojo.common.mojom.TimeDelta interval; + uint64 sequence_number; + uint32 source_id; BeginFrameArgsType type; bool on_critical_path; };
diff --git a/cc/ipc/begin_frame_args_struct_traits.h b/cc/ipc/begin_frame_args_struct_traits.h index bf3c5a8..4d81339 100644 --- a/cc/ipc/begin_frame_args_struct_traits.h +++ b/cc/ipc/begin_frame_args_struct_traits.h
@@ -24,6 +24,14 @@ return args.interval; } + static uint64_t sequence_number(const cc::BeginFrameArgs& args) { + return args.sequence_number; + } + + static uint32_t source_id(const cc::BeginFrameArgs& args) { + return args.source_id; + } + static cc::mojom::BeginFrameArgsType type(const cc::BeginFrameArgs& args) { return static_cast<cc::mojom::BeginFrameArgsType>(args.type); } @@ -39,6 +47,8 @@ !data.ReadInterval(&out->interval)) { return false; } + out->source_id = data.source_id(); + out->sequence_number = data.sequence_number(); out->type = static_cast<cc::BeginFrameArgs::BeginFrameArgsType>(data.type()); out->on_critical_path = data.on_critical_path();
diff --git a/cc/ipc/cc_param_traits_macros.h b/cc/ipc/cc_param_traits_macros.h index b90e250..1410de7 100644 --- a/cc/ipc/cc_param_traits_macros.h +++ b/cc/ipc/cc_param_traits_macros.h
@@ -159,6 +159,8 @@ IPC_STRUCT_TRAITS_MEMBER(frame_time) IPC_STRUCT_TRAITS_MEMBER(deadline) IPC_STRUCT_TRAITS_MEMBER(interval) + IPC_STRUCT_TRAITS_MEMBER(sequence_number) + IPC_STRUCT_TRAITS_MEMBER(source_id) IPC_STRUCT_TRAITS_MEMBER(type) IPC_STRUCT_TRAITS_END()
diff --git a/cc/output/begin_frame_args.cc b/cc/output/begin_frame_args.cc index 7ef2c484..f7a35ff 100644 --- a/cc/output/begin_frame_args.cc +++ b/cc/output/begin_frame_args.cc
@@ -24,26 +24,37 @@ return "???"; } +constexpr uint64_t BeginFrameArgs::kInvalidFrameNumber; +constexpr uint64_t BeginFrameArgs::kStartingFrameNumber; + BeginFrameArgs::BeginFrameArgs() : frame_time(base::TimeTicks()), deadline(base::TimeTicks()), interval(base::TimeDelta::FromMicroseconds(-1)), + sequence_number(kInvalidFrameNumber), + source_id(0), type(BeginFrameArgs::INVALID), - on_critical_path(true) { -} + on_critical_path(true) {} -BeginFrameArgs::BeginFrameArgs(base::TimeTicks frame_time, +BeginFrameArgs::BeginFrameArgs(uint32_t source_id, + uint64_t sequence_number, + base::TimeTicks frame_time, base::TimeTicks deadline, base::TimeDelta interval, BeginFrameArgs::BeginFrameArgsType type) : frame_time(frame_time), deadline(deadline), interval(interval), + sequence_number(sequence_number), + source_id(source_id), type(type), on_critical_path(true) { + DCHECK_LT(kInvalidFrameNumber, sequence_number); } BeginFrameArgs BeginFrameArgs::Create(BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, base::TimeTicks frame_time, base::TimeTicks deadline, base::TimeDelta interval, @@ -51,9 +62,11 @@ DCHECK_NE(type, BeginFrameArgs::INVALID); DCHECK_NE(type, BeginFrameArgs::BEGIN_FRAME_ARGS_TYPE_MAX); #ifdef NDEBUG - return BeginFrameArgs(frame_time, deadline, interval, type); + return BeginFrameArgs(source_id, sequence_number, frame_time, deadline, + interval, type); #else - BeginFrameArgs args = BeginFrameArgs(frame_time, deadline, interval, type); + BeginFrameArgs args = BeginFrameArgs(source_id, sequence_number, frame_time, + deadline, interval, type); args.created_from = location; return args; #endif @@ -70,6 +83,8 @@ void BeginFrameArgs::AsValueInto(base::trace_event::TracedValue* state) const { state->SetString("type", "BeginFrameArgs"); state->SetString("subtype", TypeToString(type)); + state->SetInteger("source_id", source_id); + state->SetInteger("sequence_number", sequence_number); state->SetDouble("frame_time_us", frame_time.ToInternalValue()); state->SetDouble("deadline_us", deadline.ToInternalValue()); state->SetDouble("interval_us", interval.InMicroseconds());
diff --git a/cc/output/begin_frame_args.h b/cc/output/begin_frame_args.h index 78ddce99..a75f446e8 100644 --- a/cc/output/begin_frame_args.h +++ b/cc/output/begin_frame_args.h
@@ -5,6 +5,7 @@ #ifndef CC_OUTPUT_BEGIN_FRAME_ARGS_H_ #define CC_OUTPUT_BEGIN_FRAME_ARGS_H_ +#include <stdint.h> #include <memory> #include "base/location.h" @@ -49,6 +50,9 @@ }; static const char* TypeToString(BeginFrameArgsType type); + static constexpr uint64_t kInvalidFrameNumber = 0; + static constexpr uint64_t kStartingFrameNumber = 1; + // Creates an invalid set of values. BeginFrameArgs(); @@ -63,6 +67,8 @@ // created by searching for "BeginFrameArgs::Create". // The location argument should **always** be BEGINFRAME_FROM_HERE macro. static BeginFrameArgs Create(CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, base::TimeTicks frame_time, base::TimeTicks deadline, base::TimeDelta interval, @@ -84,11 +90,21 @@ base::TimeTicks frame_time; base::TimeTicks deadline; base::TimeDelta interval; + + // |source_id| and |sequence_number| identify a BeginFrame within a single + // process and are set by the original BeginFrameSource that created the + // BeginFrameArgs. When |source_id| of consecutive BeginFrameArgs changes, + // observers should expect the continuity of |sequence_number| to break. + uint64_t sequence_number; + uint32_t source_id; // |source_id| after |sequence_number| for packing. + BeginFrameArgsType type; bool on_critical_path; private: - BeginFrameArgs(base::TimeTicks frame_time, + BeginFrameArgs(uint32_t source_id, + uint64_t sequence_number, + base::TimeTicks frame_time, base::TimeTicks deadline, base::TimeDelta interval, BeginFrameArgsType type);
diff --git a/cc/output/begin_frame_args_unittest.cc b/cc/output/begin_frame_args_unittest.cc index eb63cc7..65e0e9f6 100644 --- a/cc/output/begin_frame_args_unittest.cc +++ b/cc/output/begin_frame_args_unittest.cc
@@ -14,56 +14,79 @@ TEST(BeginFrameArgsTest, Helpers) { // Quick create methods work - BeginFrameArgs args0 = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + BeginFrameArgs args0 = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); EXPECT_TRUE(args0.IsValid()) << args0; BeginFrameArgs args1 = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 0, -1); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 0, 0, -1); EXPECT_FALSE(args1.IsValid()) << args1; BeginFrameArgs args2 = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 1, 2, 3); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 10, 1, 2, 3); EXPECT_TRUE(args2.IsValid()) << args2; + EXPECT_EQ(123u, args2.source_id); + EXPECT_EQ(10u, args2.sequence_number); EXPECT_EQ(1, args2.frame_time.ToInternalValue()); EXPECT_EQ(2, args2.deadline.ToInternalValue()); EXPECT_EQ(3, args2.interval.ToInternalValue()); EXPECT_EQ(BeginFrameArgs::NORMAL, args2.type); BeginFrameArgs args4 = CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 1, 2, 3, BeginFrameArgs::MISSED); + BEGINFRAME_FROM_HERE, 234, 20, 1, 2, 3, BeginFrameArgs::MISSED); EXPECT_TRUE(args4.IsValid()) << args4; + EXPECT_EQ(234u, args4.source_id); + EXPECT_EQ(20u, args4.sequence_number); EXPECT_EQ(1, args4.frame_time.ToInternalValue()); EXPECT_EQ(2, args4.deadline.ToInternalValue()); EXPECT_EQ(3, args4.interval.ToInternalValue()); EXPECT_EQ(BeginFrameArgs::MISSED, args4.type); // operator== - EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 4, 5, 6), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 4, 5, 6)); + EXPECT_EQ( + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 20, 4, 5, 6), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 20, 4, 5, 6)); EXPECT_NONFATAL_FAILURE( - EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 7, 8, 9, - BeginFrameArgs::MISSED), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 7, 8, 9)), + EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9, BeginFrameArgs::MISSED), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9)), ""); EXPECT_NONFATAL_FAILURE( - EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 4, 5, 6), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 7, 8, 9)), + EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 4, + 5, 6), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9)), + ""); + + EXPECT_NONFATAL_FAILURE( + EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 40, 7, + 8, 9)), + ""); + + EXPECT_NONFATAL_FAILURE( + EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 234, 30, 7, + 8, 9)), ""); // operator<< std::stringstream out1; out1 << args1; - EXPECT_EQ("BeginFrameArgs(NORMAL, 0, 0, -1us)", out1.str()); + EXPECT_EQ("BeginFrameArgs(NORMAL, 0, 1, 0, 0, -1us)", out1.str()); std::stringstream out2; out2 << args2; - EXPECT_EQ("BeginFrameArgs(NORMAL, 1, 2, 3us)", out2.str()); + EXPECT_EQ("BeginFrameArgs(NORMAL, 123, 10, 1, 2, 3us)", out2.str()); // PrintTo - EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 0, 0, -1us)"), + EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 0, 1, 0, 0, -1us)"), ::testing::PrintToString(args1)); - EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 1, 2, 3us)"), + EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 123, 10, 1, 2, 3us)"), ::testing::PrintToString(args2)); } @@ -74,10 +97,12 @@ EXPECT_TRUE(args1.on_critical_path); BeginFrameArgs args2 = BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, base::TimeTicks::FromInternalValue(1), + BEGINFRAME_FROM_HERE, 123, 10, base::TimeTicks::FromInternalValue(1), base::TimeTicks::FromInternalValue(2), base::TimeDelta::FromInternalValue(3), BeginFrameArgs::NORMAL); EXPECT_TRUE(args2.IsValid()) << args2; + EXPECT_EQ(123u, args2.source_id) << args2; + EXPECT_EQ(10u, args2.sequence_number) << args2; EXPECT_EQ(1, args2.frame_time.ToInternalValue()) << args2; EXPECT_EQ(2, args2.deadline.ToInternalValue()) << args2; EXPECT_EQ(3, args2.interval.ToInternalValue()) << args2; @@ -88,7 +113,7 @@ TEST(BeginFrameArgsTest, Location) { tracked_objects::Location expected_location = BEGINFRAME_FROM_HERE; - BeginFrameArgs args = CreateBeginFrameArgsForTesting(expected_location); + BeginFrameArgs args = CreateBeginFrameArgsForTesting(expected_location, 0, 1); EXPECT_EQ(expected_location.ToString(), args.created_from.ToString()); } #endif
diff --git a/cc/scheduler/begin_frame_source.cc b/cc/scheduler/begin_frame_source.cc index 4c859a7..a7ba684d 100644 --- a/cc/scheduler/begin_frame_source.cc +++ b/cc/scheduler/begin_frame_source.cc
@@ -6,6 +6,7 @@ #include <stddef.h> +#include "base/atomic_sequence_num.h" #include "base/auto_reset.h" #include "base/location.h" #include "base/logging.h" @@ -25,7 +26,7 @@ static const double kDoubleTickDivisor = 2.0; } -// BeginFrameObserverBase ----------------------------------------------- +// BeginFrameObserverBase ------------------------------------------------- BeginFrameObserverBase::BeginFrameObserverBase() : last_begin_frame_args_(), dropped_begin_frame_args_(0) { } @@ -33,9 +34,12 @@ const BeginFrameArgs& BeginFrameObserverBase::LastUsedBeginFrameArgs() const { return last_begin_frame_args_; } + void BeginFrameObserverBase::OnBeginFrame(const BeginFrameArgs& args) { DCHECK(args.IsValid()); DCHECK(args.frame_time >= last_begin_frame_args_.frame_time); + DCHECK(args.sequence_number > last_begin_frame_args_.sequence_number || + args.source_id != last_begin_frame_args_.source_id); bool used = OnBeginFrameDerivedImpl(args); if (used) { last_begin_frame_args_ = args; @@ -44,17 +48,31 @@ } } +// BeginFrameSource ------------------------------------------------------- +namespace { +static base::StaticAtomicSequenceNumber g_next_source_id; +} // namespace + +BeginFrameSource::BeginFrameSource() : source_id_(g_next_source_id.GetNext()) {} + +uint32_t BeginFrameSource::source_id() const { + return source_id_; +} + +// StubBeginFrameSource --------------------------------------------------- bool StubBeginFrameSource::IsThrottled() const { return true; } -// SyntheticBeginFrameSource --------------------------------------------- +// SyntheticBeginFrameSource ---------------------------------------------- SyntheticBeginFrameSource::~SyntheticBeginFrameSource() = default; -// BackToBackBeginFrameSource -------------------------------------------- +// BackToBackBeginFrameSource --------------------------------------------- BackToBackBeginFrameSource::BackToBackBeginFrameSource( std::unique_ptr<DelayBasedTimeSource> time_source) - : time_source_(std::move(time_source)), weak_factory_(this) { + : time_source_(std::move(time_source)), + next_sequence_number_(BeginFrameArgs::kStartingFrameNumber), + weak_factory_(this) { time_source_->SetClient(this); // The time_source_ ticks immediately, so we SetActive(true) for a single // tick when we need it, and keep it as SetActive(false) otherwise. @@ -97,8 +115,9 @@ base::TimeTicks frame_time = time_source_->LastTickTime(); base::TimeDelta default_interval = BeginFrameArgs::DefaultInterval(); BeginFrameArgs args = BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, frame_time, frame_time + default_interval, - default_interval, BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_, frame_time, + frame_time + default_interval, default_interval, BeginFrameArgs::NORMAL); + next_sequence_number_++; // This must happen after getting the LastTickTime() from the time source. time_source_->SetActive(false); @@ -113,7 +132,8 @@ // DelayBasedBeginFrameSource --------------------------------------------- DelayBasedBeginFrameSource::DelayBasedBeginFrameSource( std::unique_ptr<DelayBasedTimeSource> time_source) - : time_source_(std::move(time_source)) { + : time_source_(std::move(time_source)), + next_sequence_number_(BeginFrameArgs::kStartingFrameNumber) { time_source_->SetClient(this); } @@ -142,9 +162,10 @@ BeginFrameArgs DelayBasedBeginFrameSource::CreateBeginFrameArgs( base::TimeTicks frame_time, BeginFrameArgs::BeginFrameArgsType type) { - return BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, - time_source_->NextTickTime(), - time_source_->Interval(), type); + uint64_t sequence_number = next_sequence_number_++; + return BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, source_id(), sequence_number, frame_time, + time_source_->NextTickTime(), time_source_->Interval(), type); } void DelayBasedBeginFrameSource::AddObserver(BeginFrameObserver* obs) { @@ -154,14 +175,33 @@ observers_.insert(obs); obs->OnBeginFrameSourcePausedChanged(false); time_source_->SetActive(true); - BeginFrameArgs args = CreateBeginFrameArgs( - time_source_->NextTickTime() - time_source_->Interval(), - BeginFrameArgs::MISSED); + + // Missed args should correspond to |current_begin_frame_args_| (particularly, + // have the same sequence number) if |current_begin_frame_args_| still + // correspond to the last time the time source should have ticked. This may + // not be the case if OnTimerTick() has never run yet, the time source was + // inactive before AddObserver() was called, or the interval changed. In such + // a case, we create new args with a new sequence number. + base::TimeTicks last_or_missed_tick_time = + time_source_->NextTickTime() - time_source_->Interval(); + if (current_begin_frame_args_.IsValid() && + current_begin_frame_args_.frame_time == last_or_missed_tick_time && + current_begin_frame_args_.interval == time_source_->Interval()) { + // Ensure that the args have the right type. + current_begin_frame_args_.type = BeginFrameArgs::MISSED; + } else { + // The args are not up to date and we need to create new ones with the + // missed tick's time and a new sequence number. + current_begin_frame_args_ = + CreateBeginFrameArgs(last_or_missed_tick_time, BeginFrameArgs::MISSED); + } + BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); if (!last_args.IsValid() || - (args.frame_time > - last_args.frame_time + args.interval / kDoubleTickDivisor)) { - obs->OnBeginFrame(args); + (current_begin_frame_args_.frame_time > + last_args.frame_time + + current_begin_frame_args_.interval / kDoubleTickDivisor)) { + obs->OnBeginFrame(current_begin_frame_args_); } } @@ -179,18 +219,21 @@ } void DelayBasedBeginFrameSource::OnTimerTick() { - BeginFrameArgs args = CreateBeginFrameArgs(time_source_->LastTickTime(), - BeginFrameArgs::NORMAL); + current_begin_frame_args_ = CreateBeginFrameArgs(time_source_->LastTickTime(), + BeginFrameArgs::NORMAL); std::unordered_set<BeginFrameObserver*> observers(observers_); for (auto* obs : observers) { BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); if (!last_args.IsValid() || - (args.frame_time > - last_args.frame_time + args.interval / kDoubleTickDivisor)) - obs->OnBeginFrame(args); + (current_begin_frame_args_.frame_time > + last_args.frame_time + + current_begin_frame_args_.interval / kDoubleTickDivisor)) { + obs->OnBeginFrame(current_begin_frame_args_); + } } } +// ExternalBeginFrameSource ----------------------------------------------- ExternalBeginFrameSource::ExternalBeginFrameSource( ExternalBeginFrameSourceClient* client) : client_(client) { @@ -214,6 +257,9 @@ BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); if (!last_args.IsValid() || (missed_begin_frame_args_.frame_time > last_args.frame_time)) { + DCHECK((missed_begin_frame_args_.source_id != last_args.source_id) || + (missed_begin_frame_args_.sequence_number > + last_args.sequence_number)); obs->OnBeginFrame(missed_begin_frame_args_); } }
diff --git a/cc/scheduler/begin_frame_source.h b/cc/scheduler/begin_frame_source.h index bde01afd..c25fff6 100644 --- a/cc/scheduler/begin_frame_source.h +++ b/cc/scheduler/begin_frame_source.h
@@ -26,8 +26,10 @@ virtual ~BeginFrameObserver() {} // The |args| given to OnBeginFrame is guaranteed to have - // |args|.IsValid()==true and have |args|.frame_time - // field be strictly greater than the previous call. + // |args|.IsValid()==true. If |args|.source_id did not change between + // invocations, |args|.sequence_number is guaranteed to be be strictly greater + // than the previous call. Further, |args|.frame_time is guaranteed to be + // greater than or equal to the previous call even if the source_id changes. // // Side effects: This function can (and most of the time *will*) change the // return value of the LastUsedBeginFrameArgs method. See the documentation @@ -99,6 +101,7 @@ // all BeginFrameSources *must* provide. class CC_EXPORT BeginFrameSource { public: + BeginFrameSource(); virtual ~BeginFrameSource() {} // DidFinishFrame provides back pressure to a frame source about frame @@ -116,6 +119,15 @@ // Returns false if the begin frame source will just continue to produce // begin frames without waiting. virtual bool IsThrottled() const = 0; + + // Returns an identifier for this BeginFrameSource. Guaranteed unique within a + // process, but not across processes. This is used to create BeginFrames that + // originate at this source. Note that BeginFrameSources may pass on + // BeginFrames created by other sources, with different IDs. + uint32_t source_id() const; + + private: + uint32_t source_id_; }; // A BeginFrameSource that does nothing. @@ -167,6 +179,7 @@ std::unique_ptr<DelayBasedTimeSource> time_source_; std::unordered_set<BeginFrameObserver*> observers_; std::unordered_set<BeginFrameObserver*> pending_begin_frame_observers_; + uint64_t next_sequence_number_; base::WeakPtrFactory<BackToBackBeginFrameSource> weak_factory_; DISALLOW_COPY_AND_ASSIGN(BackToBackBeginFrameSource); @@ -204,6 +217,8 @@ std::unordered_set<BeginFrameObserver*> observers_; base::TimeTicks last_timebase_; base::TimeDelta authoritative_interval_; + BeginFrameArgs current_begin_frame_args_; + uint64_t next_sequence_number_; DISALLOW_COPY_AND_ASSIGN(DelayBasedBeginFrameSource); };
diff --git a/cc/scheduler/begin_frame_source_unittest.cc b/cc/scheduler/begin_frame_source_unittest.cc index 450e154..8250c6c 100644 --- a/cc/scheduler/begin_frame_source_unittest.cc +++ b/cc/scheduler/begin_frame_source_unittest.cc
@@ -19,6 +19,17 @@ namespace cc { namespace { +// BeginFrameSource testing ---------------------------------------------------- +TEST(BeginFrameSourceTest, SourceIdsAreUnique) { + StubBeginFrameSource source1; + StubBeginFrameSource source2; + StubBeginFrameSource source3; + EXPECT_NE(source1.source_id(), source2.source_id()); + EXPECT_NE(source1.source_id(), source3.source_id()); + EXPECT_NE(source2.source_id(), source3.source_id()); +} + +// BackToBackBeginFrameSource testing ------------------------------------------ class BackToBackBeginFrameSourceTest : public ::testing::Test { protected: static const int64_t kDeadline; @@ -55,20 +66,23 @@ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); source_->AddObserver(obs_.get()); EXPECT_TRUE(task_runner_->HasPendingTasks()); - EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); task_runner_->RunPendingTasks(); - EXPECT_BEGIN_FRAME_USED(*obs_, 1100, 1100 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100, + 1100 + kDeadline, kInterval); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); source_->DidFinishFrame(obs_.get(), 0); task_runner_->RunPendingTasks(); } TEST_F(BackToBackBeginFrameSourceTest, - DidFinishFrameThenRemoveObserverProducesNoFrame) { + RemoveObserverThenDidFinishFrameProducesNoFrame) { EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); task_runner_->RunPendingTasks(); source_->RemoveObserver(obs_.get()); @@ -82,10 +96,11 @@ } TEST_F(BackToBackBeginFrameSourceTest, - RemoveObserverThenDidFinishFrameProducesNoFrame) { + DidFinishFrameThenRemoveObserverProducesNoFrame) { EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); @@ -100,7 +115,8 @@ TogglingObserverThenDidFinishFrameProducesCorrectFrame) { EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); @@ -116,7 +132,8 @@ now_src_->Advance(base::TimeDelta::FromMicroseconds(10)); // The begin frame is posted at the time when the observer was added, // so it ignores changes to "now" afterward. - EXPECT_BEGIN_FRAME_USED(*obs_, 1110, 1110 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1110, + 1110 + kDeadline, kInterval); EXPECT_TRUE(task_runner_->HasPendingTasks()); task_runner_->RunPendingTasks(); } @@ -125,7 +142,8 @@ DidFinishFrameThenTogglingObserverProducesCorrectFrame) { EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); @@ -141,7 +159,8 @@ now_src_->Advance(base::TimeDelta::FromMicroseconds(10)); // Ticks at the time at which the observer was added, ignoring the // last change to "now". - EXPECT_BEGIN_FRAME_USED(*obs_, 1120, 1120 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1120, + 1120 + kDeadline, kInterval); EXPECT_TRUE(task_runner_->HasPendingTasks()); task_runner_->RunPendingTasks(); } @@ -157,7 +176,8 @@ TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameRemainingFrames) { EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); // Runs the pending begin frame. task_runner_->RunPendingTasks(); // While running the begin frame, the next frame was cancelled, this @@ -174,7 +194,8 @@ source_->DidFinishFrame(obs_.get(), 1); EXPECT_FALSE(task_runner_->HasPendingTasks()); - EXPECT_BEGIN_FRAME_USED(*obs_, 1100, 1100 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100, + 1100 + kDeadline, kInterval); source_->DidFinishFrame(obs_.get(), 0); EXPECT_EQ(base::TimeDelta(), task_runner_->DelayToNextTaskTime()); task_runner_->RunPendingTasks(); @@ -183,28 +204,32 @@ TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameMultipleCallsIdempotent) { EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); source_->DidFinishFrame(obs_.get(), 0); source_->DidFinishFrame(obs_.get(), 0); source_->DidFinishFrame(obs_.get(), 0); - EXPECT_BEGIN_FRAME_USED(*obs_, 1100, 1100 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100, + 1100 + kDeadline, kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); source_->DidFinishFrame(obs_.get(), 0); source_->DidFinishFrame(obs_.get(), 0); source_->DidFinishFrame(obs_.get(), 0); - EXPECT_BEGIN_FRAME_USED(*obs_, 1200, 1200 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 1200, + 1200 + kDeadline, kInterval); task_runner_->RunPendingTasks(); } TEST_F(BackToBackBeginFrameSourceTest, DelayInPostedTaskProducesCorrectFrame) { EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); @@ -212,7 +237,8 @@ now_src_->Advance(base::TimeDelta::FromMicroseconds(50)); // Ticks at the time the last frame finished, so ignores the last change to // "now". - EXPECT_BEGIN_FRAME_USED(*obs_, 1100, 1100 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100, + 1100 + kDeadline, kInterval); EXPECT_TRUE(task_runner_->HasPendingTasks()); task_runner_->RunPendingTasks(); @@ -226,15 +252,19 @@ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false); source_->AddObserver(&obs2); - EXPECT_BEGIN_FRAME_USED(obs1, 1000, 1000 + kDeadline, kInterval); - EXPECT_BEGIN_FRAME_USED(obs2, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); source_->DidFinishFrame(&obs1, 0); source_->DidFinishFrame(&obs2, 0); - EXPECT_BEGIN_FRAME_USED(obs1, 1100, 1100 + kDeadline, kInterval); - EXPECT_BEGIN_FRAME_USED(obs2, 1100, 1100 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); @@ -251,18 +281,21 @@ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false); source_->AddObserver(&obs1); - EXPECT_BEGIN_FRAME_USED(obs1, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false); source_->AddObserver(&obs2); - EXPECT_BEGIN_FRAME_USED(obs2, 1100, 1100 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); task_runner_->RunPendingTasks(); now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); source_->DidFinishFrame(&obs1, 0); - EXPECT_BEGIN_FRAME_USED(obs1, 1200, 1200 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 1200, 1200 + kDeadline, + kInterval); task_runner_->RunPendingTasks(); source_->DidFinishFrame(&obs1, 0); @@ -275,7 +308,8 @@ now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); source_->DidFinishFrame(&obs2, 0); - EXPECT_BEGIN_FRAME_USED(obs2, 1300, 1300 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 1300, 1300 + kDeadline, + kInterval); task_runner_->RunPendingTasks(); source_->DidFinishFrame(&obs2, 0); @@ -289,8 +323,10 @@ EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false); source_->AddObserver(&obs1); source_->AddObserver(&obs2); - EXPECT_BEGIN_FRAME_USED(obs1, 1000, 1000 + kDeadline, kInterval); - EXPECT_BEGIN_FRAME_USED(obs2, 1000, 1000 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); task_runner_->RunPendingTasks(); // |obs1| finishes first. @@ -303,8 +339,10 @@ // Because the begin frame source already ticked when |obs1| finished, // we see it as the frame time for both observers. - EXPECT_BEGIN_FRAME_USED(obs1, 1100, 1100 + kDeadline, kInterval); - EXPECT_BEGIN_FRAME_USED(obs2, 1100, 1100 + kDeadline, kInterval); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); task_runner_->RunPendingTasks(); source_->DidFinishFrame(&obs1, 0); @@ -341,18 +379,20 @@ AddObserverCallsOnBeginFrameWithMissedTick) { now_src_->Advance(base::TimeDelta::FromMicroseconds(9010)); EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); - EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 10000, 20000, 10000); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 10000, 20000, + 10000); source_->AddObserver(obs_.get()); // Should cause the last tick to be sent // No tasks should need to be run for this to occur. } TEST_F(DelayBasedBeginFrameSourceTest, AddObserverCallsCausesOnBeginFrame) { EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); - EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 10000, 10000); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000, + 10000); source_->AddObserver(obs_.get()); EXPECT_EQ(10000, task_runner_->NextTaskTime().ToInternalValue()); - EXPECT_BEGIN_FRAME_USED(*obs_, 10000, 20000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000); now_src_->Advance(base::TimeDelta::FromMicroseconds(9010)); task_runner_->RunPendingTasks(); } @@ -361,11 +401,12 @@ task_runner_->SetAutoAdvanceNowToPendingTasks(true); EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); - EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 10000, 10000); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000, + 10000); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 10000, 20000, 10000); - EXPECT_BEGIN_FRAME_USED(*obs_, 20000, 30000, 10000); - EXPECT_BEGIN_FRAME_USED(*obs_, 30000, 40000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000); task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(30001)); source_->RemoveObserver(obs_.get()); @@ -376,21 +417,22 @@ TEST_F(DelayBasedBeginFrameSourceTest, VSyncChanges) { task_runner_->SetAutoAdvanceNowToPendingTasks(true); EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); - EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 10000, 10000); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000, + 10000); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 10000, 20000, 10000); - EXPECT_BEGIN_FRAME_USED(*obs_, 20000, 30000, 10000); - EXPECT_BEGIN_FRAME_USED(*obs_, 30000, 40000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000); task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(30001)); // Update the vsync information source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(27500), base::TimeDelta::FromMicroseconds(10001)); - EXPECT_BEGIN_FRAME_USED(*obs_, 40000, 47502, 10001); - EXPECT_BEGIN_FRAME_USED(*obs_, 47502, 57503, 10001); - EXPECT_BEGIN_FRAME_USED(*obs_, 57503, 67504, 10001); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 47502, 10001); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 47502, 57503, 10001); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 57503, 67504, 10001); task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(60000)); } @@ -399,25 +441,26 @@ source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(500), base::TimeDelta::FromMicroseconds(10000)); EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); - EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 500, 10500, 10000); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 500, 10500, + 10000); source_->AddObserver(obs_.get()); - EXPECT_BEGIN_FRAME_USED(*obs_, 10500, 20500, 10000); - EXPECT_BEGIN_FRAME_USED(*obs_, 20500, 30500, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10500, 20500, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20500, 30500, 10000); task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(20501)); // This will keep the same timebase, so 500, 9999 source_->SetAuthoritativeVSyncInterval( base::TimeDelta::FromMicroseconds(9999)); - EXPECT_BEGIN_FRAME_USED(*obs_, 30500, 40496, 9999); - EXPECT_BEGIN_FRAME_USED(*obs_, 40496, 50495, 9999); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30500, 40496, 9999); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40496, 50495, 9999); task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(40497)); // Change the vsync params, but the new interval will be ignored. source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(400), base::TimeDelta::FromMicroseconds(1)); - EXPECT_BEGIN_FRAME_USED(*obs_, 50495, 60394, 9999); - EXPECT_BEGIN_FRAME_USED(*obs_, 60394, 70393, 9999); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 50495, 60394, 9999); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 60394, 70393, 9999); task_runner_->RunUntilTime(base::TimeTicks::FromInternalValue(60395)); } @@ -427,25 +470,28 @@ // now_src_ starts off at 1000. task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(9010)); EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false); - EXPECT_BEGIN_FRAME_USED_MISSED(obs1, 10000, 20000, 10000); + EXPECT_BEGIN_FRAME_USED_MISSED(obs1, source_->source_id(), 1, 10000, 20000, + 10000); source_->AddObserver(&obs1); // Should cause the last tick to be sent // No tasks should need to be run for this to occur. - EXPECT_BEGIN_FRAME_USED(obs1, 20000, 30000, 10000); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 20000, 30000, 10000); task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000)); EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false); - EXPECT_BEGIN_FRAME_USED_MISSED(obs2, 20000, 30000, 10000); + // Sequence number unchanged for missed frame with time of last normal frame. + EXPECT_BEGIN_FRAME_USED_MISSED(obs2, source_->source_id(), 2, 20000, 30000, + 10000); source_->AddObserver(&obs2); // Should cause the last tick to be sent // No tasks should need to be run for this to occur. - EXPECT_BEGIN_FRAME_USED(obs1, 30000, 40000, 10000); - EXPECT_BEGIN_FRAME_USED(obs2, 30000, 40000, 10000); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 30000, 40000, 10000); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 3, 30000, 40000, 10000); task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000)); source_->RemoveObserver(&obs1); - EXPECT_BEGIN_FRAME_USED(obs2, 40000, 50000, 10000); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 40000, 50000, 10000); task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000)); source_->RemoveObserver(&obs2); @@ -457,7 +503,7 @@ StrictMock<MockBeginFrameObserver> obs; EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); - EXPECT_BEGIN_FRAME_USED_MISSED(obs, 0, 10000, 10000); + EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000, 10000); source_->AddObserver(&obs); source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(5000), @@ -471,7 +517,7 @@ source_->OnUpdateVSyncParameters(base::TimeTicks::FromInternalValue(10000), base::TimeDelta::FromInternalValue(10000)); now_src_->Advance(base::TimeDelta::FromInternalValue(5000)); - EXPECT_BEGIN_FRAME_USED(obs, 10000, 20000, 10000); + EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000); task_runner_->RunPendingTasks(); } @@ -479,7 +525,7 @@ StrictMock<MockBeginFrameObserver> obs; EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); - EXPECT_BEGIN_FRAME_USED_MISSED(obs, 0, 10000, 10000); + EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000, 10000); source_->AddObserver(&obs); source_->RemoveObserver(&obs); @@ -489,6 +535,8 @@ // No missed frame received. EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); + // This does not cause a missed BeginFrame, but the sequence number is + // incremented, because the possible missed frame has different time/interval. source_->AddObserver(&obs); source_->RemoveObserver(&obs); @@ -497,7 +545,10 @@ base::TimeDelta::FromInternalValue(10000)); now_src_->Advance(base::TimeDelta::FromInternalValue(5000)); EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); - EXPECT_BEGIN_FRAME_USED_MISSED(obs, 10000, 20000, 10000); + // Sequence number is incremented again, because the missed frame has + // different time/interval. + EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 3, 10000, 20000, + 10000); source_->AddObserver(&obs); source_->RemoveObserver(&obs); }
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index ace12c6..64f4acd3 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc
@@ -16,6 +16,7 @@ #include "base/run_loop.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" +#include "cc/output/begin_frame_args.h" #include "cc/test/begin_frame_args_test.h" #include "cc/test/fake_external_begin_frame_source.h" #include "cc/test/ordered_simple_task_runner.h" @@ -236,7 +237,9 @@ SchedulerTest() : now_src_(new base::SimpleTestTickClock()), task_runner_(new OrderedSimpleTaskRunner(now_src_.get(), true)), - fake_external_begin_frame_source_(nullptr) { + fake_external_begin_frame_source_(nullptr), + fake_compositor_timing_history_(nullptr), + next_begin_frame_number_(BeginFrameArgs::kStartingFrameNumber) { now_src_->Advance(base::TimeDelta::FromMicroseconds(10000)); // A bunch of tests require NowTicks() // to be > BeginFrameArgs::DefaultInterval() @@ -405,8 +408,10 @@ // Creep the time forward so that any BeginFrameArgs is not equal to the // last one otherwise we violate the BeginFrameSource contract. now_src_->Advance(BeginFrameArgs::DefaultInterval()); - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src()); + BeginFrameArgs args = CreateBeginFrameArgsForTesting( + BEGINFRAME_FROM_HERE, fake_external_begin_frame_source_->source_id(), + next_begin_frame_number_, now_src()); + next_begin_frame_number_++; fake_external_begin_frame_source_->TestOnBeginFrame(args); return args; } @@ -435,6 +440,7 @@ std::unique_ptr<FakeSchedulerClient> client_; std::unique_ptr<TestScheduler> scheduler_; FakeCompositorTimingHistory* fake_compositor_timing_history_; + uint64_t next_begin_frame_number_; }; TEST_F(SchedulerTest, InitializeCompositorFrameSinkDoesNotBeginImplFrame) { @@ -1332,8 +1338,10 @@ // Advance frame and create a begin frame. now_src_->Advance(BeginFrameArgs::DefaultInterval()); - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src()); + BeginFrameArgs args = CreateBeginFrameArgsForTesting( + BEGINFRAME_FROM_HERE, fake_external_begin_frame_source_->source_id(), + next_begin_frame_number_, now_src()); + next_begin_frame_number_++; // Deliver this begin frame super late. now_src_->Advance(BeginFrameArgs::DefaultInterval() * 100);
diff --git a/cc/surfaces/display_scheduler_unittest.cc b/cc/surfaces/display_scheduler_unittest.cc index ec9c221..c7b5bd81 100644 --- a/cc/surfaces/display_scheduler_unittest.cc +++ b/cc/surfaces/display_scheduler_unittest.cc
@@ -89,8 +89,9 @@ base::TimeTicks frame_time = now_src_.NowTicks(); base::TimeDelta interval = BeginFrameArgs::DefaultInterval(); base::TimeTicks deadline = frame_time + interval; + // FakeBeginFrameSource deals with |source_id| and |sequence_number|. fake_begin_frame_source_.TestOnBeginFrame( - BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, + BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, 0, 1, frame_time, deadline, interval, BeginFrameArgs::NORMAL)); }
diff --git a/cc/test/begin_frame_args_test.cc b/cc/test/begin_frame_args_test.cc index 8b4dfd3b..7ce80b5 100644 --- a/cc/test/begin_frame_args_test.cc +++ b/cc/test/begin_frame_args_test.cc
@@ -12,15 +12,20 @@ namespace cc { BeginFrameArgs CreateBeginFrameArgsForTesting( - BeginFrameArgs::CreationLocation location) { - return CreateBeginFrameArgsForTesting(location, base::TimeTicks::Now()); + BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number) { + return CreateBeginFrameArgsForTesting(location, source_id, sequence_number, + base::TimeTicks::Now()); } BeginFrameArgs CreateBeginFrameArgsForTesting( BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, base::TimeTicks frame_time) { return BeginFrameArgs::Create( - location, frame_time, + location, source_id, sequence_number, frame_time, frame_time + BeginFrameArgs::DefaultInterval() - BeginFrameArgs::DefaultEstimatedParentDrawTime(), BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL); @@ -28,40 +33,51 @@ BeginFrameArgs CreateBeginFrameArgsForTesting( BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, int64_t frame_time, int64_t deadline, int64_t interval) { - return BeginFrameArgs::Create( - location, base::TimeTicks::FromInternalValue(frame_time), - base::TimeTicks::FromInternalValue(deadline), - base::TimeDelta::FromInternalValue(interval), BeginFrameArgs::NORMAL); + return BeginFrameArgs::Create(location, source_id, sequence_number, + base::TimeTicks::FromInternalValue(frame_time), + base::TimeTicks::FromInternalValue(deadline), + base::TimeDelta::FromInternalValue(interval), + BeginFrameArgs::NORMAL); } BeginFrameArgs CreateBeginFrameArgsForTesting( BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, int64_t frame_time, int64_t deadline, int64_t interval, BeginFrameArgs::BeginFrameArgsType type) { - return BeginFrameArgs::Create( - location, base::TimeTicks::FromInternalValue(frame_time), - base::TimeTicks::FromInternalValue(deadline), - base::TimeDelta::FromInternalValue(interval), type); + return BeginFrameArgs::Create(location, source_id, sequence_number, + base::TimeTicks::FromInternalValue(frame_time), + base::TimeTicks::FromInternalValue(deadline), + base::TimeDelta::FromInternalValue(interval), + type); } BeginFrameArgs CreateBeginFrameArgsForTesting( BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, base::SimpleTestTickClock* now_src) { base::TimeTicks now = now_src->NowTicks(); return BeginFrameArgs::Create( - location, now, now + BeginFrameArgs::DefaultInterval() - - BeginFrameArgs::DefaultEstimatedParentDrawTime(), + location, source_id, sequence_number, now, + now + BeginFrameArgs::DefaultInterval() - + BeginFrameArgs::DefaultEstimatedParentDrawTime(), BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL); } bool operator==(const BeginFrameArgs& lhs, const BeginFrameArgs& rhs) { - return (lhs.type == rhs.type) && (lhs.frame_time == rhs.frame_time) && - (lhs.deadline == rhs.deadline) && (lhs.interval == rhs.interval); + return (lhs.type == rhs.type) && (lhs.source_id == rhs.source_id) && + (lhs.sequence_number == rhs.sequence_number) && + (lhs.frame_time == rhs.frame_time) && (lhs.deadline == rhs.deadline) && + (lhs.interval == rhs.interval); } ::std::ostream& operator<<(::std::ostream& os, const BeginFrameArgs& args) { @@ -71,6 +87,7 @@ void PrintTo(const BeginFrameArgs& args, ::std::ostream* os) { *os << "BeginFrameArgs(" << BeginFrameArgs::TypeToString(args.type) << ", " + << args.source_id << ", " << args.sequence_number << ", " << args.frame_time.ToInternalValue() << ", " << args.deadline.ToInternalValue() << ", " << args.interval.InMicroseconds() << "us)";
diff --git a/cc/test/begin_frame_args_test.h b/cc/test/begin_frame_args_test.h index d798094..57cee81f 100644 --- a/cc/test/begin_frame_args_test.h +++ b/cc/test/begin_frame_args_test.h
@@ -18,17 +18,25 @@ // Functions for quickly creating BeginFrameArgs BeginFrameArgs CreateBeginFrameArgsForTesting( - BeginFrameArgs::CreationLocation location); + BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number); BeginFrameArgs CreateBeginFrameArgsForTesting( BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, base::TimeTicks frame_time); BeginFrameArgs CreateBeginFrameArgsForTesting( BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, int64_t frame_time, int64_t deadline, int64_t interval); BeginFrameArgs CreateBeginFrameArgsForTesting( BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, int64_t frame_time, int64_t deadline, int64_t interval, @@ -38,6 +46,8 @@ // OrderSimpleTaskRunner. BeginFrameArgs CreateBeginFrameArgsForTesting( BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, base::SimpleTestTickClock* now_src); // gtest helpers -- these *must* be in the same namespace as the types they
diff --git a/cc/test/begin_frame_source_test.cc b/cc/test/begin_frame_source_test.cc index ef1c033..997aa995 100644 --- a/cc/test/begin_frame_source_test.cc +++ b/cc/test/begin_frame_source_test.cc
@@ -35,6 +35,8 @@ FROM_HERE_WITH_EXPLICIT_FUNCTION( "MockBeginFrameObserver::kDefaultBeginFrameArgs"), #endif + 0, + BeginFrameArgs::kStartingFrameNumber, -1, -1, -1);
diff --git a/cc/test/begin_frame_source_test.h b/cc/test/begin_frame_source_test.h index cde725b..8be41ae 100644 --- a/cc/test/begin_frame_source_test.h +++ b/cc/test/begin_frame_source_test.h
@@ -12,24 +12,28 @@ #include "testing/gtest/include/gtest/gtest.h" // Macros to help set up expected calls on the MockBeginFrameObserver. -#define EXPECT_BEGIN_FRAME_DROP(obs, frame_time, deadline, interval) \ - EXPECT_CALL((obs), \ - OnBeginFrame(CreateBeginFrameArgsForTesting( \ - BEGINFRAME_FROM_HERE, frame_time, deadline, interval))) \ - .Times(1) \ +#define EXPECT_BEGIN_FRAME_DROP(obs, source_id, sequence_number, frame_time, \ + deadline, interval) \ + EXPECT_CALL((obs), OnBeginFrame(CreateBeginFrameArgsForTesting( \ + BEGINFRAME_FROM_HERE, source_id, sequence_number, \ + frame_time, deadline, interval))) \ + .Times(1) \ .InSequence((obs).sequence) -#define EXPECT_BEGIN_FRAME_USED(obs, frame_time, deadline, interval) \ - EXPECT_CALL((obs), \ - OnBeginFrame(CreateBeginFrameArgsForTesting( \ - BEGINFRAME_FROM_HERE, frame_time, deadline, interval))) \ - .InSequence((obs).sequence) \ +#define EXPECT_BEGIN_FRAME_USED(obs, source_id, sequence_number, frame_time, \ + deadline, interval) \ + EXPECT_CALL((obs), OnBeginFrame(CreateBeginFrameArgsForTesting( \ + BEGINFRAME_FROM_HERE, source_id, sequence_number, \ + frame_time, deadline, interval))) \ + .InSequence((obs).sequence) \ .WillOnce(::testing::SaveArg<0>(&((obs).last_begin_frame_args))) -#define EXPECT_BEGIN_FRAME_USED_MISSED(obs, frame_time, deadline, interval) \ - EXPECT_CALL((obs), OnBeginFrame(CreateBeginFrameArgsForTesting( \ - BEGINFRAME_FROM_HERE, frame_time, deadline, interval, \ - BeginFrameArgs::MISSED))) \ +#define EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_id, sequence_number, \ + frame_time, deadline, interval) \ + EXPECT_CALL( \ + (obs), OnBeginFrame(CreateBeginFrameArgsForTesting( \ + BEGINFRAME_FROM_HERE, source_id, sequence_number, frame_time, \ + deadline, interval, BeginFrameArgs::MISSED))) \ .InSequence((obs).sequence) \ .WillOnce(::testing::SaveArg<0>(&((obs).last_begin_frame_args))) @@ -40,24 +44,29 @@ // Macros to send BeginFrameArgs on a FakeBeginFrameSink (and verify resulting // observer behaviour). -#define SEND_BEGIN_FRAME(args_equal_to, source, frame_time, deadline, \ - interval) \ - { \ - BeginFrameArgs old_args = (source).TestLastUsedBeginFrameArgs(); \ - BeginFrameArgs new_args = CreateBeginFrameArgsForTesting( \ - BEGINFRAME_FROM_HERE, frame_time, deadline, interval); \ - ASSERT_FALSE(old_args == new_args); \ - (source).TestOnBeginFrame(new_args); \ - EXPECT_EQ(args_equal_to, (source).TestLastUsedBeginFrameArgs()); \ +#define SEND_BEGIN_FRAME(args_equal_to, source, sequence_number, frame_time, \ + deadline, interval) \ + { \ + BeginFrameArgs old_args = (source).TestLastUsedBeginFrameArgs(); \ + BeginFrameArgs new_args = CreateBeginFrameArgsForTesting( \ + BEGINFRAME_FROM_HERE, (source).source_id(), sequence_number, \ + frame_time, deadline, interval); \ + ASSERT_FALSE(old_args == new_args); \ + (source).TestOnBeginFrame(new_args); \ + EXPECT_EQ(args_equal_to, (source).TestLastUsedBeginFrameArgs()); \ } // When dropping LastUsedBeginFrameArgs **shouldn't** change. -#define SEND_BEGIN_FRAME_DROP(source, frame_time, deadline, interval) \ - SEND_BEGIN_FRAME(old_args, source, frame_time, deadline, interval); +#define SEND_BEGIN_FRAME_DROP(source, sequence_number, frame_time, deadline, \ + interval) \ + SEND_BEGIN_FRAME(old_args, source, sequence_number, frame_time, deadline, \ + interval); // When used LastUsedBeginFrameArgs **should** be updated. -#define SEND_BEGIN_FRAME_USED(source, frame_time, deadline, interval) \ - SEND_BEGIN_FRAME(new_args, source, frame_time, deadline, interval); +#define SEND_BEGIN_FRAME_USED(source, sequence_number, frame_time, deadline, \ + interval) \ + SEND_BEGIN_FRAME(new_args, source, sequence_number, frame_time, deadline, \ + interval); namespace cc {
diff --git a/cc/test/begin_frame_source_test_unittest.cc b/cc/test/begin_frame_source_test_unittest.cc index 601da31..d3765cc 100644 --- a/cc/test/begin_frame_source_test_unittest.cc +++ b/cc/test/begin_frame_source_test_unittest.cc
@@ -14,110 +14,110 @@ TEST(MockBeginFrameObserverTest, FailOnMissingCalls) { EXPECT_MOCK_FAILURE({ ::testing::NiceMock<MockBeginFrameObserver> obs; - EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300); - EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 2, 400, 600, 300); - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, + 400, 600, 300)); }); } TEST(MockBeginFrameObserverTest, FailOnMultipleCalls) { EXPECT_MOCK_FAILURE({ ::testing::NiceMock<MockBeginFrameObserver> obs; - EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300); - EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 2, 400, 600, 300); - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, + 100, 200, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, + 100, 200, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, + 400, 600, 300)); }); } TEST(MockBeginFrameObserverTest, FailOnWrongCallOrder) { EXPECT_MOCK_FAILURE({ ::testing::NiceMock<MockBeginFrameObserver> obs; - EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300); - EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 2, 400, 600, 300); - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300)); - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, + 400, 600, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, + 100, 200, 300)); }); } TEST(MockBeginFrameObserverTest, ExpectOnBeginFrame) { ::testing::NiceMock<MockBeginFrameObserver> obs; - EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300); - EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300); - EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 2, 400, 600, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 3, 700, 900, 300); EXPECT_EQ(obs.LastUsedBeginFrameArgs(), MockBeginFrameObserver::kDefaultBeginFrameArgs); obs.OnBeginFrame(CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 100, 200, + BEGINFRAME_FROM_HERE, 0, 1, 100, 200, 300)); // One call to LastUsedBeginFrameArgs - EXPECT_EQ( - obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); + EXPECT_EQ(obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 100, 200, + 300)); obs.OnBeginFrame(CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 400, 600, + BEGINFRAME_FROM_HERE, 0, 2, 400, 600, 300)); // Multiple calls to LastUsedBeginFrameArgs - EXPECT_EQ( - obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300)); - EXPECT_EQ( - obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300)); + EXPECT_EQ(obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, 400, 600, + 300)); + EXPECT_EQ(obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, 400, 600, + 300)); obs.OnBeginFrame(CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 700, 900, + BEGINFRAME_FROM_HERE, 0, 3, 700, 900, 300)); // No calls to LastUsedBeginFrameArgs } TEST(MockBeginFrameObserverTest, ExpectOnBeginFrameStatus) { ::testing::NiceMock<MockBeginFrameObserver> obs; - EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300); - EXPECT_BEGIN_FRAME_DROP(obs, 400, 600, 300); - EXPECT_BEGIN_FRAME_DROP(obs, 450, 650, 300); - EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 1, 100, 200, 300); + EXPECT_BEGIN_FRAME_DROP(obs, 0, 2, 400, 600, 300); + EXPECT_BEGIN_FRAME_DROP(obs, 0, 3, 450, 650, 300); + EXPECT_BEGIN_FRAME_USED(obs, 0, 4, 700, 900, 300); EXPECT_EQ(obs.LastUsedBeginFrameArgs(), MockBeginFrameObserver::kDefaultBeginFrameArgs); // Used - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); - EXPECT_EQ( - obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, + 100, 200, 300)); + EXPECT_EQ(obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 100, 200, + 300)); // Dropped - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300)); - EXPECT_EQ( - obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, + 400, 600, 300)); + EXPECT_EQ(obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 100, 200, + 300)); // Dropped - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 450, 650, 300)); - EXPECT_EQ( - obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3, + 450, 650, 300)); + EXPECT_EQ(obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 100, 200, + 300)); // Used - obs.OnBeginFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 700, 900, 300)); - EXPECT_EQ( - obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 700, 900, 300)); + obs.OnBeginFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 4, + 700, 900, 300)); + EXPECT_EQ(obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 4, 700, 900, + 300)); } } // namespace
diff --git a/cc/test/fake_external_begin_frame_source.cc b/cc/test/fake_external_begin_frame_source.cc index 1a15b09..23051fa 100644 --- a/cc/test/fake_external_begin_frame_source.cc +++ b/cc/test/fake_external_begin_frame_source.cc
@@ -65,9 +65,12 @@ void FakeExternalBeginFrameSource::TestOnBeginFrame( const BeginFrameArgs& args) { DCHECK(CalledOnValidThread()); + BeginFrameArgs modified_args = args; + modified_args.source_id = source_id(); + modified_args.sequence_number = next_begin_frame_number_++; std::set<BeginFrameObserver*> observers(observers_); for (auto* obs : observers) - obs->OnBeginFrame(args); + obs->OnBeginFrame(modified_args); if (tick_automatically_) PostTestOnBeginFrame(); } @@ -77,10 +80,12 @@ base::Bind(&FakeExternalBeginFrameSource::TestOnBeginFrame, weak_ptr_factory_.GetWeakPtr())); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, - base::Bind(begin_frame_task_.callback(), - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE)), + FROM_HERE, base::Bind(begin_frame_task_.callback(), + CreateBeginFrameArgsForTesting( + BEGINFRAME_FROM_HERE, source_id(), + next_begin_frame_number_)), base::TimeDelta::FromMilliseconds(milliseconds_per_frame_)); + next_begin_frame_number_++; } } // namespace cc
diff --git a/cc/test/fake_external_begin_frame_source.h b/cc/test/fake_external_begin_frame_source.h index 8f2d4f09..ced3b3b 100644 --- a/cc/test/fake_external_begin_frame_source.h +++ b/cc/test/fake_external_begin_frame_source.h
@@ -10,6 +10,7 @@ #include "base/cancelable_callback.h" #include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" +#include "cc/output/begin_frame_args.h" #include "cc/scheduler/begin_frame_source.h" namespace cc { @@ -49,6 +50,7 @@ const double milliseconds_per_frame_; Client* client_ = nullptr; bool paused_ = false; + uint64_t next_begin_frame_number_ = BeginFrameArgs::kStartingFrameNumber; std::set<BeginFrameObserver*> observers_; base::CancelableCallback<void(const BeginFrameArgs&)> begin_frame_task_; base::WeakPtrFactory<FakeExternalBeginFrameSource> weak_ptr_factory_;
diff --git a/cc/test/fake_layer_tree_host_impl.cc b/cc/test/fake_layer_tree_host_impl.cc index 445b63d..d6b2cd07 100644 --- a/cc/test/fake_layer_tree_host_impl.cc +++ b/cc/test/fake_layer_tree_host_impl.cc
@@ -40,7 +40,7 @@ // Start an impl frame so tests have a valid frame_time to work with. base::TimeTicks time_ticks = base::TimeTicks::FromInternalValue(1); WillBeginImplFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks)); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, time_ticks)); } FakeLayerTreeHostImpl::~FakeLayerTreeHostImpl() {
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index c540c24d..b2fd2e17 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -207,7 +207,7 @@ host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f); // Set the BeginFrameArgs so that methods which use it are able to. host_impl_->WillBeginImplFrame(CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, + BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks() + base::TimeDelta::FromMilliseconds(1))); host_impl_->DidFinishImplFrame(); @@ -362,8 +362,8 @@ } LayerImpl* SetupScrollAndContentsLayers(const gfx::Size& content_size) { - LayerImpl* scroll_layer = CreateScrollAndContentsLayers( - host_impl_->active_tree(), content_size); + LayerImpl* scroll_layer = + CreateScrollAndContentsLayers(host_impl_->active_tree(), content_size); host_impl_->active_tree()->DidBecomeActive(); return scroll_layer; } @@ -748,10 +748,11 @@ DrawFrame(); // We should not crash if the tree is replaced while we are scrolling. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); host_impl_->active_tree()->DetachLayers(); scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100)); @@ -780,9 +781,8 @@ host_impl_->GetEventListenerProperties(EventListenerClass::kMouseWheel)); // But they don't influence the actual handling of the scroll gestures. - InputHandler::ScrollStatus status = - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL); + InputHandler::ScrollStatus status = host_impl_->ScrollBegin( + BeginState(gfx::Point()).get(), InputHandler::WHEEL); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -1130,64 +1130,64 @@ // Trying to scroll to the left/top will not succeed. EXPECT_FALSE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get()) .did_scroll); EXPECT_FALSE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get()) .did_scroll); EXPECT_FALSE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get()) .did_scroll); // Scrolling to the right/bottom will succeed. EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(10, 0)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(10, 0)).get()) .did_scroll); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()) .did_scroll); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(10, 10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(10, 10)).get()) .did_scroll); // Scrolling to left/top will now succeed. EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get()) .did_scroll); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get()) .did_scroll); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get()) .did_scroll); // Scrolling diagonally against an edge will succeed. EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(10, -10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(10, -10)).get()) .did_scroll); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get()) .did_scroll); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(-10, 10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(-10, 10)).get()) .did_scroll); // Trying to scroll more than the available space will also succeed. EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(5000, 5000)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(5000, 5000)).get()) .did_scroll); } @@ -1211,10 +1211,11 @@ DrawFrame(); gfx::Point scroll_position(10, 10); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(scroll_position).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(), overflow->CurrentScrollOffset()); @@ -1229,10 +1230,11 @@ DrawFrame(); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(scroll_position).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset()); @@ -1245,10 +1247,11 @@ host_impl_->active_tree()->BuildPropertyTreesForTesting(); DrawFrame(); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(scroll_position).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset()); @@ -1345,7 +1348,7 @@ base::TimeTicks now = base::TimeTicks::Now(); host_impl_->WillBeginImplFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now)); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, now)); // TODO(crbug.com/551134): We always request a new frame and a draw for // animations that are on the pending tree, but we don't need to do that @@ -1457,7 +1460,7 @@ base::TimeTicks now = base::TimeTicks::Now(); host_impl_->WillBeginImplFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now)); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, now)); EXPECT_TRUE(did_request_next_frame_); did_request_next_frame_ = false; @@ -1537,7 +1540,7 @@ base::TimeTicks now = base::TimeTicks::Now(); host_impl_->WillBeginImplFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now)); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, now)); host_impl_->ActivateAnimations(); host_impl_->Animate(); @@ -1651,8 +1654,9 @@ gfx::Vector2d scroll_delta(0, 10); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::WHEEL) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), + InputHandler::WHEEL) .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -1680,9 +1684,8 @@ inner_scroll_layer->SetDrawsContent(true); host_impl_->active_tree()->BuildPropertyTreesForTesting(); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(500, 500), - outer_scroll_layer->MaxScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(500, 500), + outer_scroll_layer->MaxScrollOffset()); host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(), InputHandler::TOUCHSCREEN); @@ -1692,12 +1695,10 @@ host_impl_->ScrollEnd(EndState().get()); // Sanity check - we're zoomed in, starting from the origin. - EXPECT_VECTOR_EQ( - gfx::Vector2dF(0, 0), - outer_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(0, 0), - inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), + outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), + inner_scroll_layer->CurrentScrollOffset()); // Scroll down - only the inner viewport should scroll. host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), @@ -1706,12 +1707,10 @@ UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100.f, 100.f)).get()); host_impl_->ScrollEnd(EndState().get()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(50, 50), - inner_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(0, 0), - outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), + inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), + outer_scroll_layer->CurrentScrollOffset()); // Scroll down - outer viewport should start scrolling after the inner is at // its maximum. @@ -1721,12 +1720,10 @@ UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000.f, 1000.f)).get()); host_impl_->ScrollEnd(EndState().get()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(250, 250), - inner_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(300, 300), - outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(250, 250), + inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(300, 300), + outer_scroll_layer->CurrentScrollOffset()); } // Make sure scrolls smaller than a unit applied to the viewport don't get @@ -1747,9 +1744,8 @@ host_impl_->active_tree()->BuildPropertyTreesForTesting(); // Sanity checks. - EXPECT_VECTOR_EQ( - gfx::Vector2dF(500, 500), - outer_scroll_layer->MaxScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(500, 500), + outer_scroll_layer->MaxScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(), outer_scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(), inner_scroll_layer->CurrentScrollOffset()); @@ -1758,12 +1754,10 @@ InputHandler::TOUCHSCREEN); host_impl_->ScrollBy( UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.125f, 0.125f)).get()); - EXPECT_VECTOR2DF_EQ( - gfx::Vector2dF(0.125f, 0.125f), - outer_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR2DF_EQ( - gfx::Vector2dF(0, 0), - inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0.125f, 0.125f), + outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0, 0), + inner_scroll_layer->CurrentScrollOffset()); host_impl_->ScrollEnd(EndState().get()); host_impl_->active_tree()->PushPageScaleFromMainThread(2.f, 1.f, 2.f); @@ -1773,12 +1767,10 @@ InputHandler::TOUCHSCREEN); host_impl_->ScrollBy( UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.5f, 0.5f)).get()); - EXPECT_VECTOR2DF_EQ( - gfx::Vector2dF(0.125f, 0.125f), - outer_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR2DF_EQ( - gfx::Vector2dF(0.25f, 0.25f), - inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0.125f, 0.125f), + outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0.25f, 0.25f), + inner_scroll_layer->CurrentScrollOffset()); host_impl_->ScrollEnd(EndState().get()); } @@ -1800,44 +1792,37 @@ inner_scroll_layer->SetDrawsContent(true); host_impl_->active_tree()->BuildPropertyTreesForTesting(); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(500, 500), - outer_scroll_layer->MaxScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(500, 500), + outer_scroll_layer->MaxScrollOffset()); host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(), InputHandler::TOUCHSCREEN); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(2, gfx::Point(250, 250)); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(0, 0), - outer_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(125, 125), - inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), + outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(125, 125), + inner_scroll_layer->CurrentScrollOffset()); // Needed so that the pinch is accounted for in draw properties. DrawFrame(); host_impl_->ScrollBy( UpdateState(gfx::Point(250, 250), gfx::Vector2dF(10.f, 10.f)).get()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(0, 0), - outer_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(130, 130), - inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), + outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(130, 130), + inner_scroll_layer->CurrentScrollOffset()); DrawFrame(); host_impl_->ScrollBy( UpdateState(gfx::Point(250, 250), gfx::Vector2dF(400.f, 400.f)).get()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(80, 80), - outer_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(250, 250), - inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(80, 80), + outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(250, 250), + inner_scroll_layer->CurrentScrollOffset()); host_impl_->PinchGestureEnd(); host_impl_->ScrollEnd(EndState().get()); @@ -1950,12 +1935,9 @@ UpdateState(gfx::Point(0, 0), gfx::Vector2dF(10.f, 20.f)).get()); host_impl_->ScrollEnd(EndState().get()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(5, 10), - inner_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(), - outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 10), + inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(), outer_scroll_layer->CurrentScrollOffset()); // Scroll by the inner viewport's max scroll extent, the remainder // should bubble up to the outer viewport. @@ -1965,12 +1947,10 @@ UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100.f, 100.f)).get()); host_impl_->ScrollEnd(EndState().get()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(50, 50), - inner_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(5, 10), - outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), + inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 10), + outer_scroll_layer->CurrentScrollOffset()); // Scroll by the outer viewport's max scroll extent, it should all go to the // outer viewport. @@ -1980,12 +1960,10 @@ UpdateState(gfx::Point(0, 0), gfx::Vector2dF(190.f, 180.f)).get()); host_impl_->ScrollEnd(EndState().get()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(100, 100), - outer_scroll_layer->CurrentScrollOffset()); - EXPECT_VECTOR_EQ( - gfx::Vector2dF(50, 50), - inner_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(100, 100), + outer_scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), + inner_scroll_layer->CurrentScrollOffset()); } TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) { @@ -2021,9 +1999,9 @@ // Set up two scrolling children of the root, one of which is a scroll parent // to the other. Scrolls shouldn't bubbling from the child. - LayerImpl *parent; - LayerImpl *child; - LayerImpl *child_clip; + LayerImpl* parent; + LayerImpl* child; + LayerImpl* child_clip; std::unique_ptr<LayerImpl> scroll_parent_clip = LayerImpl::Create(host_impl_->active_tree(), 6); @@ -2104,7 +2082,6 @@ } } - TEST_F(LayerTreeHostImplTest, PinchGesture) { SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->SetViewportSize(gfx::Size(50, 50)); @@ -2336,14 +2313,14 @@ float min_page_scale = 0.5f; float max_page_scale = 4.f; - base::TimeTicks start_time = base::TimeTicks() + - base::TimeDelta::FromSeconds(1); + base::TimeTicks start_time = + base::TimeTicks() + base::TimeDelta::FromSeconds(1); base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100); base::TimeTicks halfway_through_animation = start_time + duration / 2; base::TimeTicks end_time = start_time + duration; BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); // Non-anchor zoom-in { @@ -2367,6 +2344,7 @@ did_request_redraw_ = false; did_request_next_frame_ = false; begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_redraw_); @@ -2376,6 +2354,7 @@ did_request_redraw_ = false; did_request_next_frame_ = false; begin_frame_args.frame_time = halfway_through_animation; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_redraw_); @@ -2386,6 +2365,7 @@ did_request_next_frame_ = false; did_request_commit_ = false; begin_frame_args.frame_time = end_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_commit_); @@ -2425,6 +2405,7 @@ did_request_redraw_ = false; did_request_next_frame_ = false; begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_redraw_); @@ -2435,6 +2416,7 @@ did_request_commit_ = false; did_request_next_frame_ = false; begin_frame_args.frame_time = end_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_redraw_); @@ -2461,14 +2443,14 @@ float min_page_scale = 0.5f; float max_page_scale = 4.f; - base::TimeTicks start_time = base::TimeTicks() + - base::TimeDelta::FromSeconds(1); + base::TimeTicks start_time = + base::TimeTicks() + base::TimeDelta::FromSeconds(1); base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100); base::TimeTicks halfway_through_animation = start_time + duration / 2; base::TimeTicks end_time = start_time + duration; BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); // Anchor zoom with unchanged page scale should not change scroll or scale. { @@ -2485,17 +2467,20 @@ duration))); host_impl_->ActivateSyncTree(); begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->DidFinishImplFrame(); begin_frame_args.frame_time = halfway_through_animation; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_redraw_); host_impl_->DidFinishImplFrame(); begin_frame_args.frame_time = end_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_commit_); @@ -2511,9 +2496,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationTransferedOnSyncTreeActivate) { host_impl_->CreatePendingTree(); host_impl_->pending_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f); - CreateScrollAndContentsLayers( - host_impl_->pending_tree(), - gfx::Size(100, 100)); + CreateScrollAndContentsLayers(host_impl_->pending_tree(), + gfx::Size(100, 100)); host_impl_->pending_tree()->BuildPropertyTreesForTesting(); host_impl_->ActivateSyncTree(); DrawFrame(); @@ -2527,8 +2511,8 @@ max_page_scale); host_impl_->ActivateSyncTree(); - base::TimeTicks start_time = base::TimeTicks() + - base::TimeDelta::FromSeconds(1); + base::TimeTicks start_time = + base::TimeTicks() + base::TimeDelta::FromSeconds(1); base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100); base::TimeTicks third_through_animation = start_time + duration / 3; base::TimeTicks halfway_through_animation = start_time + duration / 2; @@ -2536,7 +2520,7 @@ float target_scale = 2.f; BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); scroll_layer->layer_tree_impl() ->property_trees() @@ -2562,6 +2546,7 @@ std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation( gfx::Vector2d(), false, target_scale, duration))); begin_frame_args.frame_time = halfway_through_animation; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_FALSE(did_request_next_frame_); @@ -2572,7 +2557,7 @@ // It should also clear the pointer on the sync tree. host_impl_->ActivateSyncTree(); EXPECT_EQ(nullptr, - host_impl_->sync_tree()->TakePendingPageScaleAnimation().get()); + host_impl_->sync_tree()->TakePendingPageScaleAnimation().get()); EXPECT_FALSE(did_request_redraw_); EXPECT_TRUE(did_request_next_frame_); @@ -2585,6 +2570,7 @@ did_request_redraw_ = false; did_request_next_frame_ = false; begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_redraw_); @@ -2594,6 +2580,7 @@ did_request_redraw_ = false; did_request_next_frame_ = false; begin_frame_args.frame_time = third_through_animation; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_redraw_); @@ -2606,6 +2593,7 @@ did_request_redraw_ = false; did_request_next_frame_ = false; begin_frame_args.frame_time = halfway_through_animation; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_redraw_); @@ -2616,6 +2604,7 @@ did_request_next_frame_ = false; did_request_commit_ = false; begin_frame_args.frame_time = end_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_commit_); @@ -2644,7 +2633,7 @@ base::TimeTicks end_time = start_time + duration; BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.5f, 4.f); scroll_layer->layer_tree_impl() @@ -2658,18 +2647,21 @@ gfx::Vector2d(), false, 2.f, duration))); host_impl_->ActivateSyncTree(); begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_FALSE(did_complete_page_scale_animation_); host_impl_->DidFinishImplFrame(); begin_frame_args.frame_time = halfway_through_animation; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_FALSE(did_complete_page_scale_animation_); host_impl_->DidFinishImplFrame(); begin_frame_args.frame_time = end_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_complete_page_scale_animation_); @@ -2720,7 +2712,7 @@ 0) {} BeginFrameArgs CurrentBeginFrameArgs() const override { - return CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, + return CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, fake_current_physical_time_); } @@ -2807,7 +2799,7 @@ // Before the scrollbar animation exists, we should not get redraws. BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, fake_now); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, fake_now); host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_FALSE(did_request_next_frame_); @@ -2844,7 +2836,7 @@ if (expecting_animations) { // Before the scrollbar animation begins, we should not get redraws. begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, fake_now); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3, fake_now); host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_FALSE(did_request_next_frame_); @@ -2864,7 +2856,7 @@ // After the scrollbar animation begins, we should start getting redraws. begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, fake_now); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 4, fake_now); host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_TRUE(did_request_next_frame_); @@ -2902,7 +2894,7 @@ // Scrolling should have stopped the animation, so we should not be // getting redraws. begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, fake_now); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 5, fake_now); host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_FALSE(did_request_next_frame_); @@ -3306,10 +3298,11 @@ } // Scrolling should update metadata immediately. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); { CompositorFrameMetadata metadata = @@ -4591,8 +4584,7 @@ host_impl_->ScrollEnd(EndState().get()); // The viewport offset shouldn't have changed. - EXPECT_EQ(viewport_offset, - host_impl_->active_tree()->TotalScrollOffset()); + EXPECT_EQ(viewport_offset, host_impl_->active_tree()->TotalScrollOffset()); // Scroll the viewports to max scroll offset. SetScrollOffsetDelta(outer_scroll, gfx::Vector2dF(0, 200.f)); @@ -4629,9 +4621,9 @@ // Browser controls were hidden by 25px so the inner viewport should have // expanded by that much. LayerImpl* outer_container = - host_impl_->active_tree()->OuterViewportContainerLayer(); + host_impl_->active_tree()->OuterViewportContainerLayer(); LayerImpl* inner_container = - host_impl_->active_tree()->InnerViewportContainerLayer(); + host_impl_->active_tree()->InnerViewportContainerLayer(); EXPECT_EQ(gfx::SizeF(100.f, 100.f + 25.f), inner_container->BoundsForScrolling()); @@ -4826,12 +4818,13 @@ BOTH, SHOWN, true); base::TimeTicks start_time = base::TimeTicks::Now(); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); // Pump an animation frame to put some delta in the browser controls. { begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(50); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -4858,6 +4851,7 @@ { begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(200); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -4914,10 +4908,11 @@ host_impl_->SetViewportSize(surface_size); DrawFrame(); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL) + .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_TRUE(did_request_redraw_); @@ -4939,10 +4934,11 @@ host_impl_->SetViewportSize(surface_size); DrawFrame(); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL) + .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_TRUE(did_request_redraw_); @@ -5064,10 +5060,11 @@ gfx::Vector2d scroll_delta(0, 10); gfx::Vector2d expected_scroll_delta = scroll_delta; gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset(); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL) + .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -5114,10 +5111,11 @@ gfx::Vector2d scroll_delta(0, 10); gfx::Vector2d expected_scroll_delta = scroll_delta; gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset(); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL) + .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -5216,10 +5214,11 @@ gfx::Vector2d scroll_delta(0, 10); gfx::Vector2d expected_scroll_delta(scroll_delta); gfx::ScrollOffset expected_max_scroll(outer_scroll->MaxScrollOffset()); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL) + .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -5279,10 +5278,11 @@ DrawFrame(); { gfx::Vector2d scroll_delta(-8, -7); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -5350,8 +5350,9 @@ { gfx::Vector2d scroll_delta(0, -10); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::NON_BUBBLING_GESTURE) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), + InputHandler::NON_BUBBLING_GESTURE) .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -5378,8 +5379,9 @@ // The next time we scroll we should only scroll the parent. scroll_delta = gfx::Vector2d(0, -3); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::NON_BUBBLING_GESTURE) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), + InputHandler::NON_BUBBLING_GESTURE) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); @@ -5400,8 +5402,9 @@ // should still scroll the child. scroll_delta = gfx::Vector2d(0, 7); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::NON_BUBBLING_GESTURE) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), + InputHandler::NON_BUBBLING_GESTURE) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); @@ -5424,8 +5427,9 @@ scroll_delta = gfx::Vector2d(0, -2); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(1, 1)).get(), - InputHandler::NON_BUBBLING_GESTURE) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(1, 1)).get(), + InputHandler::NON_BUBBLING_GESTURE) .thread); EXPECT_EQ(grand_child, host_impl_->CurrentlyScrollingLayer()); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); @@ -5473,8 +5477,9 @@ { gfx::Vector2d scroll_delta(0, 4); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::WHEEL) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), + InputHandler::WHEEL) .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -5549,10 +5554,11 @@ host_impl_->active_tree()->DidBecomeActive(); // Scrolling should still work even though we did not draw yet. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL) + .thread); } TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) { @@ -5591,10 +5597,11 @@ // Reset and scroll down with the wheel. SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF()); gfx::Vector2d wheel_scroll_delta(0, 10); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), wheel_scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -5769,8 +5776,7 @@ } TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) { - LayerImpl* scroll_layer = - SetupScrollAndContentsLayers(gfx::Size(100, 100)); + LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100)); // Scale the layer to twice its normal size. int scale = 2; @@ -5804,10 +5810,11 @@ // Reset and scroll down with the wheel. SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF()); gfx::Vector2d wheel_scroll_delta(0, 10); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), wheel_scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -5866,29 +5873,17 @@ } void DeliverInputForBeginFrame() override {} - gfx::ScrollOffset last_set_scroll_offset() { - return last_set_scroll_offset_; - } + gfx::ScrollOffset last_set_scroll_offset() { return last_set_scroll_offset_; } - gfx::ScrollOffset max_scroll_offset() const { - return max_scroll_offset_; - } + gfx::ScrollOffset max_scroll_offset() const { return max_scroll_offset_; } - gfx::SizeF scrollable_size() const { - return scrollable_size_; - } + gfx::SizeF scrollable_size() const { return scrollable_size_; } - float page_scale_factor() const { - return page_scale_factor_; - } + float page_scale_factor() const { return page_scale_factor_; } - float min_page_scale_factor() const { - return min_page_scale_factor_; - } + float min_page_scale_factor() const { return min_page_scale_factor_; } - float max_page_scale_factor() const { - return max_page_scale_factor_; - } + float max_page_scale_factor() const { return max_page_scale_factor_; } private: gfx::ScrollOffset last_set_scroll_offset_; @@ -6050,10 +6045,11 @@ EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll()); // In-bounds scrolling does not affect overscroll. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); scroll_result = host_impl_->ScrollBy( UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); EXPECT_TRUE(scroll_result.did_scroll); @@ -6212,8 +6208,9 @@ { gfx::Vector2d scroll_delta(0, -10); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::NON_BUBBLING_GESTURE) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), + InputHandler::NON_BUBBLING_GESTURE) .thread); scroll_result = host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); @@ -6226,8 +6223,9 @@ // should still not reach the root layer. scroll_delta = gfx::Vector2d(0, -30); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::NON_BUBBLING_GESTURE) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), + InputHandler::NON_BUBBLING_GESTURE) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer); EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll()); @@ -6249,8 +6247,9 @@ // should scroll the child. scroll_delta = gfx::Vector2d(0, 70); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::NON_BUBBLING_GESTURE) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), + InputHandler::NON_BUBBLING_GESTURE) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer); scroll_result = @@ -6275,8 +6274,9 @@ { gfx::Vector2d scroll_delta(0, 8); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::WHEEL) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), + InputHandler::WHEEL) .thread); scroll_result = host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); @@ -6315,10 +6315,11 @@ EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll()); // Even though the layer can't scroll the overscroll still happens. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); scroll_result = host_impl_->ScrollBy( UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); EXPECT_FALSE(scroll_result.did_scroll); @@ -6343,8 +6344,9 @@ // of the content. unnecessary glow effect calls shouldn't be // called while scrolling up without reaching the edge of the content. EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), - InputHandler::WHEEL) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), + InputHandler::WHEEL) .thread); scroll_result = host_impl_->ScrollBy( UpdateState(gfx::Point(), gfx::Vector2dF(0, 100)).get()); @@ -6362,8 +6364,9 @@ // unusedrootDelta should be subtracted from applied delta so that // unwanted glow effect calls are not called. EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), - InputHandler::NON_BUBBLING_GESTURE) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), + InputHandler::NON_BUBBLING_GESTURE) .thread); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_->FlingScrollBegin().thread); @@ -6384,8 +6387,9 @@ // TestCase to check kEpsilon, which prevents minute values to trigger // gloweffect without reaching edge. EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), - InputHandler::WHEEL) + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), + InputHandler::WHEEL) .thread); scroll_result = host_impl_->ScrollBy( UpdateState(gfx::Point(), gfx::Vector2dF(-0.12f, 0.1f)).get()); @@ -6898,15 +6902,10 @@ TileDrawQuad* test_blending_draw_quad = render_pass->CreateAndAppendDrawQuad<TileDrawQuad>(); - test_blending_draw_quad->SetNew(shared_quad_state, - quad_rect_, - opaque_rect, - visible_quad_rect, - resource_id_, + test_blending_draw_quad->SetNew(shared_quad_state, quad_rect_, opaque_rect, + visible_quad_rect, resource_id_, gfx::RectF(0.f, 0.f, 1.f, 1.f), - gfx::Size(1, 1), - false, - false); + gfx::Size(1, 1), false, false); test_blending_draw_quad->visible_rect = quad_visible_rect_; EXPECT_EQ(blend_, test_blending_draw_quad->ShouldDrawWithBlending()); EXPECT_EQ(has_render_surface_, !!render_surface()); @@ -7254,10 +7253,10 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { protected: - LayerTreeHostImplViewportCoveredTest() : - gutter_quad_material_(DrawQuad::SOLID_COLOR), - child_(NULL), - did_activate_pending_tree_(false) {} + LayerTreeHostImplViewportCoveredTest() + : gutter_quad_material_(DrawQuad::SOLID_COLOR), + child_(NULL), + did_activate_pending_tree_(false) {} std::unique_ptr<CompositorFrameSink> CreateFakeCompositorFrameSink( bool software) { @@ -7548,11 +7547,12 @@ EXPECT_SCOPED(TestLayerIsLargerThanViewportWithOnDraw()); } -class FakeDrawableLayerImpl: public LayerImpl { +class FakeDrawableLayerImpl : public LayerImpl { public: static std::unique_ptr<LayerImpl> Create(LayerTreeImpl* tree_impl, int id) { return base::WrapUnique(new FakeDrawableLayerImpl(tree_impl, id)); } + protected: FakeDrawableLayerImpl(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id) {} @@ -7583,7 +7583,7 @@ layer_tree_host_impl->SetVisible(true); layer_tree_host_impl->InitializeRenderer(compositor_frame_sink.get()); layer_tree_host_impl->WillBeginImplFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE)); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2)); layer_tree_host_impl->SetViewportSize(gfx::Size(500, 500)); std::unique_ptr<LayerImpl> root = @@ -7687,8 +7687,8 @@ gfx::Rect visible_quad_rect(quad_rect); SolidColorDrawQuad* my_quad = render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); - my_quad->SetNew( - shared_quad_state, quad_rect, visible_quad_rect, gray, false); + my_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, gray, + false); } private: @@ -7712,7 +7712,7 @@ my_host_impl->SetVisible(true); my_host_impl->InitializeRenderer(compositor_frame_sink); my_host_impl->WillBeginImplFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE)); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2)); my_host_impl->SetViewportSize(gfx::Size(100, 100)); /* @@ -7841,9 +7841,8 @@ root_layer->SetBounds(gfx::Size(10, 10)); root_layer->test_properties()->force_render_surface = true; - scoped_refptr<VideoFrame> softwareFrame = - media::VideoFrame::CreateColorFrame( - gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta()); + scoped_refptr<VideoFrame> softwareFrame = media::VideoFrame::CreateColorFrame( + gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta()); FakeVideoFrameProvider provider; provider.set_frame(softwareFrame); std::unique_ptr<VideoLayerImpl> video_layer = VideoLayerImpl::Create( @@ -7873,10 +7872,8 @@ class MockDrawQuadsToFillScreenContext : public TestWebGraphicsContext3D { public: MOCK_METHOD1(useProgram, void(GLuint program)); - MOCK_METHOD4(drawElements, void(GLenum mode, - GLsizei count, - GLenum type, - GLintptr offset)); + MOCK_METHOD4(drawElements, + void(GLenum mode, GLsizei count, GLenum type, GLintptr offset)); }; TEST_F(LayerTreeHostImplTest, HasTransparentBackground) { @@ -8612,10 +8609,11 @@ host_impl_->SetViewportSize(surface_size); DrawFrame(); { - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_->FlingScrollBegin().thread); @@ -8735,10 +8733,11 @@ // We should have scrolled |child_scroll| even though it does not move // any layer that is a drawn RSLL member. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); EXPECT_EQ(7, host_impl_->CurrentlyScrollingLayer()->id()); } @@ -8761,8 +8760,8 @@ host_impl_->compositor_frame_sink()); ui::LatencyInfo latency_info; - latency_info.AddLatencyNumber( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0, 0); + latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0, + 0); std::unique_ptr<SwapPromise> swap_promise( new LatencyInfoSwapPromise(latency_info)); host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise)); @@ -8925,8 +8924,8 @@ InputHandler::TOUCHSCREEN) .thread); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()) .did_scroll); host_impl_->ScrollEnd(EndState().get()); @@ -8943,8 +8942,8 @@ InputHandler::TOUCHSCREEN) .thread); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()) .did_scroll); host_impl_->ScrollEnd(EndState().get()); @@ -8977,7 +8976,7 @@ ->scroll_tree.UpdateScrollOffsetBaseForTesting(scroll_layer->id(), gfx::ScrollOffset(0, 10)); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_FALSE(did_request_redraw_); @@ -9110,10 +9109,11 @@ LayerImpl* viewport_layer = host_impl_->InnerViewportScrollLayer(); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(), viewport_layer->CurrentScrollOffset()); @@ -9121,8 +9121,8 @@ // directly through to the viewport. const float delta = top_controls_height_; EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, delta)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, delta)).get()) .did_scroll); EXPECT_FLOAT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); @@ -9130,8 +9130,8 @@ viewport_layer->CurrentScrollOffset()); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, delta)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, delta)).get()) .did_scroll); EXPECT_FLOAT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); @@ -9162,8 +9162,8 @@ const float residue = 35; float offset = top_controls_height_ - residue; EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) .did_scroll); EXPECT_FLOAT_EQ(-offset, host_impl_->browser_controls_manager()->ControlsTopOffset()); @@ -9184,7 +9184,7 @@ // The browser controls should properly animate until finished, despite the // scroll offset being at the origin. BeginFrameArgs begin_frame_args = CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, base::TimeTicks::Now()); + BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now()); while (did_request_next_frame_) { did_request_redraw_ = false; did_request_next_frame_ = false; @@ -9194,6 +9194,7 @@ host_impl_->browser_controls_manager()->ControlsTopOffset(); begin_frame_args.frame_time += base::TimeDelta::FromMilliseconds(5); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); EXPECT_EQ(gfx::Vector2dF().ToString(), @@ -9246,8 +9247,8 @@ const float residue = 15; float offset = top_controls_height_ - residue; EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) .did_scroll); EXPECT_FLOAT_EQ(-offset, host_impl_->browser_controls_manager()->ControlsTopOffset()); @@ -9267,7 +9268,7 @@ // Animate the browser controls to the limit. BeginFrameArgs begin_frame_args = CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, base::TimeTicks::Now()); + BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now()); while (did_request_next_frame_) { did_request_redraw_ = false; did_request_next_frame_ = false; @@ -9277,6 +9278,7 @@ host_impl_->browser_controls_manager()->ControlsTopOffset(); begin_frame_args.frame_time += base::TimeDelta::FromMilliseconds(5); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); @@ -9322,8 +9324,8 @@ const float residue = 15; float offset = top_controls_height_ - residue; EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) .did_scroll); EXPECT_FLOAT_EQ(-offset, host_impl_->browser_controls_manager()->ControlsTopOffset()); @@ -9343,7 +9345,7 @@ // Animate the browser controls to the limit. BeginFrameArgs begin_frame_args = CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, base::TimeTicks::Now()); + BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now()); while (did_request_next_frame_) { did_request_redraw_ = false; did_request_next_frame_ = false; @@ -9353,6 +9355,7 @@ host_impl_->browser_controls_manager()->ControlsTopOffset(); begin_frame_args.frame_time += base::TimeDelta::FromMilliseconds(5); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); @@ -9391,8 +9394,8 @@ float offset = 50; EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) .did_scroll); EXPECT_EQ(-offset, host_impl_->browser_controls_manager()->ControlsTopOffset()); @@ -9400,15 +9403,15 @@ scroll_layer->CurrentScrollOffset().ToString()); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) .did_scroll); EXPECT_EQ(gfx::Vector2dF(0, offset).ToString(), scroll_layer->CurrentScrollOffset().ToString()); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) .did_scroll); // Should have fully scrolled @@ -9418,28 +9421,29 @@ float overscrollamount = 10; // Overscroll the content - EXPECT_FALSE( - host_impl_->ScrollBy(UpdateState(gfx::Point(), - gfx::Vector2d(0, overscrollamount)) - .get()) - .did_scroll); + EXPECT_FALSE(host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), + gfx::Vector2d(0, overscrollamount)) + .get()) + .did_scroll); EXPECT_EQ(gfx::Vector2dF(0, 2 * offset).ToString(), scroll_layer->CurrentScrollOffset().ToString()); EXPECT_EQ(gfx::Vector2dF(0, overscrollamount).ToString(), host_impl_->accumulated_root_overscroll().ToString()); - EXPECT_TRUE(host_impl_->ScrollBy(UpdateState(gfx::Point(), - gfx::Vector2d(0, -2 * offset)) - .get()) - .did_scroll); + EXPECT_TRUE( + host_impl_ + ->ScrollBy( + UpdateState(gfx::Point(), gfx::Vector2d(0, -2 * offset)).get()) + .did_scroll); EXPECT_EQ(gfx::Vector2dF(0, 0).ToString(), scroll_layer->CurrentScrollOffset().ToString()); EXPECT_EQ(-offset, host_impl_->browser_controls_manager()->ControlsTopOffset()); EXPECT_TRUE( - host_impl_->ScrollBy( - UpdateState(gfx::Point(), gfx::Vector2d(0, -offset)).get()) + host_impl_ + ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, -offset)).get()) .did_scroll); EXPECT_EQ(gfx::Vector2dF(0, 0).ToString(), scroll_layer->CurrentScrollOffset().ToString()); @@ -10290,7 +10294,7 @@ base::TimeTicks() + base::TimeDelta::FromMilliseconds(100); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, @@ -10300,6 +10304,7 @@ EXPECT_EQ(host_impl_->OuterViewportScrollLayer(), scrolling_layer); begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10309,6 +10314,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(50); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10324,6 +10330,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(200); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10335,6 +10342,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(250); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10371,8 +10379,9 @@ base::TimeTicks() + base::TimeDelta::FromMilliseconds(200); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->UpdateAnimationState(true); host_impl_->DidFinishImplFrame(); @@ -10389,6 +10398,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(250); + begin_frame_args.sequence_number++; // This is when the animation above gets promoted to STARTING. host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->UpdateAnimationState(true); @@ -10396,6 +10406,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(300); + begin_frame_args.sequence_number++; // This is when the animation above gets ticked. host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->UpdateAnimationState(true); @@ -10419,7 +10430,7 @@ base::TimeTicks start_time = base::TimeTicks() + base::TimeDelta::FromMilliseconds(100); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); // Create animation with a 100ms delay. EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, @@ -10432,6 +10443,7 @@ // First tick, animation is started. begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->UpdateAnimationState(true); EXPECT_EQ(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); @@ -10441,6 +10453,7 @@ // the duration due to delay is 100ms. begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(50); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->UpdateAnimationState(true); EXPECT_EQ(50, scrolling_layer->CurrentScrollOffset().y()); @@ -10457,6 +10470,7 @@ // target was called with a large value of jank. begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(100); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->UpdateAnimationState(true); EXPECT_LT(100, scrolling_layer->CurrentScrollOffset().y()); @@ -10475,7 +10489,7 @@ base::TimeTicks() + base::TimeDelta::FromMilliseconds(100); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); // Perform animated scroll. EXPECT_EQ( @@ -10485,6 +10499,7 @@ LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer(); begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10497,6 +10512,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(50); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10505,10 +10521,11 @@ EXPECT_TRUE(y > 1 && y < 49); // Perform instant scroll. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point(0, y)).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point(0, y)).get(), InputHandler::WHEEL) + .thread); EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y), InputHandler::WHEEL)); host_impl_->ScrollBy( @@ -10546,7 +10563,7 @@ base::TimeTicks() + base::TimeDelta::FromMilliseconds(100); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); // Perform animated scroll. EXPECT_EQ( @@ -10556,6 +10573,7 @@ LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer(); begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10568,6 +10586,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(50); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10600,7 +10619,7 @@ base::TimeTicks() + base::TimeDelta::FromMilliseconds(100); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, @@ -10608,6 +10627,7 @@ LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer(); begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10617,6 +10637,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(50); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10632,6 +10653,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(200); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10643,6 +10665,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(250); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10676,7 +10699,7 @@ base::TimeTicks start_time = base::TimeTicks() + base::TimeDelta::FromMilliseconds(250); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(10.f, 20.f)) .thread); @@ -10684,6 +10707,7 @@ host_impl_->UpdateAnimationState(true); EXPECT_EQ(inner_scroll_layer, host_impl_->CurrentlyScrollingLayer()); + begin_frame_args.sequence_number++; BeginImplFrameAndAnimate(begin_frame_args, start_time); EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 10), inner_scroll_layer->CurrentScrollOffset()); @@ -10701,6 +10725,7 @@ host_impl_->UpdateAnimationState(true); EXPECT_EQ(inner_scroll_layer, host_impl_->CurrentlyScrollingLayer()); + begin_frame_args.sequence_number++; BeginImplFrameAndAnimate(begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(350)); EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), @@ -10718,6 +10743,7 @@ host_impl_->UpdateAnimationState(true); EXPECT_EQ(outer_scroll_layer, host_impl_->CurrentlyScrollingLayer()); + begin_frame_args.sequence_number++; BeginImplFrameAndAnimate(begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(850)); EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), @@ -10735,6 +10761,7 @@ host_impl_->UpdateAnimationState(true); EXPECT_EQ(inner_scroll_layer, host_impl_->CurrentlyScrollingLayer()); + begin_frame_args.sequence_number++; BeginImplFrameAndAnimate( begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1200)); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), @@ -10764,7 +10791,7 @@ base::TimeTicks start_time = base::TimeTicks() + base::TimeDelta::FromMilliseconds(50); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(90, 90)).thread); @@ -10772,6 +10799,7 @@ host_impl_->UpdateAnimationState(true); EXPECT_EQ(inner_scroll_layer, host_impl_->CurrentlyScrollingLayer()); + begin_frame_args.sequence_number++; BeginImplFrameAndAnimate(begin_frame_args, start_time); float inner_x = inner_scroll_layer->CurrentScrollOffset().x(); float inner_y = inner_scroll_layer->CurrentScrollOffset().y(); @@ -10790,6 +10818,7 @@ // Verify that all the delta is applied to the inner viewport and nothing is // carried forward. + begin_frame_args.sequence_number++; BeginImplFrameAndAnimate(begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(350)); EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), @@ -10816,7 +10845,7 @@ base::TimeTicks() + base::TimeDelta::FromMilliseconds(100); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, @@ -10824,6 +10853,7 @@ LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer(); begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10833,6 +10863,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(50); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10850,6 +10881,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(200); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10861,6 +10893,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(250); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10885,12 +10918,13 @@ base::TimeTicks start_time = base::TimeTicks() + base::TimeDelta::FromMilliseconds(100); BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(500, 500)); LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer(); begin_frame_args.frame_time = start_time; + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -10904,6 +10938,7 @@ begin_frame_args.frame_time = start_time + base::TimeDelta::FromMilliseconds(200); + begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->Animate(); host_impl_->UpdateAnimationState(true); @@ -11016,10 +11051,11 @@ host_impl_->ScrollEnd(EndState().get()); gfx::Vector2dF scroll_delta(0, 5); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL) - .thread); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) + .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset()); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); @@ -11193,7 +11229,7 @@ TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerInsideFrame) { BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); FakeVideoFrameController controller; host_impl_->WillBeginImplFrame(begin_frame_args); @@ -11216,7 +11252,7 @@ TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerOutsideFrame) { BeginFrameArgs begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); FakeVideoFrameController controller; host_impl_->WillBeginImplFrame(begin_frame_args); @@ -11226,7 +11262,7 @@ host_impl_->AddVideoFrameController(&controller); EXPECT_FALSE(controller.begin_frame_args().IsValid()); - begin_frame_args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); + begin_frame_args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3); EXPECT_FALSE(controller.begin_frame_args().IsValid()); host_impl_->WillBeginImplFrame(begin_frame_args); EXPECT_TRUE(controller.begin_frame_args().IsValid());
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 593a7dc0..f30413a 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc
@@ -450,7 +450,7 @@ } BeginFrameArgs begin_frame_args(BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, frame_begin_time, base::TimeTicks(), + BEGINFRAME_FROM_HERE, 0, 1, frame_begin_time, base::TimeTicks(), BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL)); // Start the impl frame.
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni index 09b345ff..6646b59 100644 --- a/chrome/android/chrome_public_apk_tmpl.gni +++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -104,6 +104,7 @@ # Configrations to make android load shared library from APK. uncompress_shared_libraries = true + page_align_shared_libraries = true forward_variables_from(invoker, "*")
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java index 3f00b3a..30ad2e99 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -74,6 +74,7 @@ import org.chromium.chrome.browser.dom_distiller.ReaderModeManager; import org.chromium.chrome.browser.download.DownloadManagerService; import org.chromium.chrome.browser.download.DownloadUtils; +import org.chromium.chrome.browser.firstrun.ForcedSigninProcessor; import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; import org.chromium.chrome.browser.gsa.ContextReporter; import org.chromium.chrome.browser.gsa.GSAAccountChangeListener; @@ -845,6 +846,14 @@ } } }); + + DeferredStartupHandler.getInstance().addDeferredTask(new Runnable() { + @Override + public void run() { + if (isActivityDestroyed()) return; + ForcedSigninProcessor.checkCanSignIn(ChromeActivity.this); + } + }); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java index ba1c4ceb..55e5a68 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java
@@ -127,18 +127,28 @@ */ private final Activity mActivity; + /** + * The modal dialog that is shown to the user. + */ private Dialog mDialog; /** + * Whether the dialog can be canceled by the user. + */ + private final boolean mCancelable; + + /** * Create a new Modal Dialog handler for the specified activity and error code. The * specified activity may be used to launch the dialog via * {@link Activity#startActivityForResult(android.content.Intent, int)} and also to receive * the result via Activity's protected onActivityResult method. * * @param activity the activity to use + * @param cancelable whether the dialog can be canceled by the user */ - public ModalDialog(Activity activity) { + public ModalDialog(Activity activity, boolean cancelable) { mActivity = activity; + mCancelable = cancelable; } /** @@ -151,13 +161,17 @@ protected final void handle(final Context context, final int errorCode) { mDialog = GoogleApiAvailability.getInstance().getErrorDialog( mActivity, errorCode, NO_RESPONSE_REQUIRED); + // This can happen if |errorCode| is ConnectionResult.SERVICE_INVALID. if (mDialog != null) { - // This can happen if |errorCode| is ConnectionResult.SERVICE_INVALID. + mDialog.setCancelable(mCancelable); mDialog.show(); } sErrorHandlerActionHistogramSample.record(ERROR_HANDLER_ACTION_MODAL_DIALOG); } + /** + * Cancels the dialog. + */ public void cancelDialog() { if (mDialog != null) { mDialog.cancel();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java index 286fb2d..82ac3ab9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
@@ -9,6 +9,9 @@ import org.chromium.base.Callback; import org.chromium.base.Log; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; +import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler; import org.chromium.chrome.browser.services.AndroidEduAndChildAccountHelper; import org.chromium.chrome.browser.signin.AccountManagementFragment; import org.chromium.chrome.browser.signin.SigninManager; @@ -90,4 +93,19 @@ } }); } + + /** + * If forced signin is required by policy, check that Google Play Services is available, and + * show a non-cancelable dialog otherwise. + * @param activity The activity for which to show the dialog. + */ + // TODO(bauerb): Once external dependencies reliably use policy to force sign-in, + // consider removing the child account / EDU checks. + public static void checkCanSignIn(final ChromeActivity activity) { + final Context appContext = activity.getApplicationContext(); + if (SigninManager.get(appContext).isForceSigninEnabled()) { + ExternalAuthUtils.getInstance().canUseGooglePlayServices(appContext, + new UserRecoverableErrorHandler.ModalDialog(activity, false)); + } + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java index 3eb3a43..d313592d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
@@ -185,6 +185,31 @@ mPublisherBar.setLayoutParams(params); } + private static String getAttributionString(SnippetArticle article) { + if (article.mPublishTimestampMilliseconds == 0) return article.mPublisher; + + // DateUtils.getRelativeTimeSpanString(...) calls through to TimeZone.getDefault(). If this + // has never been called before it loads the current time zone from disk. In most likelihood + // this will have been called previously and the current time zone will have been cached, + // but in some cases (eg instrumentation tests) it will cause a strict mode violation. + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + CharSequence relativeTimeSpan; + try { + long time = SystemClock.elapsedRealtime(); + relativeTimeSpan = + DateUtils.getRelativeTimeSpanString(article.mPublishTimestampMilliseconds, + System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS); + RecordHistogram.recordTimesHistogram("Android.StrictMode.SnippetUIBuildTime", + SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } + // We format the publisher here so that having a publisher name in an RTL language + // doesn't mess up the formatting on an LTR device and vice versa. + return String.format(PUBLISHER_FORMAT_STRING, + BidiFormatter.getInstance().unicodeWrap(article.mPublisher), relativeTimeSpan); + } + public void onBindViewHolder(SnippetArticle article, SuggestionsCategoryInfo categoryInfo) { super.onBindViewHolder(); @@ -196,28 +221,7 @@ updateLayout(); mHeadlineTextView.setText(mArticle.mTitle); - - // DateUtils.getRelativeTimeSpanString(...) calls through to TimeZone.getDefault(). If this - // has never been called before it loads the current time zone from disk. In most likelihood - // this will have been called previously and the current time zone will have been cached, - // but in some cases (eg instrumentation tests) it will cause a strict mode violation. - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - try { - long time = SystemClock.elapsedRealtime(); - CharSequence relativeTimeSpan = DateUtils.getRelativeTimeSpanString( - mArticle.mPublishTimestampMilliseconds, System.currentTimeMillis(), - DateUtils.MINUTE_IN_MILLIS); - RecordHistogram.recordTimesHistogram("Android.StrictMode.SnippetUIBuildTime", - SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS); - - // We format the publisher here so that having a publisher name in an RTL language - // doesn't mess up the formatting on an LTR device and vice versa. - String publisherAttribution = String.format(PUBLISHER_FORMAT_STRING, - BidiFormatter.getInstance().unicodeWrap(mArticle.mPublisher), relativeTimeSpan); - mPublisherTextView.setText(publisherAttribution); - } finally { - StrictMode.setThreadPolicy(oldPolicy); - } + mPublisherTextView.setText(getAttributionString(mArticle)); // The favicon of the publisher should match the TextView height. int widthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java index d3b204ba..cd62bc1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
@@ -306,8 +306,9 @@ private boolean checkGooglePlayServicesAvailable() { ExternalAuthUtils extAuthUtils = ExternalAuthUtils.getInstance(); + boolean cancelable = !SigninManager.get(getContext()).isForceSigninEnabled(); mGooglePlayServicesUpdateErrorHandler = new UserRecoverableErrorHandler.ModalDialog( - mDelegate.getActivity()); + mDelegate.getActivity(), cancelable); int resultCode = extAuthUtils.canUseGooglePlayServicesResultCode( getContext(), mGooglePlayServicesUpdateErrorHandler); if (extAuthUtils.isGooglePlayServicesUpdateRequiredError(resultCode)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java index 134b7505..15d00d2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -257,6 +257,13 @@ } /** + * @return Whether force sign-in is enabled by policy. + */ + public boolean isForceSigninEnabled() { + return nativeIsForceSigninEnabled(mNativeSigninManagerAndroid); + } + + /** * Registers a SignInStateObserver to be notified when the user signs in or out of Chrome. */ public void addSignInStateObserver(SignInStateObserver observer) { @@ -376,7 +383,7 @@ } else { Activity activity = mSignInState.activity; UserRecoverableErrorHandler errorHandler = activity != null - ? new UserRecoverableErrorHandler.ModalDialog(activity) + ? new UserRecoverableErrorHandler.ModalDialog(activity, !isForceSigninEnabled()) : new UserRecoverableErrorHandler.SystemNotification(); ExternalAuthUtils.getInstance().canUseGooglePlayServices(mContext, errorHandler); Log.w(TAG, "Cancelling the sign-in process as Google Play services is unavailable"); @@ -666,6 +673,7 @@ private static native void nativeIsUserManaged(String username, Callback<Boolean> callback); private native long nativeInit(); private native boolean nativeIsSigninAllowedByPolicy(long nativeSigninManagerAndroid); + private native boolean nativeIsForceSigninEnabled(long nativeSigninManagerAndroid); private native void nativeCheckPolicyBeforeSignIn( long nativeSigninManagerAndroid, String username); private native void nativeFetchPolicyBeforeSignIn(long nativeSigninManagerAndroid);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java index e9efda1..a39909f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -115,13 +115,14 @@ recordUpdate(storage, false); if (fetchedInfo != null) { - updateAsync(fetchedInfo, bestIconUrl); + updateAsync(fetchedInfo, bestIconUrl, false /* isManifestStale */); return; } - // Since we could not fetch the Web Manifest, we do not know what the best icon URL is. Pass - // an empty "best icon URL" to tell the server that there is no best icon URL. - updateAsync(mInfo, ""); + // Tell the server that the our version of the Web Manifest might be stale and to ignore + // our Web Manifest data if the server's Web Manifest data is newer. This scenario can + // occur if the Web Manifest is temporarily unreachable. + updateAsync(mInfo, "", true /* isManifestStale */); } /** @@ -134,14 +135,22 @@ /** * Sends request to WebAPK Server to update WebAPK. */ - protected void updateAsync(WebApkInfo info, String bestIconUrl) { + protected void updateAsync(WebApkInfo info, String bestIconUrl, boolean isManifestStale) { int versionCode = readVersionCodeFromAndroidManifest(info.webApkPackageName()); - String bestIconMurmur2Hash = info.iconUrlToMurmur2HashMap().get(bestIconUrl); + int size = info.iconUrlToMurmur2HashMap().size(); + String[] iconUrls = new String[size]; + String[] iconHashes = new String[size]; + int i = 0; + for (Map.Entry<String, String> entry : info.iconUrlToMurmur2HashMap().entrySet()) { + iconUrls[i] = entry.getKey(); + String iconHash = entry.getValue(); + iconHashes[i] = iconHash != null ? iconHash : ""; + i++; + } nativeUpdateAsync(info.id(), info.manifestStartUrl(), info.scopeUri().toString(), - info.name(), info.shortName(), bestIconUrl, bestIconMurmur2Hash, info.icon(), - info.iconUrlToMurmur2HashMap().keySet().toArray(new String[0]), info.displayMode(), - info.orientation(), info.themeColor(), info.backgroundColor(), info.manifestUrl(), - info.webApkPackageName(), versionCode); + info.name(), info.shortName(), bestIconUrl, info.icon(), iconUrls, iconHashes, + info.displayMode(), info.orientation(), info.themeColor(), info.backgroundColor(), + info.manifestUrl(), info.webApkPackageName(), versionCode, isManifestStale); } /** @@ -309,7 +318,8 @@ } private static native void nativeUpdateAsync(String id, String startUrl, String scope, - String name, String shortName, String bestIconUrl, String bestIconMurmur2Hash, - Bitmap bestIcon, String[] iconUrls, int displayMode, int orientation, long themeColor, - long backgroundColor, String manifestUrl, String webApkPackage, int webApkVersion); + String name, String shortName, String bestIconUrl, Bitmap bestIcon, String[] iconUrls, + String[] iconHashes, int displayMode, int orientation, long themeColor, + long backgroundColor, String manifestUrl, String webApkPackage, int webApkVersion, + boolean isManifestStale); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java index 94e0b1c6..69fd24ae 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -75,7 +75,8 @@ } @Override - protected void updateAsync(WebApkInfo fetchedInfo, String bestIconUrl) { + protected void updateAsync(WebApkInfo fetchedInfo, String bestIconUrl, + boolean isManifestStale) { mNeedsUpdate = true; }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java index bca36a9..38d8dd5 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -138,7 +138,7 @@ } @Override - protected void updateAsync(WebApkInfo info, String bestIconUrl) { + protected void updateAsync(WebApkInfo info, String bestIconUrl, boolean isManifestStale) { mUpdateRequested = true; mUpdateName = info.name(); }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index 4e3e5b1..149ac6a5 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -2465,6 +2465,15 @@ <message name="IDS_OPTIONS_DEVICE_GROUP_STYLUS_SETTINGS_BUTTON" desc="Text on button/link to take user to the 'Stylus' overlay."> Stylus settings </message> + <message name="IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_LABEL" desc="Label for dropdown menu in 'Stylus' overlay that lets the user set a preferred note-taking app."> + Note-taking app: + </message> + <message name="IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_NONE_AVAILABLE" desc="Entry in 'Stylus' overlay's note-taking app menu when no apps are available."> + None available + </message> + <message name="IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_WAITING_FOR_ANDROID" desc="Entry in 'Stylus' overlay's note-taking app menu when Android isn't ready yet."> + Waiting for Android apps... + </message> <message name="IDS_OPTIONS_SETTINGS_SET_TIME_BUTTON" desc="In the settings tab, the prompt on the button to open a dialog to set date and time."> Set date and time </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 84b8c583..37486eba 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -1937,8 +1937,8 @@ Copy to clipboard </message> <message name="IDS_DOWNLOAD_NOTIFICATION_ANNOTATE" - desc="In the download notification, the text of the button that opens the file (if it is an image) in the Google Keep app so the user can annotate it"> - Annotate in Google Keep + desc="In the download notification, the text of the button that opens the file (if it is an image) in the note-taking app so the user can annotate it"> + Annotate image </message> <!-- Download Context Menu Items -->
diff --git a/chrome/browser/android/signin/signin_manager_android.cc b/chrome/browser/android/signin/signin_manager_android.cc index 757cbf78a..4b30b5d 100644 --- a/chrome/browser/android/signin/signin_manager_android.cc +++ b/chrome/browser/android/signin/signin_manager_android.cc
@@ -29,6 +29,7 @@ #include "chrome/browser/signin/oauth2_token_service_delegate_android.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" +#include "chrome/common/pref_names.h" #include "components/bookmarks/browser/bookmark_model.h" #include "components/policy/core/browser/browser_policy_connector.h" #include "components/policy/core/common/cloud/cloud_policy_core.h" @@ -269,6 +270,14 @@ return SigninManagerFactory::GetForProfile(profile_)->IsSigninAllowed(); } +jboolean SigninManagerAndroid::IsForceSigninEnabled( + JNIEnv* env, + const JavaParamRef<jobject>& obj) { + // prefs::kForceBrowserSignin is set in Local State, not in user prefs. + PrefService* prefs = g_browser_process->local_state(); + return prefs->GetBoolean(prefs::kForceBrowserSignin); +} + jboolean SigninManagerAndroid::IsSignedInOnNative( JNIEnv* env, const JavaParamRef<jobject>& obj) {
diff --git a/chrome/browser/android/signin/signin_manager_android.h b/chrome/browser/android/signin/signin_manager_android.h index b618efc..ea7f1ecd 100644 --- a/chrome/browser/android/signin/signin_manager_android.h +++ b/chrome/browser/android/signin/signin_manager_android.h
@@ -71,6 +71,10 @@ JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); + jboolean IsForceSigninEnabled( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj); + jboolean IsSignedInOnNative(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
diff --git a/chrome/browser/android/webapk/webapk.proto b/chrome/browser/android/webapk/webapk.proto index 90b171e..05615266 100644 --- a/chrome/browser/android/webapk/webapk.proto +++ b/chrome/browser/android/webapk/webapk.proto
@@ -48,6 +48,10 @@ // The cpu abi of the browser making the request. optional string android_abi = 8; + // If set true, this flag indicates that the Web App Manifest of the site is + // no longer available. + optional bool stale_manifest = 9; + reserved 4; }
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc index 840e1147..a34ddd3 100644 --- a/chrome/browser/android/webapk/webapk_installer.cc +++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -111,7 +111,8 @@ std::unique_ptr<webapk::WebApk> BuildWebApkProtoInBackground( const ShortcutInfo& shortcut_info, const SkBitmap& shortcut_icon, - const std::string& shortcut_icon_murmur2_hash) { + const std::map<std::string, std::string>& icon_url_to_murmur2_hash, + bool is_manifest_stale) { DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); std::unique_ptr<webapk::WebApk> webapk(new webapk::WebApk); @@ -120,11 +121,7 @@ base::android::BuildInfo::GetInstance()->package_name()); webapk->set_requester_application_version(version_info::GetVersionNumber()); webapk->set_android_abi(getCurrentAbi()); - - // TODO(hanxi): crbug.com/665078. Add a flag in WebAPK's proto to indicate - // that the Web Manifest data in the proto might be stale. - if (shortcut_icon_murmur2_hash.empty()) - return webapk; + webapk->set_stale_manifest(is_manifest_stale); webapk::WebAppManifest* web_app_manifest = webapk->mutable_manifest(); web_app_manifest->set_name(base::UTF16ToUTF8(shortcut_info.name)); @@ -144,22 +141,33 @@ scope->assign(GetScope(shortcut_info).spec()); webapk::Image* best_image = web_app_manifest->add_icons(); - best_image->set_src(shortcut_info.best_icon_url.spec()); - best_image->set_hash(shortcut_icon_murmur2_hash); + std::string best_icon_url = shortcut_info.best_icon_url.spec(); + best_image->set_src(best_icon_url); + auto it = icon_url_to_murmur2_hash.find(best_icon_url); + if (it != icon_url_to_murmur2_hash.end()) + best_image->set_hash(it->second); std::vector<unsigned char> png_bytes; gfx::PNGCodec::EncodeBGRASkBitmap(shortcut_icon, false, &png_bytes); best_image->set_image_data(&png_bytes.front(), png_bytes.size()); - for (const std::string& icon_url : shortcut_info.icon_urls) { - if (icon_url == shortcut_info.best_icon_url.spec()) + for (const auto& entry : icon_url_to_murmur2_hash) { + if (entry.first == shortcut_info.best_icon_url.spec()) continue; webapk::Image* image = web_app_manifest->add_icons(); - image->set_src(icon_url); + image->set_src(entry.first); + image->set_hash(entry.second); } return webapk; } +// Calls the callback when the |webapk| request is created. +void OnWebApkProtoBuilt( + const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback, + std::unique_ptr<webapk::WebApk> webapk) { + callback.Run(std::move(webapk)); +} + // Returns task runner for running background tasks. scoped_refptr<base::TaskRunner> GetBackgroundTaskRunner() { return content::BrowserThread::GetBlockingPool() @@ -250,33 +258,36 @@ DownloadAppIconAndComputeMurmur2Hash(); } -void WebApkInstaller::UpdateAsync(content::BrowserContext* browser_context, - const FinishCallback& finish_callback, - const std::string& icon_murmur2_hash, - const std::string& webapk_package, - int webapk_version) { +void WebApkInstaller::UpdateAsync( + content::BrowserContext* browser_context, + const FinishCallback& finish_callback, + const std::string& webapk_package, + int webapk_version, + const std::map<std::string, std::string>& icon_url_to_murmur2_hash, + bool is_manifest_stale) { UpdateAsyncWithURLRequestContextGetter( Profile::FromBrowserContext(browser_context)->GetRequestContext(), - finish_callback, icon_murmur2_hash, webapk_package, webapk_version); + finish_callback, webapk_package, webapk_version, + icon_url_to_murmur2_hash, is_manifest_stale); } void WebApkInstaller::UpdateAsyncWithURLRequestContextGetter( net::URLRequestContextGetter* request_context_getter, const FinishCallback& finish_callback, - const std::string& icon_murmur2_hash, const std::string& webapk_package, - int webapk_version) { + int webapk_version, + const std::map<std::string, std::string>& icon_url_to_murmur2_hash, + bool is_manifest_stale) { request_context_getter_ = request_context_getter; finish_callback_ = finish_callback; - shortcut_icon_murmur2_hash_ = icon_murmur2_hash; webapk_package_ = webapk_package; webapk_version_ = webapk_version; task_type_ = UPDATE; base::PostTaskAndReplyWithResult( GetBackgroundTaskRunner().get(), FROM_HERE, - base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, - shortcut_icon_, shortcut_icon_murmur2_hash_), + base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_, + icon_url_to_murmur2_hash, is_manifest_stale), base::Bind(&WebApkInstaller::SendUpdateWebApkRequest, weak_ptr_factory_.GetWeakPtr())); } @@ -296,6 +307,17 @@ OnFailure(); } +void WebApkInstaller::BuildWebApkProtoInBackgroundForTesting( + const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback, + const std::map<std::string, std::string>& icon_url_to_murmur2_hash, + bool is_manifest_stale) { + base::PostTaskAndReplyWithResult( + GetBackgroundTaskRunner().get(), FROM_HERE, + base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_, + icon_url_to_murmur2_hash, is_manifest_stale), + base::Bind(&OnWebApkProtoBuilt, callback)); +} + // static bool WebApkInstaller::Register(JNIEnv* env) { return RegisterNativesImpl(env); @@ -410,18 +432,24 @@ timer_.Stop(); icon_hasher_.reset(); - shortcut_icon_murmur2_hash_ = icon_murmur2_hash; - // An empty hash indicates that |icon_hasher_| encountered an error. if (icon_murmur2_hash.empty()) { OnFailure(); return; } + std::map<std::string, std::string> icon_url_to_murmur2_hash; + for (const std::string& icon_url : shortcut_info_.icon_urls) { + if (icon_url != shortcut_info_.best_icon_url.spec()) + icon_url_to_murmur2_hash[icon_url] = ""; + else + icon_url_to_murmur2_hash[icon_url] = icon_murmur2_hash; + } + base::PostTaskAndReplyWithResult( GetBackgroundTaskRunner().get(), FROM_HERE, - base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, - shortcut_icon_, shortcut_icon_murmur2_hash_), + base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_, + icon_url_to_murmur2_hash, false /* is_manifest_stale */), base::Bind(&WebApkInstaller::SendCreateWebApkRequest, weak_ptr_factory_.GetWeakPtr())); }
diff --git a/chrome/browser/android/webapk/webapk_installer.h b/chrome/browser/android/webapk/webapk_installer.h index 052f988e0..95ad8fb 100644 --- a/chrome/browser/android/webapk/webapk_installer.h +++ b/chrome/browser/android/webapk/webapk_installer.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_INSTALLER_H_ #include <jni.h> +#include <map> #include <memory> #include "base/android/scoped_java_ref.h" @@ -63,19 +64,22 @@ // Talks to the Chrome WebAPK server to update a WebAPK on the server and to // the Google Play server to install the downloaded WebAPK. Calls |callback| // after the request to install the WebAPK is sent to the Google Play server. - void UpdateAsync(content::BrowserContext* browser_context, - const FinishCallback& callback, - const std::string& icon_murmur2_hash, - const std::string& webapk_package, - int webapk_version); + void UpdateAsync( + content::BrowserContext* browser_context, + const FinishCallback& callback, + const std::string& webapk_package, + int webapk_version, + const std::map<std::string, std::string>& icon_url_to_murmur2_hash, + bool is_manifest_stale); // Same as UpdateAsync() but uses the passed in |request_context_getter|. void UpdateAsyncWithURLRequestContextGetter( net::URLRequestContextGetter* request_context_getter, const FinishCallback& callback, - const std::string& icon_murmur2_hash, const std::string& webapk_package, - int webapk_version); + int webapk_version, + const std::map<std::string, std::string>& icon_url_to_murmur2_hash, + bool is_manifest_stale); // Sets the timeout for the server requests. void SetTimeoutMs(int timeout_ms); @@ -85,6 +89,13 @@ const base::android::JavaParamRef<jobject>& obj, jboolean success); + // Creates a WebApk install or update request. + // Should be used only for testing. + void BuildWebApkProtoInBackgroundForTesting( + const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback, + const std::map<std::string, std::string>& icon_url_to_murmur2_hash, + bool is_manifest_stale); + // Registers JNI hooks. static bool Register(JNIEnv* env); @@ -222,10 +233,6 @@ // WebAPK app icon. const SkBitmap shortcut_icon_; - // Murmur2 hash of the bitmap at the app icon URL prior to any transformations - // being applied to the bitmap (such as encoding/decoding the icon bitmap). - std::string shortcut_icon_murmur2_hash_; - // WebAPK server URL. GURL server_url_;
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc index 0734459e..b5ad46d 100644 --- a/chrome/browser/android/webapk/webapk_installer_unittest.cc +++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -128,17 +128,18 @@ } void RunUpdateWebApk() { - const std::string kIconMurmur2Hash = "0"; const int kWebApkVersion = 1; - WebApkInstaller* installer = CreateWebApkInstaller(); + std::map<std::string, std::string> icon_url_to_murmur2_hash { + {best_icon_url_.spec(), "0"} }; - installer->UpdateAsyncWithURLRequestContextGetter( + CreateWebApkInstaller()->UpdateAsyncWithURLRequestContextGetter( url_request_context_getter_.get(), base::Bind(&WebApkInstallerRunner::OnCompleted, base::Unretained(this)), - kIconMurmur2Hash, kDownloadedWebApkPackageName, - kWebApkVersion); + kWebApkVersion, + icon_url_to_murmur2_hash, + false /* is_manifest_stale */); Run(); } @@ -204,6 +205,52 @@ return std::move(response); } +// Builds WebApk proto and blocks till done. +class BuildProtoRunner { + public: + BuildProtoRunner() {} + + ~BuildProtoRunner() {} + + void BuildSync( + const GURL& best_icon_url, + const std::map<std::string, std::string>& icon_url_to_murmur2_hash, + bool is_manifest_stale) { + ShortcutInfo info(GURL::EmptyGURL()); + info.best_icon_url = best_icon_url; + + // WebApkInstaller owns itself. + WebApkInstaller* installer = + new TestWebApkInstaller(info, SkBitmap(), false); + installer->BuildWebApkProtoInBackgroundForTesting( + base::Bind(&BuildProtoRunner::OnBuiltWebApkProto, + base::Unretained(this)), + icon_url_to_murmur2_hash, + is_manifest_stale); + + base::RunLoop run_loop; + on_completed_callback_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + webapk::WebApk* GetWebApkRequest() { return webapk_request_.get(); } + + private: + // Called when the |webapk_request_| is populated. + void OnBuiltWebApkProto(std::unique_ptr<webapk::WebApk> webapk) { + webapk_request_ = std::move(webapk); + on_completed_callback_.Run(); + } + + // The populated webapk::WebApk. + std::unique_ptr<webapk::WebApk> webapk_request_; + + // Called after the |webapk_request_| is built. + base::Closure on_completed_callback_; + + DISALLOW_COPY_AND_ASSIGN(BuildProtoRunner); +}; + } // anonymous namespace class WebApkInstallerTest : public ::testing::Test { @@ -248,6 +295,10 @@ new WebApkInstallerRunner(best_icon_url_)); } + std::unique_ptr<BuildProtoRunner> CreateBuildProtoRunner() { + return std::unique_ptr<BuildProtoRunner>(new BuildProtoRunner()); + } + net::test_server::EmbeddedTestServer* test_server() { return &test_server_; } private: @@ -368,3 +419,75 @@ runner->RunInstallWebApk(); EXPECT_TRUE(runner->success()); } + +// When there is no Web Manifest available for a site, an empty |best_icon_url| +// is used to build a WebApk update request. Tests the request can be built +// properly. +TEST_F(WebApkInstallerTest, BuildWebApkProtoWhenManifestIsObsolete) { + GURL icon_url_1 = test_server()->GetURL("/icon1.png"); + GURL icon_url_2 = test_server()->GetURL("/icon2.png"); + std::string icon_murmur2_hash_1 = "1"; + std::string icon_murmur2_hash_2 = "2"; + std::map<std::string, std::string> icon_url_to_murmur2_hash; + icon_url_to_murmur2_hash[icon_url_1.spec()] = icon_murmur2_hash_1; + icon_url_to_murmur2_hash[icon_url_2.spec()] = icon_murmur2_hash_2; + + std::unique_ptr<BuildProtoRunner> runner = CreateBuildProtoRunner(); + runner->BuildSync(GURL(""), icon_url_to_murmur2_hash, + true /* is_manifest_stale*/); + webapk::WebApk* webapk_request = runner->GetWebApkRequest(); + ASSERT_NE(nullptr, webapk_request); + + webapk::WebAppManifest manifest = webapk_request->manifest(); + ASSERT_EQ(3, manifest.icons_size()); + + webapk::Image icons[3]; + for (int i = 0; i < 3; ++i) + icons[i] = manifest.icons(i); + + EXPECT_EQ("", icons[0].src()); + EXPECT_FALSE(icons[0].has_hash()); + EXPECT_TRUE(icons[0].has_image_data()); + + EXPECT_EQ(icon_url_1.spec(), icons[1].src()); + EXPECT_EQ(icon_murmur2_hash_1, icons[1].hash()); + EXPECT_FALSE(icons[1].has_image_data()); + + EXPECT_EQ(icon_url_2.spec(), icons[2].src()); + EXPECT_EQ(icon_murmur2_hash_2, icons[2].hash()); + EXPECT_FALSE(icons[2].has_image_data()); +} + +// Tests a WebApk install or update request is built properly when the Chrome +// knows the best icon URL of a site after fetching its Web Manifest. +TEST_F(WebApkInstallerTest, BuildWebApkProtoWhenManifestIsAvailable) { + GURL icon_url_1 = test_server()->GetURL("/icon.png"); + GURL best_icon_url = test_server()->GetURL(kBestIconUrl); + std::string icon_murmur2_hash_1 = "1"; + std::string best_icon_murmur2_hash = "0"; + std::map<std::string, std::string> icon_url_to_murmur2_hash; + icon_url_to_murmur2_hash[icon_url_1.spec()] = icon_murmur2_hash_1; + icon_url_to_murmur2_hash[best_icon_url.spec()] = + best_icon_murmur2_hash; + + std::unique_ptr<BuildProtoRunner> runner = CreateBuildProtoRunner(); + runner->BuildSync(best_icon_url, icon_url_to_murmur2_hash, + false /* is_manifest_stale*/); + webapk::WebApk* webapk_request = runner->GetWebApkRequest(); + ASSERT_NE(nullptr, webapk_request); + + webapk::WebAppManifest manifest = webapk_request->manifest(); + ASSERT_EQ(2, manifest.icons_size()); + + webapk::Image icons[2]; + for (int i = 0; i < 2; ++i) + icons[i] = manifest.icons(i); + + EXPECT_EQ(best_icon_url.spec(), icons[0].src()); + EXPECT_EQ(best_icon_murmur2_hash, icons[0].hash()); + EXPECT_TRUE(icons[0].has_image_data()); + + EXPECT_EQ(icon_url_1.spec(), icons[1].src()); + EXPECT_EQ(icon_murmur2_hash_1, icons[1].hash()); + EXPECT_FALSE(icons[1].has_image_data()); +}
diff --git a/chrome/browser/android/webapk/webapk_update_manager.cc b/chrome/browser/android/webapk/webapk_update_manager.cc index a00af61..e8951658 100644 --- a/chrome/browser/android/webapk/webapk_update_manager.cc +++ b/chrome/browser/android/webapk/webapk_update_manager.cc
@@ -53,16 +53,17 @@ const JavaParamRef<jstring>& java_name, const JavaParamRef<jstring>& java_short_name, const JavaParamRef<jstring>& java_best_icon_url, - const JavaParamRef<jstring>& java_best_icon_murmur2_hash, const JavaParamRef<jobject>& java_best_icon_bitmap, const JavaParamRef<jobjectArray>& java_icon_urls, + const JavaParamRef<jobjectArray>& java_icon_hashes, jint java_display_mode, jint java_orientation, jlong java_theme_color, jlong java_background_color, const JavaParamRef<jstring>& java_web_manifest_url, const JavaParamRef<jstring>& java_webapk_package, - jint java_webapk_version) { + jint java_webapk_version, + jboolean java_is_manifest_stale) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); Profile* profile = ProfileManager::GetLastUsedProfile(); @@ -92,15 +93,19 @@ base::android::AppendJavaStringArrayToStringVector( env, java_icon_urls.obj(), &info.icon_urls); + std::vector<std::string> icon_hashes; + base::android::AppendJavaStringArrayToStringVector( + env, java_icon_hashes.obj(), &icon_hashes); + + std::map<std::string, std::string> icon_url_to_murmur2_hash; + for (size_t i = 0; i < info.icon_urls.size(); ++i) + icon_url_to_murmur2_hash[info.icon_urls[i]] = icon_hashes[i]; + gfx::JavaBitmap java_bitmap_lock(java_best_icon_bitmap); SkBitmap best_icon_bitmap = gfx::CreateSkBitmapFromJavaBitmap(java_bitmap_lock); best_icon_bitmap.setImmutable(); - std::string best_icon_murmur2_hash; - ConvertJavaStringToUTF8(env, java_best_icon_murmur2_hash, - &best_icon_murmur2_hash); - std::string webapk_package; ConvertJavaStringToUTF8(env, java_webapk_package, &webapk_package); @@ -108,5 +113,6 @@ installer->UpdateAsync( profile, base::Bind(&WebApkUpdateManager::OnBuiltWebApk, id), - best_icon_murmur2_hash, webapk_package, java_webapk_version); + webapk_package, java_webapk_version, icon_url_to_murmur2_hash, + java_is_manifest_stale); }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index d85c0a5..0a65e623f 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -359,6 +359,10 @@ "display/overscan_calibrator.h", "display/quirks_manager_delegate_impl.cc", "display/quirks_manager_delegate_impl.h", + "display/touch_calibrator/touch_calibrator_controller.cc", + "display/touch_calibrator/touch_calibrator_controller.h", + "display/touch_calibrator/touch_calibrator_view.cc", + "display/touch_calibrator/touch_calibrator_view.h", "drive/debug_info_collector.cc", "drive/debug_info_collector.h", "drive/download_handler.cc", @@ -1449,6 +1453,7 @@ "certificate_provider/certificate_provider_service_unittest.cc", "customization/customization_document_unittest.cc", "display/display_preferences_unittest.cc", + "display/touch_calibrator/touch_calibrator_controller_unittest.cc", "drive/download_handler_unittest.cc", "drive/drive_file_stream_reader_unittest.cc", "drive/drive_integration_service_unittest.cc",
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc new file mode 100644 index 0000000..29a99197 --- /dev/null +++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc
@@ -0,0 +1,146 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h" + +#include "ash/shell.h" +#include "ash/touch/touch_transformer_controller.h" +#include "base/memory/ptr_util.h" +#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h" +#include "ui/display/screen.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" + +namespace chromeos { + +// Time interval after a touch event during which all other touch events are +// ignored during calibration. +const base::TimeDelta TouchCalibratorController::kTouchIntervalThreshold = + base::TimeDelta::FromMilliseconds(200); + +TouchCalibratorController::TouchCalibratorController() + : last_touch_timestamp_(base::Time::Now()) { + ash::Shell::GetInstance()->window_tree_host_manager()->AddObserver(this); +} + +TouchCalibratorController::~TouchCalibratorController() { + ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this); + touch_calibrator_views_.clear(); + StopCalibration(); +} + +void TouchCalibratorController::OnDisplayConfigurationChanged() { + touch_calibrator_views_.clear(); + StopCalibration(); +} + +void TouchCalibratorController::StartCalibration( + const display::Display& target_display) { + is_calibrating_ = true; + target_display_ = target_display; + + // Clear all touch calibrator views used in any previous calibration. + touch_calibrator_views_.clear(); + + // Reset the calibration data. + touch_point_quad_.fill(std::make_pair(gfx::Point(0, 0), gfx::Point(0, 0))); + + std::vector<display::Display> displays = + display::Screen::GetScreen()->GetAllDisplays(); + + for (const display::Display& display : displays) { + bool is_primary_view = display.id() == target_display_.id(); + touch_calibrator_views_[display.id()] = + base::MakeUnique<TouchCalibratorView>(display, is_primary_view); + } + + // TODO(malaykeshav): Call TouchTransformerController::SetForCalibration() + + // Add self as an event handler target. + ash::Shell::GetInstance()->AddPreTargetHandler(this); +} + +void TouchCalibratorController::StopCalibration() { + if (!is_calibrating_) + return; + is_calibrating_ = false; + + // TODO(malaykeshav): Call TouchTransformerController::SetForCalibration() + + // Remove self as the event handler. + ash::Shell::GetInstance()->RemovePreTargetHandler(this); + + // Transition all touch calibrator views to their final state for a graceful + // exit. + for (const auto& it : touch_calibrator_views_) + it.second->SkipToFinalState(); +} + +// ui::EventHandler: +void TouchCalibratorController::OnKeyEvent(ui::KeyEvent* key) { + if (!is_calibrating_) + return; + // Detect ESC key press. + if (key->type() == ui::ET_KEY_PRESSED && key->key_code() == ui::VKEY_ESCAPE) + StopCalibration(); + key->StopPropagation(); +} + +void TouchCalibratorController::OnTouchEvent(ui::TouchEvent* touch) { + if (!is_calibrating_) + return; + if (touch->type() != ui::ET_TOUCH_RELEASED) + return; + if (base::Time::Now() - last_touch_timestamp_ < kTouchIntervalThreshold) + return; + + last_touch_timestamp_ = base::Time::Now(); + + TouchCalibratorView* target_screen_calibration_view = + touch_calibrator_views_[target_display_.id()].get(); + + int state_index; + // Maps the state to an integer value. Assigns a non negative integral value + // for a state in which the user can interact with the the interface. + switch (target_screen_calibration_view->state()) { + case TouchCalibratorView::DISPLAY_POINT_1: + state_index = 0; + break; + case TouchCalibratorView::DISPLAY_POINT_2: + state_index = 1; + break; + case TouchCalibratorView::DISPLAY_POINT_3: + state_index = 2; + break; + case TouchCalibratorView::DISPLAY_POINT_4: + state_index = 3; + break; + default: + // Return early if the interface is in a state that does not allow user + // interaction. + return; + } + + // Store touch point corresponding to its display point. + gfx::Point display_point; + if (target_screen_calibration_view->GetDisplayPointLocation(&display_point)) { + touch_point_quad_[state_index] = + std::make_pair(display_point, touch->location()); + } + + // If this is the final state, then store all calibration data and stop + // calibration. + if (target_screen_calibration_view->state() == + TouchCalibratorView::CALIBRATION_COMPLETE) { + ash::Shell::GetInstance()->display_manager()->SetTouchCalibrationData( + target_display_.id(), touch_point_quad_, + target_screen_calibration_view->size()); + StopCalibration(); + return; + } + + target_screen_calibration_view->AdvanceToNextState(); +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h new file mode 100644 index 0000000..4f51cd4 --- /dev/null +++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h
@@ -0,0 +1,85 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_ +#define CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_ + +#include <map> + +#include "ash/display/window_tree_host_manager.h" +#include "base/time/time.h" +#include "ui/display/display.h" +#include "ui/display/manager/managed_display_info.h" +#include "ui/events/event_handler.h" + +namespace ui { +class KeyEvent; +class TouchEvent; +} + +namespace chromeos { + +class TouchCalibratorView; + +// TouchCalibratorController is responsible for collecting the touch calibration +// associated data from the user. It instantiates TouchCalibratorView classes to +// present an interface the user can interact with for calibration. +// Touch calibration is restricted to calibrate only one display at a time. +class TouchCalibratorController : public ui::EventHandler, + public ash::WindowTreeHostManager::Observer { + public: + using CalibrationPointPairQuad = + display::TouchCalibrationData::CalibrationPointPairQuad; + + static const base::TimeDelta kTouchIntervalThreshold; + + TouchCalibratorController(); + ~TouchCalibratorController() override; + + // ui::EventHandler + void OnKeyEvent(ui::KeyEvent* event) override; + void OnTouchEvent(ui::TouchEvent* event) override; + + // WindowTreeHostManager::Observer + void OnDisplayConfigurationChanged() override; + + // Starts the calibration process for the given |target_display|. + void StartCalibration(const display::Display& target_display); + + // Stops any ongoing calibration process. + void StopCalibration(); + + bool is_calibrating() { return is_calibrating_; } + + private: + friend class TouchCalibratorControllerTest; + FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest, StartCalibration); + FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest, KeyEventIntercept); + FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest, TouchThreshold); + + // A map for TouchCalibrator view with the key as display id of the display + // it is present in. + std::map<int64_t, std::unique_ptr<TouchCalibratorView>> + touch_calibrator_views_; + + // The display which is being calibrated by the touch calibrator controller. + // This is valid only if |is_calibrating| is set to true. + display::Display target_display_; + + // During calibration this stores the timestamp when the previous touch event + // was received. + base::Time last_touch_timestamp_; + + // Is true if a touch calibration is already underprocess. + bool is_calibrating_ = false; + + // An array of Calibration point pairs. This stores all the 4 display and + // touch input point pairs that will be used for calibration. + CalibrationPointPairQuad touch_point_quad_; + + DISALLOW_COPY_AND_ASSIGN(TouchCalibratorController); +}; + +} // namespace chromeos +#endif // CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc new file mode 100644 index 0000000..d5627a5 --- /dev/null +++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc
@@ -0,0 +1,116 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h" + +#include <algorithm> +#include <vector> + +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h" +#include "ui/display/display.h" +#include "ui/display/manager/display_manager.h" +#include "ui/events/base_event_utils.h" +#include "ui/events/event_handler.h" +#include "ui/events/test/event_generator.h" +#include "ui/events/test/events_test_utils.h" + +using namespace display; + +namespace chromeos { + +class TouchCalibratorControllerTest : public ash::test::AshTestBase { + public: + TouchCalibratorControllerTest() {} + + const Display& InitDisplays() { + // Initialize 2 displays each with resolution 500x500. + UpdateDisplay("500x500,500x500"); + // Assuming index 0 points to the native display, we will calibrate the + // touch display at index 1. + const int kTargetDisplayIndex = 1; + DisplayIdList display_id_list = + display_manager()->GetCurrentDisplayIdList(); + int64_t target_display_id = display_id_list[kTargetDisplayIndex]; + const Display& touch_display = + display_manager()->GetDisplayForId(target_display_id); + return touch_display; + } + + void StartCalibrationChecks(TouchCalibratorController* ctrl, + const Display& target_display) { + EXPECT_FALSE(ctrl->is_calibrating()); + EXPECT_FALSE(!!ctrl->touch_calibrator_views_.size()); + + ctrl->StartCalibration(target_display); + EXPECT_TRUE(ctrl->is_calibrating()); + + // There should be a touch calibrator view associated with each of the + // active displays. + EXPECT_EQ(ctrl->touch_calibrator_views_.size(), + display_manager()->GetCurrentDisplayIdList().size()); + + TouchCalibratorView* target_calibrator_view = + ctrl->touch_calibrator_views_[target_display.id()].get(); + + EXPECT_EQ(target_calibrator_view->state(), TouchCalibratorView::UNKNOWN); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TouchCalibratorControllerTest); +}; + +TEST_F(TouchCalibratorControllerTest, StartCalibration) { + const Display& touch_display = InitDisplays(); + TouchCalibratorController touch_calibrator_controller; + StartCalibrationChecks(&touch_calibrator_controller, touch_display); + + ui::EventTargetTestApi test_api(ash::Shell::GetInstance()); + const ui::EventHandlerList& handlers = test_api.pre_target_handlers(); + ui::EventHandlerList::const_iterator event_target = + std::find(handlers.begin(), handlers.end(), &touch_calibrator_controller); + EXPECT_NE(event_target, handlers.end()); +} + +TEST_F(TouchCalibratorControllerTest, KeyEventIntercept) { + const Display& touch_display = InitDisplays(); + TouchCalibratorController touch_calibrator_controller; + StartCalibrationChecks(&touch_calibrator_controller, touch_display); + + ui::test::EventGenerator& eg = GetEventGenerator(); + EXPECT_TRUE(touch_calibrator_controller.is_calibrating()); + eg.PressKey(ui::VKEY_ESCAPE, ui::EF_NONE); + EXPECT_FALSE(touch_calibrator_controller.is_calibrating()); +} + +TEST_F(TouchCalibratorControllerTest, TouchThreshold) { + const Display& touch_display = InitDisplays(); + TouchCalibratorController touch_calibrator_controller; + StartCalibrationChecks(&touch_calibrator_controller, touch_display); + + ui::test::EventGenerator& eg = GetEventGenerator(); + + base::Time current_timestamp = base::Time::Now(); + touch_calibrator_controller.last_touch_timestamp_ = current_timestamp; + + eg.set_current_location(gfx::Point(0, 0)); + eg.PressTouch(); + eg.ReleaseTouch(); + + EXPECT_EQ(touch_calibrator_controller.last_touch_timestamp_, + current_timestamp); + + current_timestamp = base::Time::Now(); + touch_calibrator_controller.last_touch_timestamp_ = + current_timestamp - (TouchCalibratorController::kTouchIntervalThreshold); + + eg.PressTouch(); + eg.ReleaseTouch(); + + EXPECT_LT(current_timestamp, + touch_calibrator_controller.last_touch_timestamp_); +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc new file mode 100644 index 0000000..c0484a6 --- /dev/null +++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
@@ -0,0 +1,82 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h" + +#include "ash/display/window_tree_host_manager.h" +#include "ash/public/cpp/shell_window_ids.h" +#include "ash/shell.h" +#include "ui/aura/window.h" +#include "ui/gfx/canvas.h" +#include "ui/views/widget/widget.h" + +namespace chromeos { + +namespace { + +constexpr char kWidgetName[] = "TouchCalibratorOverlay"; + +// Returns the initialization params for the widget that contains the touch +// calibrator view. +views::Widget::InitParams GetWidgetParams(aura::Window* root_window) { + views::Widget::InitParams params; + params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; + params.name = kWidgetName; + params.keep_on_top = true; + params.accept_events = true; + params.activatable = views::Widget::InitParams::ACTIVATABLE_NO; + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + params.parent = ash::Shell::GetContainer( + root_window, ash::kShellWindowId_OverlayContainer); + return params; +} + +} // namespace + +TouchCalibratorView::TouchCalibratorView(const display::Display& target_display, + bool is_primary_view) + : display_(target_display), is_primary_view_(is_primary_view) { + aura::Window* root = ash::Shell::GetInstance() + ->window_tree_host_manager() + ->GetRootWindowForDisplayId(display_.id()); + widget_.reset(new views::Widget); + widget_->Init(GetWidgetParams(root)); + widget_->SetContentsView(this); + widget_->SetBounds(display_.bounds()); + widget_->Show(); + set_owned_by_client(); +} + +TouchCalibratorView::~TouchCalibratorView() { + state_ = UNKNOWN; + widget_->Hide(); +} + +void TouchCalibratorView::OnPaint(gfx::Canvas* canvas) { + OnPaintBackground(canvas); +} + +void TouchCalibratorView::OnPaintBackground(gfx::Canvas* canvas) {} + +void TouchCalibratorView::AnimationProgressed(const gfx::Animation* animation) { +} + +void TouchCalibratorView::AnimationCanceled(const gfx::Animation* animation) { + AnimationEnded(animation); +} + +void TouchCalibratorView::AnimationEnded(const gfx::Animation* animation) {} + +void TouchCalibratorView::AdvanceToNextState() {} + +bool TouchCalibratorView::GetDisplayPointLocation(gfx::Point* location) { + if (!is_primary_view_) + return false; + return false; +} + +void TouchCalibratorView::SkipToFinalState() {} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h new file mode 100644 index 0000000..aeaf20f --- /dev/null +++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h
@@ -0,0 +1,94 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_ +#define CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_ + +#include "base/macros.h" +#include "ui/display/display.h" +#include "ui/gfx/animation/animation_delegate.h" +#include "ui/views/view.h" + +namespace views { +class Widget; +} + +namespace chromeos { + +// An overlay view used during touch calibration. This view is responsible for +// all animations and UX during touch calibration on all displays currently +// active on the device. The view on the display being calibrated is the primary +// touch calibration view. +// |TouchCalibratorView| acts as a state machine and has an API to toggle its +// state or get the current state. +class TouchCalibratorView : public views::View, public gfx::AnimationDelegate { + public: + // Different states of |TouchCalibratorView| in order. + enum State { + UNKNOWN = 0, + BACKGROUND_FADING_IN, // Transition state where the background is fading + // in. + DISPLAY_POINT_1, // Static state where the touch point is at its first + // location. + ANIMATING_1_TO_2, // Transition state when the touch point is being moved + // from one location to another. + DISPLAY_POINT_2, // Static state where the touch point is at its second + // location. + ANIMATING_2_TO_3, + DISPLAY_POINT_3, // Static state where the touch point is at its third + // location. + ANIMATING_3_TO_4, + DISPLAY_POINT_4, // Static state where the touch point is at its final + // location. + CALIBRATION_COMPLETE, // Static state when the calibration complete message + // is displayed to the user. + BACKGROUND_FADING_OUT // Transition state where the background is fading + // out + }; + + TouchCalibratorView(const display::Display& target_display, + bool is_primary_view); + ~TouchCalibratorView() override; + + // views::View overrides: + void OnPaint(gfx::Canvas* canvas) override; + void OnPaintBackground(gfx::Canvas* canvas) override; + + // gfx::AnimationDelegate overrides: + void AnimationEnded(const gfx::Animation* animation) override; + void AnimationProgressed(const gfx::Animation* animation) override; + void AnimationCanceled(const gfx::Animation* animation) override; + + // Moves the touch calibrator view to its next state. + void AdvanceToNextState(); + + // Skips to the final state. Should be used to cancel calibration and hide all + // views from the screen with a smooth transition out animation. + void SkipToFinalState(); + + // Returns true if |location| is set by the end of this function call. If set, + // |location| will point to the center of the circle that the user sees during + // the touch calibration UX. + bool GetDisplayPointLocation(gfx::Point* location); + + // Returns the current state of the view. + State state() { return state_; } + + private: + // The target display on which this view is rendered on. + const display::Display display_; + + // True if this view is on the display that is being calibrated. + bool is_primary_view_ = false; + + std::unique_ptr<views::Widget> widget_; + + State state_ = UNKNOWN; + + DISALLOW_COPY_AND_ASSIGN(TouchCalibratorView); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_
diff --git a/chrome/browser/chromeos/login/easy_unlock/bootstrap_manager.cc b/chrome/browser/chromeos/login/easy_unlock/bootstrap_manager.cc index b5c8f1a..d7d9c09 100644 --- a/chrome/browser/chromeos/login/easy_unlock/bootstrap_manager.cc +++ b/chrome/browser/chromeos/login/easy_unlock/bootstrap_manager.cc
@@ -11,6 +11,7 @@ #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" +#include "components/signin/core/account_id/account_id.h" #include "components/user_manager/known_user.h" namespace chromeos { @@ -66,7 +67,8 @@ if (users->GetString(i, ¤t_user_email)) { delegate_->RemovePendingBootstrapUser( user_manager::known_user::GetAccountId(current_user_email, - std::string() /* gaia_id */)); + std::string() /* id */, + AccountType::UNKNOWN)); } }
diff --git a/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_context_initializer.cc b/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_context_initializer.cc index 0cee775..d2434ec 100644 --- a/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_context_initializer.cc +++ b/chrome/browser/chromeos/login/easy_unlock/bootstrap_user_context_initializer.cc
@@ -178,7 +178,7 @@ } user_context_.SetAccountId(user_manager::known_user::GetAccountId( - user_manager::CanonicalizeUserID(email), gaia_id)); + user_manager::CanonicalizeUserID(email), gaia_id, AccountType::GOOGLE)); StartCheckExistingKeys(); }
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc index cdce1cf1..04a96fb5 100644 --- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc +++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc
@@ -40,8 +40,9 @@ EasyUnlockTpmKeyManager* EasyUnlockTpmKeyManagerFactory::GetForUser( const std::string& user_id) { user_manager::UserManager* user_manager = user_manager::UserManager::Get(); - const user_manager::User* user = user_manager->FindUser( - user_manager::known_user::GetAccountId(user_id, std::string())); + const user_manager::User* user = + user_manager->FindUser(user_manager::known_user::GetAccountId( + user_id, std::string() /* id */, AccountType::UNKNOWN)); if (!user) return NULL; Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user);
diff --git a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc index 9362462..affc58b 100644 --- a/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc +++ b/chrome/browser/chromeos/login/screens/chrome_user_selection_screen.cc
@@ -85,8 +85,8 @@ void ChromeUserSelectionScreen::CheckForPublicSessionDisplayNameChange( policy::DeviceLocalAccountPolicyBroker* broker) { - const AccountId& account_id = - user_manager::known_user::GetAccountId(broker->user_id(), std::string()); + const AccountId& account_id = user_manager::known_user::GetAccountId( + broker->user_id(), std::string() /* id */, AccountType::UNKNOWN); DCHECK(account_id.is_valid()); const std::string& display_name = broker->GetDisplayName(); if (display_name == public_session_display_names_[account_id]) @@ -116,8 +116,8 @@ void ChromeUserSelectionScreen::CheckForPublicSessionLocalePolicyChange( policy::DeviceLocalAccountPolicyBroker* broker) { - const AccountId& account_id = - user_manager::known_user::GetAccountId(broker->user_id(), std::string()); + const AccountId& account_id = user_manager::known_user::GetAccountId( + broker->user_id(), std::string() /* id */, AccountType::UNKNOWN); DCHECK(account_id.is_valid()); const policy::PolicyMap::Entry* entry = broker->core()->store()->policy_map().Get(policy::key::kSessionLocales);
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.cc b/chrome/browser/chromeos/login/screens/user_selection_screen.cc index dedfd7d..a3e89b2c 100644 --- a/chrome/browser/chromeos/login/screens/user_selection_screen.cc +++ b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
@@ -372,8 +372,8 @@ std::string owner_email; chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner_email); - const AccountId owner = - user_manager::known_user::GetAccountId(owner_email, std::string()); + const AccountId owner = user_manager::known_user::GetAccountId( + owner_email, std::string() /* id */, AccountType::UNKNOWN); policy::BrowserPolicyConnectorChromeOS* connector = g_browser_process->platform_part()->browser_policy_connector_chromeos();
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc index 581a1c1..94406b4 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.cc +++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -568,7 +568,7 @@ .WithShutdownBehavior( base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) .WithPriority(base::TaskPriority::BACKGROUND) - .WithFileIO(), + .MayBlock(), base::Bind(&base::PathExists, GetRlzDisabledFlagPath()), base::Bind(&UserSessionManager::InitRlzImpl, AsWeakPtr(), profile)); #endif @@ -898,11 +898,7 @@ } void UserSessionManager::StoreUserContextDataBeforeProfileIsCreated() { - // Store obfuscated GAIA ID. - if (!user_context_.GetGaiaID().empty()) { - user_manager::known_user::UpdateGaiaID(user_context_.GetAccountId(), - user_context_.GetGaiaID()); - } + user_manager::known_user::UpdateId(user_context_.GetAccountId()); } void UserSessionManager::StartCrosSession() {
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc b/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc index 7cbad7c..bca16cf0 100644 --- a/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc +++ b/chrome/browser/chromeos/login/supervised/supervised_user_authenticator.cc
@@ -70,7 +70,7 @@ Key key(attempt->password); key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt); const AccountId account_id = user_manager::known_user::GetAccountId( - attempt->username, std::string() /* gaia_id */); + attempt->username, std::string() /* id */, AccountType::UNKNOWN); const cryptohome::Identification cryptohome_id(account_id); cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount( cryptohome_id, key.GetSecret(), flags, @@ -95,7 +95,7 @@ Key master_key(plain_text_master_key); master_key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt); const AccountId account_id = user_manager::known_user::GetAccountId( - attempt->username, std::string() /* gaia_id */); + attempt->username, std::string() /* id */, AccountType::UNKNOWN); cryptohome::AsyncMethodCaller::GetInstance()->AsyncAddKey( cryptohome::Identification(account_id), user_key.GetSecret(), master_key.GetSecret(),
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc index c7c0dba2..c72843a6 100644 --- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc +++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -500,8 +500,8 @@ void ChromeUserManagerImpl::OnExternalDataSet(const std::string& policy, const std::string& user_id) { - const AccountId account_id = - user_manager::known_user::GetAccountId(user_id, std::string()); + const AccountId account_id = user_manager::known_user::GetAccountId( + user_id, std::string() /* id */, AccountType::UNKNOWN); if (policy == policy::key::kUserAvatarImage) GetUserImageManager(account_id)->OnExternalDataSet(policy); else if (policy == policy::key::kWallpaperImage) @@ -512,8 +512,8 @@ void ChromeUserManagerImpl::OnExternalDataCleared(const std::string& policy, const std::string& user_id) { - const AccountId account_id = - user_manager::known_user::GetAccountId(user_id, std::string()); + const AccountId account_id = user_manager::known_user::GetAccountId( + user_id, std::string() /* id */, AccountType::UNKNOWN); if (policy == policy::key::kUserAvatarImage) GetUserImageManager(account_id)->OnExternalDataCleared(policy); else if (policy == policy::key::kWallpaperImage) @@ -526,8 +526,8 @@ const std::string& policy, const std::string& user_id, std::unique_ptr<std::string> data) { - const AccountId account_id = - user_manager::known_user::GetAccountId(user_id, std::string()); + const AccountId account_id = user_manager::known_user::GetAccountId( + user_id, std::string() /* id */, AccountType::UNKNOWN); if (policy == policy::key::kUserAvatarImage) GetUserImageManager(account_id) ->OnExternalDataFetched(policy, std::move(data)); @@ -539,8 +539,8 @@ } void ChromeUserManagerImpl::OnPolicyUpdated(const std::string& user_id) { - const AccountId account_id = - user_manager::known_user::GetAccountId(user_id, std::string()); + const AccountId account_id = user_manager::known_user::GetAccountId( + user_id, std::string() /* id */, AccountType::UNKNOWN); const user_manager::User* user = FindUser(account_id); if (!user || user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT) return; @@ -1216,8 +1216,8 @@ void ChromeUserManagerImpl::SetUserAffiliation( const std::string& user_email, const AffiliationIDSet& user_affiliation_ids) { - const AccountId& account_id = - user_manager::known_user::GetAccountId(user_email, std::string()); + const AccountId& account_id = user_manager::known_user::GetAccountId( + user_email, std::string() /* id */, AccountType::UNKNOWN); user_manager::User* user = FindUserAndModify(account_id); if (user) {
diff --git a/chrome/browser/chromeos/printing/printer_pref_manager.cc b/chrome/browser/chromeos/printing/printer_pref_manager.cc index e92824e..3e52bf1e 100644 --- a/chrome/browser/chromeos/printing/printer_pref_manager.cc +++ b/chrome/browser/chromeos/printing/printer_pref_manager.cc
@@ -10,6 +10,8 @@ #include <vector> #include "base/guid.h" +#include "base/json/json_reader.h" +#include "base/md5.h" #include "base/memory/ptr_util.h" #include "base/values.h" #include "chrome/browser/profiles/profile.h" @@ -65,7 +67,16 @@ } // anonymous namespace -PrinterPrefManager::PrinterPrefManager(Profile* profile) : profile_(profile) {} +PrinterPrefManager::PrinterPrefManager(Profile* profile) : profile_(profile) { + pref_change_registrar_.Init(profile->GetPrefs()); + pref_change_registrar_.Add( + prefs::kRecommendedNativePrinters, + base::Bind(&PrinterPrefManager::UpdateRecommendedPrinters, + base::Unretained(this))); + UpdateRecommendedPrinters(); +} + +PrinterPrefManager::~PrinterPrefManager() {} // static void PrinterPrefManager::RegisterProfilePrefs( @@ -96,8 +107,32 @@ return printers; } +std::vector<std::unique_ptr<Printer>> +PrinterPrefManager::GetRecommendedPrinters() const { + std::vector<std::unique_ptr<Printer>> printers; + + for (const std::string& key : recommended_printer_ids_) { + const base::DictionaryValue& printer_dictionary = + *(recommended_printers_.at(key)); + std::unique_ptr<Printer> printer = + printing::RecommendedPrinterToPrinter(printer_dictionary); + if (printer) { + printer->set_source(Printer::SRC_POLICY); + printers.push_back(std::move(printer)); + } + } + + return printers; +} + std::unique_ptr<Printer> PrinterPrefManager::GetPrinter( const std::string& printer_id) const { + // check for a policy printer first + const auto& policy_printers = recommended_printers_; + auto found = policy_printers.find(printer_id); + if (found != policy_printers.end()) + return printing::RecommendedPrinterToPrinter(*(found->second)); + const base::ListValue* values = GetPrinterList(profile_); const base::DictionaryValue* printer = FindPrinterPref(values, printer_id); @@ -108,6 +143,7 @@ if (printer->id().empty()) printer->set_id(base::GenerateGUID()); + DCHECK_EQ(Printer::SRC_USER_PREFS, printer->source()); std::unique_ptr<base::DictionaryValue> updated_printer = printing::PrinterToPref(*printer); UpdatePrinterPref(profile_, printer->id(), std::move(updated_printer)); @@ -123,4 +159,41 @@ return printer && printer_list->Remove(*printer, nullptr); } +// This method is not thread safe and could interact poorly with readers of +// |recommended_printers_|. +void PrinterPrefManager::UpdateRecommendedPrinters() { + const PrefService* prefs = profile_->GetPrefs(); + + const base::ListValue* values = + prefs->GetList(prefs::kRecommendedNativePrinters); + + recommended_printer_ids_.clear(); + for (const auto& value : *values) { + std::string printer_json; + if (!value->GetAsString(&printer_json)) { + NOTREACHED(); + continue; + } + + std::unique_ptr<base::DictionaryValue> printer_dictionary = + base::DictionaryValue::From(base::JSONReader::Read( + printer_json, base::JSON_ALLOW_TRAILING_COMMAS)); + + if (!printer_dictionary) { + LOG(WARNING) << "Ignoring invalid printer. Invalid JSON object: " + << printer_json; + continue; + } + + // Policy printers don't have id's but the ids only need to be locally + // unique so we'll hash the record. This will not collide with the UUIDs + // generated for user entries. + std::string id = base::MD5String(printer_json); + printer_dictionary->SetString(printing::kPrinterId, id); + + recommended_printer_ids_.push_back(id); + recommended_printers_[id] = std::move(printer_dictionary); + } +} + } // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/printer_pref_manager.h b/chrome/browser/chromeos/printing/printer_pref_manager.h index 69cb923..222de0d 100644 --- a/chrome/browser/chromeos/printing/printer_pref_manager.h +++ b/chrome/browser/chromeos/printing/printer_pref_manager.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_PRINTING_PRINTER_PREF_MANAGER_H_ #define CHROME_BROWSER_CHROMEOS_PRINTING_PRINTER_PREF_MANAGER_H_ +#include <map> #include <memory> #include <string> #include <vector> @@ -12,6 +13,7 @@ #include "chromeos/printing/printer_configuration.h" #include "chromeos/printing/printer_translator.h" #include "components/keyed_service/core/keyed_service.h" +#include "components/prefs/pref_change_registrar.h" class Profile; @@ -24,6 +26,7 @@ class PrinterPrefManager : public KeyedService { public: explicit PrinterPrefManager(Profile* profile); + ~PrinterPrefManager() override; // Register the printing preferences with the |registry|. static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); @@ -31,6 +34,9 @@ // Returns the printers that are saved in preferences. std::vector<std::unique_ptr<Printer>> GetPrinters() const; + // Returns printers from enterprise policy. + std::vector<std::unique_ptr<Printer>> GetRecommendedPrinters() const; + // Returns the printer with id |printer_id|. std::unique_ptr<Printer> GetPrinter(const std::string& printer_id) const; @@ -43,7 +49,17 @@ bool RemovePrinter(const std::string& printer_id); private: + // Updates the in-memory recommended printer list. + void UpdateRecommendedPrinters(); + Profile* profile_; + PrefChangeRegistrar pref_change_registrar_; + + // Contains the keys for all recommended printers in order so we can return + // the list of recommended printers in the order they were received. + std::vector<std::string> recommended_printer_ids_; + std::map<std::string, std::unique_ptr<base::DictionaryValue>> + recommended_printers_; }; } // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/printer_pref_manager_unittest.cc b/chrome/browser/chromeos/printing/printer_pref_manager_unittest.cc index 58dc1c4..4b02a745 100644 --- a/chrome/browser/chromeos/printing/printer_pref_manager_unittest.cc +++ b/chrome/browser/chromeos/printing/printer_pref_manager_unittest.cc
@@ -9,7 +9,9 @@ #include "base/memory/ptr_util.h" #include "chrome/browser/chromeos/printing/printer_pref_manager_factory.h" +#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" @@ -20,11 +22,23 @@ const char kPrinterId[] = "UUID-UUID-UUID-PRINTER"; const char kUri[] = "ipps://printer.chromium.org/ipp/print"; +const char kLexJson[] = R"json({ + "display_name": "LexaPrint", + "description": "Laser on the test shelf", + "manufacturer": "LexaPrint, Inc.", + "model": "MS610de", + "uri": "ipp://192.168.1.5", + "ppd_resource": { + "effective_manufacturer": "LexaPrint", + "effective_model": "MS610de", + }, + } )json"; + } // namespace TEST(PrinterPrefManagerTest, AddPrinter) { content::TestBrowserThreadBundle thread_bundle; - std::unique_ptr<Profile> profile = base::MakeUnique<TestingProfile>(); + auto profile = base::MakeUnique<TestingProfile>(); PrinterPrefManager* manager = PrinterPrefManagerFactory::GetForBrowserContext(profile.get()); @@ -37,7 +51,7 @@ TEST(PrinterPrefManagerTest, UpdatePrinterAssignsId) { content::TestBrowserThreadBundle thread_bundle; - std::unique_ptr<Profile> profile = base::MakeUnique<TestingProfile>(); + auto profile = base::MakeUnique<TestingProfile>(); PrinterPrefManager* manager = PrinterPrefManagerFactory::GetForBrowserContext(profile.get()); @@ -50,13 +64,12 @@ TEST(PrinterPrefManagerTest, UpdatePrinter) { content::TestBrowserThreadBundle thread_bundle; - std::unique_ptr<Profile> profile = base::MakeUnique<TestingProfile>(); + auto profile = base::MakeUnique<TestingProfile>(); PrinterPrefManager* manager = PrinterPrefManagerFactory::GetForBrowserContext(profile.get()); manager->RegisterPrinter(base::MakeUnique<Printer>(kPrinterId)); - std::unique_ptr<Printer> updated_printer = - base::MakeUnique<Printer>(kPrinterId); + auto updated_printer = base::MakeUnique<Printer>(kPrinterId); updated_printer->set_uri(kUri); manager->RegisterPrinter(std::move(updated_printer)); @@ -67,7 +80,7 @@ TEST(PrinterPrefManagerTest, RemovePrinter) { content::TestBrowserThreadBundle thread_bundle; - std::unique_ptr<Profile> profile = base::MakeUnique<TestingProfile>(); + auto profile = base::MakeUnique<TestingProfile>(); PrinterPrefManager* manager = PrinterPrefManagerFactory::GetForBrowserContext(profile.get()); @@ -83,4 +96,68 @@ EXPECT_NE(kPrinterId, printers.at(1)->id()); } +TEST(PrinterPrefManagerTest, RecommendedPrinters) { + content::TestBrowserThreadBundle thread_bundle; + + std::string first_printer = + R"json({ + "display_name": "Color Laser", + "description": "The printer next to the water cooler.", + "manufacturer": "Printer Manufacturer", + "model":"Color Laser 2004", + "uri":"ipps://print-server.intranet.example.com:443/ipp/cl2k4", + "uuid":"1c395fdb-5d93-4904-b246-b2c046e79d12", + "ppd_resource":{ + "effective_manufacturer": "MakesPrinters", + "effective_model":"ColorLaser2k4" + } + })json"; + + std::string second_printer = kLexJson; + + auto value = base::MakeUnique<base::ListValue>(); + value->AppendString(first_printer); + value->AppendString(second_printer); + + TestingProfile profile; + sync_preferences::TestingPrefServiceSyncable* prefs = + profile.GetTestingPrefService(); + // TestingPrefSyncableService assumes ownership of |value|. + prefs->SetManagedPref(prefs::kRecommendedNativePrinters, value.release()); + + PrinterPrefManager* manager = + PrinterPrefManagerFactory::GetForBrowserContext(&profile); + + auto printers = manager->GetRecommendedPrinters(); + ASSERT_EQ(2U, printers.size()); + EXPECT_EQ("Color Laser", printers[0]->display_name()); + EXPECT_EQ("ipp://192.168.1.5", printers[1]->uri()); +} + +TEST(PrinterPrefManagerTest, GetRecommendedPrinter) { + content::TestBrowserThreadBundle thread_bundle; + + std::string printer = kLexJson; + auto value = base::MakeUnique<base::ListValue>(); + value->AppendString(printer); + + TestingProfile profile; + sync_preferences::TestingPrefServiceSyncable* prefs = + profile.GetTestingPrefService(); + // TestingPrefSyncableService assumes ownership of |value|. + prefs->SetManagedPref(prefs::kRecommendedNativePrinters, value.release()); + + PrinterPrefManager* manager = + PrinterPrefManagerFactory::GetForBrowserContext(&profile); + + auto printers = manager->GetRecommendedPrinters(); + + const Printer& from_list = *(printers.front()); + std::unique_ptr<Printer> retrieved = manager->GetPrinter(from_list.id()); + + EXPECT_EQ(from_list.id(), retrieved->id()); + EXPECT_EQ("LexaPrint", from_list.display_name()); + EXPECT_EQ(Printer::Source::SRC_POLICY, from_list.source()); +} + } // namespace chromeos
diff --git a/chrome/browser/mac/keystone_glue.mm b/chrome/browser/mac/keystone_glue.mm index 7571460..1404616 100644 --- a/chrome/browser/mac/keystone_glue.mm +++ b/chrome/browser/mac/keystone_glue.mm
@@ -73,7 +73,7 @@ scoped_refptr<PerformBridge> op = new PerformBridge(target, sel, arg); base::PostTaskWithTraits( FROM_HERE, base::TaskTraits() - .WithFileIO() + .MayBlock() .WithPriority(base::TaskPriority::BACKGROUND) .WithShutdownBehavior( base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc index 8cfc600..2674edc 100644 --- a/chrome/browser/metrics/chrome_metrics_service_client.cc +++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -30,6 +30,7 @@ #include "chrome/browser/google/google_brand.h" #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" #include "chrome/browser/metrics/chrome_stability_metrics_provider.h" +#include "chrome/browser/metrics/https_engagement_metrics_provider.h" #include "chrome/browser/metrics/metrics_reporting_state.h" #include "chrome/browser/metrics/sampling_metrics_provider.h" #include "chrome/browser/metrics/subprocess_metrics_provider.h" @@ -691,6 +692,10 @@ std::unique_ptr<metrics::MetricsProvider>( new syncer::DeviceCountMetricsProvider(base::Bind( &browser_sync::ChromeSyncClient::GetDeviceInfoTrackers)))); + + metrics_service_->RegisterMetricsProvider( + std::unique_ptr<metrics::MetricsProvider>( + new HttpsEngagementMetricsProvider())); } bool ChromeMetricsServiceClient::ShouldIncludeProfilerDataInLog() {
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 666f0714..c028993 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -50,6 +50,7 @@ #include "components/signin/core/common/signin_pref_names.h" #include "components/spellcheck/spellcheck_build_features.h" #include "components/ssl_config/ssl_config_prefs.h" +#include "components/sync/base/pref_names.h" #include "components/sync/driver/sync_policy_handler.h" #include "components/translate/core/common/translate_pref_names.h" #include "components/variations/pref_names.h" @@ -569,10 +570,11 @@ { key::kBrowserAddPersonEnabled, prefs::kBrowserAddPersonEnabled, base::Value::Type::BOOLEAN }, +#endif // !defined(OS_CHROMEOS) && !defined(OS_ANDROID) + { key::kForceBrowserSignin, prefs::kForceBrowserSignin, base::Value::Type::BOOLEAN }, -#endif // !defined(OS_CHROMEOS) && !defined(OS_ANDROID) #if defined(OS_WIN) { key::kWelcomePageOnOSUpgradeEnabled, @@ -601,20 +603,33 @@ prefs::kNetworkThrottlingEnabled, base::Value::Type::DICTIONARY }, - { key::kAllowScreenLock, prefs::kAllowScreenLock, + { key::kAllowScreenLock, + prefs::kAllowScreenLock, base::Value::Type::BOOLEAN }, - { key::kQuickUnlockModeWhitelist, prefs::kQuickUnlockModeWhitelist, + { key::kQuickUnlockModeWhitelist, + prefs::kQuickUnlockModeWhitelist, base::Value::Type::LIST }, - { key::kQuickUnlockTimeout, prefs::kQuickUnlockTimeout, + { key::kQuickUnlockTimeout, + prefs::kQuickUnlockTimeout, base::Value::Type::INTEGER }, - { key::kPinUnlockMinimumLength, prefs::kPinUnlockMinimumLength, + { key::kPinUnlockMinimumLength, + prefs::kPinUnlockMinimumLength, base::Value::Type::INTEGER }, - { key::kPinUnlockMaximumLength, prefs::kPinUnlockMaximumLength, + { key::kPinUnlockMaximumLength, + prefs::kPinUnlockMaximumLength, base::Value::Type::INTEGER }, - { key::kPinUnlockWeakPinsAllowed, prefs::kPinUnlockWeakPinsAllowed, + { key::kPinUnlockWeakPinsAllowed, + prefs::kPinUnlockWeakPinsAllowed, base::Value::Type::BOOLEAN }, #endif + + { key::kRoamingProfileSupportEnabled, + syncer::prefs::kEnableLocalSyncBackend, + base::Value::Type::BOOLEAN }, + { key::kRoamingProfileLocation, + syncer::prefs::kLocalSyncBackendDir, + base::Value::Type::STRING }, }; class ForceSafeSearchPolicyHandler : public TypeCheckingPolicyHandler {
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc index 70c3131..7b292b3 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
@@ -3,12 +3,14 @@ // found in the LICENSE file. #include "base/command_line.h" +#include "chrome/browser/browsing_data/browsing_data_helper.h" +#include "chrome/browser/browsing_data/browsing_data_remover.h" +#include "chrome/browser/browsing_data/browsing_data_remover_factory.h" #include "chrome/browser/predictors/resource_prefetch_predictor.h" #include "chrome/browser/predictors/resource_prefetch_predictor_factory.h" #include "chrome/browser/predictors/resource_prefetch_predictor_test_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" @@ -92,6 +94,25 @@ DISALLOW_COPY_AND_ASSIGN(InitializationObserver); }; +class BrowsingDataRemoverObserver : public BrowsingDataRemover::Observer { + public: + explicit BrowsingDataRemoverObserver(BrowsingDataRemover* remover) + : remover_(remover) { + remover_->AddObserver(this); + } + ~BrowsingDataRemoverObserver() override { remover_->RemoveObserver(this); } + + void OnBrowsingDataRemoverDone() override { run_loop_.Quit(); } + + void Wait() { run_loop_.Run(); } + + private: + BrowsingDataRemover* remover_; + base::RunLoop run_loop_; + + DISALLOW_COPY_AND_ASSIGN(BrowsingDataRemoverObserver); +}; + using PageRequestSummary = ResourcePrefetchPredictor::PageRequestSummary; using URLRequestSummary = ResourcePrefetchPredictor::URLRequestSummary; @@ -222,7 +243,7 @@ void SetUpCommandLine(base::CommandLine* command_line) override { command_line->AppendSwitchASCII( switches::kSpeculativeResourcePrefetching, - switches::kSpeculativeResourcePrefetchingEnabled); + switches::kSpeculativeResourcePrefetchingEnabledExternal); } void SetUpOnMainThread() override { @@ -332,7 +353,14 @@ } void ClearCache() { - chrome::ClearCache(browser()); + BrowsingDataRemover* remover = + BrowsingDataRemoverFactory::GetForBrowserContext(browser()->profile()); + BrowsingDataRemoverObserver observer(remover); + remover->RemoveAndReply(BrowsingDataRemover::Unbounded(), + BrowsingDataRemover::REMOVE_CACHE, + BrowsingDataHelper::UNPROTECTED_WEB, &observer); + observer.Wait(); + for (auto& kv : resources_) kv.second.request.was_cached = false; }
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index a87a1a4..faf7b3ef 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -166,6 +166,7 @@ #if defined(OS_ANDROID) #include "chrome/browser/android/preferences/browser_prefs_android.h" #include "chrome/browser/ntp_snippets/download_suggestions_provider.h" +#include "components/ntp_snippets/category_rankers/click_based_category_ranker.h" #include "components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h" #include "components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h" #else @@ -590,6 +591,7 @@ #if defined(OS_ANDROID) DownloadSuggestionsProvider::RegisterProfilePrefs(registry); + ntp_snippets::ClickBasedCategoryRanker::RegisterProfilePrefs(registry); ntp_snippets::PhysicalWebPageSuggestionsProvider::RegisterProfilePrefs( registry); ntp_snippets::RecentTabSuggestionsProvider::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc index 9956202f..97a48ef5 100644 --- a/chrome/browser/prerender/prerender_browsertest.cc +++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -250,7 +250,6 @@ case FINAL_STATUS_DEVTOOLS_ATTACHED: case FINAL_STATUS_PAGE_BEING_CAPTURED: case FINAL_STATUS_NAVIGATION_UNCOMMITTED: - case FINAL_STATUS_WOULD_HAVE_BEEN_USED: case FINAL_STATUS_NON_EMPTY_BROWSING_INSTANCE: return false; default: @@ -1000,9 +999,6 @@ PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_USED, 1); EXPECT_EQ(1, GetPrerenderDomContentLoadedEventCountForLinkNumber(0)); histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1); - histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0); - histogram_tester().ExpectTotalCount( - "Prerender.none_PerceivedPLTMatchedComplete", 0); histogram_tester().ExpectTotalCount( "Prerender.websame_PrerenderNotSwappedInPLT", 1); @@ -1013,10 +1009,6 @@ channel_close_watcher.WaitForChannelClose(); histogram_tester().ExpectTotalCount("Prerender.websame_PerceivedPLT", 1); - histogram_tester().ExpectTotalCount("Prerender.websame_PerceivedPLTMatched", - 1); - histogram_tester().ExpectTotalCount( - "Prerender.websame_PerceivedPLTMatchedComplete", 1); ASSERT_TRUE(IsEmptyPrerenderLinkManager()); } @@ -1026,18 +1018,11 @@ PrerenderTestURL(GetCrossDomainTestUrl("prerender/prerender_page.html"), FINAL_STATUS_USED, 1); histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1); - histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0); - histogram_tester().ExpectTotalCount( - "Prerender.none_PerceivedPLTMatchedComplete", 0); histogram_tester().ExpectTotalCount( "Prerender.webcross_PrerenderNotSwappedInPLT", 1); NavigateToDestURL(); histogram_tester().ExpectTotalCount("Prerender.webcross_PerceivedPLT", 1); - histogram_tester().ExpectTotalCount("Prerender.webcross_PerceivedPLTMatched", - 1); - histogram_tester().ExpectTotalCount( - "Prerender.webcross_PerceivedPLTMatchedComplete", 1); } // Checks that pending prerenders launch and receive proper event treatment. @@ -1441,9 +1426,6 @@ EXPECT_EQ(1, prerender->number_of_loads()); histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1); - histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0); - histogram_tester().ExpectTotalCount( - "Prerender.none_PerceivedPLTMatchedComplete", 0); // Although there is a client redirect, it is dropped from histograms because // it is a Google URL. The target page itself does not load until after the // swap. @@ -1468,9 +1450,6 @@ histogram_tester().ExpectTotalCount("Prerender.gws_PrerenderNotSwappedInPLT", 0); histogram_tester().ExpectTotalCount("Prerender.gws_PerceivedPLT", 1); - histogram_tester().ExpectTotalCount("Prerender.gws_PerceivedPLTMatched", 1); - histogram_tester().ExpectTotalCount( - "Prerender.gws_PerceivedPLTMatchedComplete", 1); // The client redirect does /not/ count as a miss because it's a Google URL. histogram_tester().ExpectTotalCount("Prerender.PerceivedPLTFirstAfterMiss", @@ -2910,9 +2889,6 @@ EXPECT_TRUE(DidPrerenderPass(prerender->contents()->prerender_contents())); EXPECT_EQ(0, prerender->number_of_loads()); histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1); - histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0); - histogram_tester().ExpectTotalCount( - "Prerender.none_PerceivedPLTMatchedComplete", 0); histogram_tester().ExpectTotalCount( "Prerender.websame_PrerenderNotSwappedInPLT", 0); @@ -2933,10 +2909,6 @@ histogram_tester().ExpectTotalCount( "Prerender.websame_PrerenderNotSwappedInPLT", 0); histogram_tester().ExpectTotalCount("Prerender.websame_PerceivedPLT", 1); - histogram_tester().ExpectTotalCount("Prerender.websame_PerceivedPLTMatched", - 1); - histogram_tester().ExpectTotalCount( - "Prerender.websame_PerceivedPLTMatchedComplete", 1); } // Checks that a deferred redirect to an image is not loaded until the @@ -3085,9 +3057,6 @@ GURL url = embedded_test_server()->GetURL("/prerender/prerender_page.html"); ui_test_utils::NavigateToURL(current_browser(), url); histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLT", 1); - histogram_tester().ExpectTotalCount("Prerender.none_PerceivedPLTMatched", 0); - histogram_tester().ExpectTotalCount( - "Prerender.none_PerceivedPLTMatchedComplete", 0); } // Checks that a prerender which calls window.close() on itself is aborted.
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc index f7c11cf..b1e40f90 100644 --- a/chrome/browser/prerender/prerender_contents.cc +++ b/chrome/browser/prerender/prerender_contents.cc
@@ -367,8 +367,7 @@ prerender_manager_->RecordFinalStatus(origin(), final_status()); - bool used = final_status() == FINAL_STATUS_USED || - final_status() == FINAL_STATUS_WOULD_HAVE_BEEN_USED; + bool used = final_status() == FINAL_STATUS_USED; prerender_manager_->RecordNetworkBytes(origin(), used, network_bytes_); // Broadcast the removal of aliases.
diff --git a/chrome/browser/prerender/prerender_final_status.h b/chrome/browser/prerender/prerender_final_status.h index 9de710b..718eeac 100644 --- a/chrome/browser/prerender/prerender_final_status.h +++ b/chrome/browser/prerender/prerender_final_status.h
@@ -53,7 +53,7 @@ // Obsolete: FINAL_STATUS_MATCH_COMPLETE_DUMMY = 38, FINAL_STATUS_DUPLICATE = 39, FINAL_STATUS_OPEN_URL = 40, - FINAL_STATUS_WOULD_HAVE_BEEN_USED = 41, + // Obsolete: FINAL_STATUS_WOULD_HAVE_BEEN_USED = 41, FINAL_STATUS_REGISTER_PROTOCOL_HANDLER = 42, FINAL_STATUS_CREATING_AUDIO_STREAM = 43, FINAL_STATUS_PAGE_BEING_CAPTURED = 44,
diff --git a/chrome/browser/prerender/prerender_histograms.cc b/chrome/browser/prerender/prerender_histograms.cc index 4e97663..1e892bfd 100644 --- a/chrome/browser/prerender/prerender_histograms.cc +++ b/chrome/browser/prerender/prerender_histograms.cc
@@ -241,10 +241,6 @@ // (all prefixed PerceivedPLT) // PerceivedPLT -- Perceived Pageloadtimes (PPLT) for all pages in the group. // ...Windowed -- PPLT for pages in the 30s after a prerender is created. -// ...Matched -- A prerendered page that was swapped in. In the NoUse -// and Control group cases, while nothing ever gets swapped in, we do keep -// track of what would be prerendered and would be swapped in -- and those -// cases are what is classified as Match for these groups. // ...FirstAfterMiss -- First page to finish loading after a prerender, which // is different from the page that was prerendered. // ...FirstAfterMissNonOverlapping -- Same as FirstAfterMiss, but only @@ -270,15 +266,10 @@ if (within_window) RECORD_PLT("PerceivedPLTWindowed", perceived_page_load_time); if (navigation_type != NAVIGATION_TYPE_NORMAL) { - DCHECK(navigation_type == NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED || - navigation_type == NAVIGATION_TYPE_PRERENDERED); - RECORD_PLT("PerceivedPLTMatchedComplete", perceived_page_load_time); - if (navigation_type == NAVIGATION_TYPE_PRERENDERED) - RECORD_PLT("PerceivedPLTMatched", perceived_page_load_time); + DCHECK(navigation_type == NAVIGATION_TYPE_PRERENDERED); seen_any_pageload_ = true; seen_pageload_started_after_prerender_ = true; } else if (within_window) { - RECORD_PLT("PerceivedPLTWindowNotMatched", perceived_page_load_time); if (!is_google_url) { bool recorded_any = false; bool recorded_non_overlapping = false;
diff --git a/chrome/browser/prerender/prerender_histograms.h b/chrome/browser/prerender/prerender_histograms.h index 7fdc1875..ad2a9a8 100644 --- a/chrome/browser/prerender/prerender_histograms.h +++ b/chrome/browser/prerender/prerender_histograms.h
@@ -26,10 +26,6 @@ NAVIGATION_TYPE_NORMAL, // A completed navigation or swap that began as a prerender. NAVIGATION_TYPE_PRERENDERED, - // A normal completed navigation in the control group or with a control - // prerender that would have been prerendered. - NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED, - NAVIGATION_TYPE_MAX, }; // PrerenderHistograms is responsible for recording all prerender specific
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index f908885f..7102bb8 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc
@@ -412,15 +412,7 @@ return nullptr; } - // If we are just in the control group (which can be detected by noticing - // that prerendering hasn't even started yet), record that |web_contents| now - // would be showing a prerendered contents, but otherwise, don't do anything. - if (!prerender_data->contents()->prerendering_has_started()) { - target_tab_helper->WouldHavePrerenderedNextLoad( - prerender_data->contents()->origin()); - prerender_data->contents()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED); - return nullptr; - } + DCHECK(prerender_data->contents()->prerendering_has_started()); // Don't use prerendered pages if debugger is attached to the tab. // See http://crbug.com/98541
diff --git a/chrome/browser/prerender/prerender_tab_helper.cc b/chrome/browser/prerender/prerender_tab_helper.cc index a7450cac..52a669b 100644 --- a/chrome/browser/prerender/prerender_tab_helper.cc +++ b/chrome/browser/prerender/prerender_tab_helper.cc
@@ -24,8 +24,6 @@ PrerenderTabHelper::PrerenderTabHelper(content::WebContents* web_contents) : content::WebContentsObserver(web_contents), origin_(ORIGIN_NONE), - next_load_is_control_prerender_(false), - next_load_origin_(ORIGIN_NONE), weak_factory_(this) { // Determine if this is a prerender. PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); @@ -112,14 +110,6 @@ pplt_load_start_ = GetTimeTicksFromPrerenderManager(); actual_load_start_ = base::TimeTicks(); - if (next_load_is_control_prerender_) { - DCHECK_EQ(NAVIGATION_TYPE_NORMAL, navigation_type_); - navigation_type_ = NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED; - origin_ = next_load_origin_; - next_load_is_control_prerender_ = false; - next_load_origin_ = ORIGIN_NONE; - } - MainFrameUrlDidChange(validated_url); } @@ -167,11 +157,6 @@ } } -void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin) { - next_load_is_control_prerender_ = true; - next_load_origin_ = origin; -} - void PrerenderTabHelper::RecordPerceivedPageLoadTime( base::TimeDelta perceived_page_load_time, double fraction_plt_elapsed_at_swap_in) { @@ -180,10 +165,6 @@ if (!prerender_manager) return; - // Note: it is possible for |next_load_is_control_prerender_| to be true at - // this point. This does not affect the classification of the current load, - // but only the next load. (This occurs if a WOULD_HAVE_BEEN_PRERENDERED - // navigation interrupts and aborts another navigation.) prerender_manager->RecordPerceivedPageLoadTime( origin_, navigation_type_, perceived_page_load_time, fraction_plt_elapsed_at_swap_in, url_);
diff --git a/chrome/browser/prerender/prerender_tab_helper.h b/chrome/browser/prerender/prerender_tab_helper.h index 19fb16e3..bc5db07 100644 --- a/chrome/browser/prerender/prerender_tab_helper.h +++ b/chrome/browser/prerender/prerender_tab_helper.h
@@ -48,9 +48,6 @@ // Called when this prerendered WebContents has just been swapped in. void PrerenderSwappedIn(); - // Called when a control prerender is resolved. Applies to the next load. - void WouldHavePrerenderedNextLoad(Origin origin); - private: explicit PrerenderTabHelper(content::WebContents* web_contents); friend class content::WebContentsUserData<PrerenderTabHelper>; @@ -79,13 +76,6 @@ // relevant prerender. Otherwise, ORIGIN_NONE. Origin origin_; - // True if the next load will be associated with a control prerender. This - // extra state is needed because control prerenders are resolved before the - // actual load begins. |next_load_origin_| gives the origin of the control - // prerender. - bool next_load_is_control_prerender_; - Origin next_load_origin_; - // System time at which the current load was started for the purpose of // the perceived page load time (PPLT). If null, there is no current // load.
diff --git a/chrome/browser/resources/.clang-format b/chrome/browser/resources/.clang-format new file mode 100644 index 0000000..4d25b73 --- /dev/null +++ b/chrome/browser/resources/.clang-format
@@ -0,0 +1,13 @@ +BasedOnStyle: Chromium + +# Renaming quotes in <include> and <if> break things. +# For normal JS code, please prefer ' to ". +JavaScriptQuotes: Leave + +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +SpacesInContainerLiterals: false +SpacesInSquareBrackets: false
diff --git a/chrome/browser/resources/bluetooth_internals/adapter_broker.js b/chrome/browser/resources/bluetooth_internals/adapter_broker.js index c1cf8fa..13a8eee 100644 --- a/chrome/browser/resources/bluetooth_internals/adapter_broker.js +++ b/chrome/browser/resources/bluetooth_internals/adapter_broker.js
@@ -15,7 +15,7 @@ * handles and back when necessary. * @constructor * @extends {cr.EventTarget} - * @param {!interfaces.BluetoothAdapter.Adapter.proxyClass} adapter + * @param {!interfaces.BluetoothAdapter.AdapterPtr} adapter */ var AdapterBroker = function(adapter) { this.adapter_ = adapter; @@ -29,7 +29,7 @@ /** * Creates a GATT connection to the device with |address|. * @param {string} address - * @return {!Promise<!interfaces.BluetoothDevice.Device.proxyClass>} + * @return {!Promise<!interfaces.BluetoothDevice.DevicePtr>} */ connectToDevice: function(address) { return this.adapter_.connectToDevice(address).then(function(response) { @@ -45,9 +45,7 @@ throw new Error(errorString); } - return interfaces.Connection.bindHandleToProxy( - response.device.ptr.passInterface().handle, - interfaces.BluetoothDevice.Device); + return response.device; }); }, @@ -145,14 +143,9 @@ if (adapterBroker) return Promise.resolve(adapterBroker); return interfaces.setupInterfaces().then(function(adapter) { - // Hook up the instance properties. - AdapterClient.prototype.__proto__ = - interfaces.BluetoothAdapter.AdapterClient.stubClass.prototype; - - var adapterFactory = interfaces.Connection.bindHandleToProxy( + var adapterFactory = new interfaces.BluetoothAdapter.AdapterFactoryPtr( interfaces.FrameInterfaces.getInterface( - interfaces.BluetoothAdapter.AdapterFactory.name), - interfaces.BluetoothAdapter.AdapterFactory); + interfaces.BluetoothAdapter.AdapterFactory.name)); // Get an Adapter service. return adapterFactory.getAdapter(); @@ -161,11 +154,7 @@ throw new Error('Bluetooth Not Supported on this platform.'); } - var adapter = interfaces.Connection.bindHandleToProxy( - response.adapter.ptr.passInterface().handle, - interfaces.BluetoothAdapter.Adapter); - - adapterBroker = new AdapterBroker(adapter); + adapterBroker = new AdapterBroker(response.adapter); return adapterBroker; }); }
diff --git a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js index a87c537..fe2167d 100644 --- a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js +++ b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js
@@ -41,7 +41,7 @@ }, }; - /** @type {!Map<string, !interfaces.BluetoothDevice.Device.proxyClass>} */ + /** @type {!Map<string, !interfaces.BluetoothDevice.DevicePtr>} */ var deviceAddressToProxy = new Map(); /** @type {!device_collection.DeviceCollection} */
diff --git a/chrome/browser/resources/bluetooth_internals/interfaces.js b/chrome/browser/resources/bluetooth_internals/interfaces.js index 0bbe9cb..20eb2b8b 100644 --- a/chrome/browser/resources/bluetooth_internals/interfaces.js +++ b/chrome/browser/resources/bluetooth_internals/interfaces.js
@@ -26,13 +26,11 @@ 'device/bluetooth/public/interfaces/adapter.mojom', 'device/bluetooth/public/interfaces/device.mojom', 'mojo/public/js/bindings', - 'mojo/public/js/connection', ]).then(function([frameInterfaces, bluetoothAdapter, bluetoothDevice, - bindings, connection]) { + bindings]) { interfaces.BluetoothAdapter = bluetoothAdapter; interfaces.BluetoothDevice = bluetoothDevice; interfaces.Bindings = bindings; - interfaces.Connection = connection; interfaces.FrameInterfaces = frameInterfaces; }); });
diff --git a/chrome/browser/resources/engagement/site_engagement.js b/chrome/browser/resources/engagement/site_engagement.js index bd19f11..3557fd0 100644 --- a/chrome/browser/resources/engagement/site_engagement.js +++ b/chrome/browser/resources/engagement/site_engagement.js
@@ -5,15 +5,13 @@ 'use strict'; define('main', [ - 'mojo/public/js/connection', 'chrome/browser/ui/webui/engagement/site_engagement.mojom', 'content/public/renderer/frame_interfaces', -], function(connection, siteEngagementMojom, frameInterfaces) { +], function(siteEngagementMojom, frameInterfaces) { return function() { - var uiHandler = connection.bindHandleToProxy( + var uiHandler = new siteEngagementMojom.SiteEngagementUIHandlerPtr( frameInterfaces.getInterface( - siteEngagementMojom.SiteEngagementUIHandler.name), - siteEngagementMojom.SiteEngagementUIHandler); + siteEngagementMojom.SiteEngagementUIHandler.name)); var engagementTableBody = $('engagement-table-body'); var updateInterval = null;
diff --git a/chrome/browser/resources/omnibox/omnibox.js b/chrome/browser/resources/omnibox/omnibox.js index 11c4a993..2570deb 100644 --- a/chrome/browser/resources/omnibox/omnibox.js +++ b/chrome/browser/resources/omnibox/omnibox.js
@@ -416,18 +416,15 @@ function initializeProxies() { return importModules([ 'mojo/public/js/bindings', - 'mojo/public/js/connection', 'chrome/browser/ui/webui/omnibox/omnibox.mojom', 'content/public/renderer/frame_interfaces', ]).then(function(modules) { var bindings = modules[0]; - var connection = modules[1]; - var mojom = modules[2]; - var frameInterfaces = modules[3]; + var mojom = modules[1]; + var frameInterfaces = modules[2]; - browserProxy = connection.bindHandleToProxy( - frameInterfaces.getInterface(mojom.OmniboxPageHandler.name), - mojom.OmniboxPageHandler); + browserProxy = new mojom.OmniboxPageHandlerPtr( + frameInterfaces.getInterface(mojom.OmniboxPageHandler.name)); /** @constructor */ var OmniboxPageImpl = function() {
diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js index 835f8d5..73d37ff 100644 --- a/chrome/browser/resources/options/browser_options.js +++ b/chrome/browser/resources/options/browser_options.js
@@ -1226,6 +1226,9 @@ $('sync-action-link').onclick = function(event) { switch (syncData.statusAction) { case 'reauthenticate': + SyncSetupOverlay.startSignIn(false /* creatingSupervisedUser */); + break; + case 'signOutAndSignIn': <if expr="chromeos"> // On Chrome OS, sign out the user and sign in again to get fresh // credentials on auth errors.
diff --git a/chrome/browser/resources/options/chromeos/stylus_overlay.html b/chrome/browser/resources/options/chromeos/stylus_overlay.html index dff83f31..9e0cc025 100644 --- a/chrome/browser/resources/options/chromeos/stylus_overlay.html +++ b/chrome/browser/resources/options/chromeos/stylus_overlay.html
@@ -19,6 +19,13 @@ <span i18n-content="stylusAutoOpenStylusTools"></span> </label> </div> + <div class="controlled-setting-with-label"> + <label> + <span class="option-name" i18n-content="stylusNoteTakingApp"></span> + <select id="stylus-note-taking-app-select" class="option-value"> + </select> + </label> + </div> </div> </div>
diff --git a/chrome/browser/resources/options/chromeos/stylus_overlay.js b/chrome/browser/resources/options/chromeos/stylus_overlay.js index 36b4160..9462dc2 100644 --- a/chrome/browser/resources/options/chromeos/stylus_overlay.js +++ b/chrome/browser/resources/options/chromeos/stylus_overlay.js
@@ -21,18 +21,129 @@ StylusOverlay.prototype = { __proto__: options.SettingsDialog.prototype, + /** + * True if the "enable stylus tools" pref is set to true. + * @type {boolean} + */ + stylusToolsEnabled_: true, + + /** + * True if the last call to updateNoteTakingApps_() passed at least one + * available note-taking app and indicated that we are not waiting for + * Android to start. + * @type {boolean} + */ + noteTakingAppsAvailable_: false, + + /** + * Note-taking app ID selected by the user, or null if the user didn't + * change the selection. + * @type {?string} + */ + selectedNoteTakingAppId_: null, + /** @override */ initializePage: function() { options.SettingsDialog.prototype.initializePage.call(this); - // Disable auto-launch pref when enable stylus tools pref is false. + // Disable some elements when enable stylus tools pref is false. Preferences.getInstance().addEventListener('settings.enable_stylus_tools', function(e) { + this.stylusToolsEnabled_ = e.value.value; $('launch-palette-on-eject-input').disabled = !e.value.value; - }); + this.updateNoteTakingAppsSelectDisabled_(); + }.bind(this)); + + $('stylus-note-taking-app-select') + .addEventListener( + 'change', this.handleNoteTakingAppSelected_.bind(this)); + }, + + /** @override */ + handleConfirm: function() { + options.SettingsDialog.prototype.handleConfirm.call(this); + + if (this.selectedNoteTakingAppId_) { + chrome.send( + 'setPreferredNoteTakingApp', [this.selectedNoteTakingAppId_]); + this.selectedNoteTakingAppId_ = null; + } + }, + + /** @override */ + handleCancel: function() { + options.SettingsDialog.prototype.handleCancel.call(this); + + var selectOptions = $('stylus-note-taking-app-select').children; + for (var i = 0; i < selectOptions.length; i++) { + selectOptions[i].selected = selectOptions[i].defaultSelected; + } + }, + + /** + * Updates the list of available note-taking apps. Called from C++. + * @param {Array<{name: string, id: string, preferred: boolean}>} + * apps Array of app info structs containing: + * name App name to display to user. + * id Opaque app ID. + * preferred Whether this is the preferred app. + * @param {boolean} waitingForAndroid True if Android is starting. + * @private + */ + updateNoteTakingApps_: function(apps, waitingForAndroid) { + var element = $('stylus-note-taking-app-select'); + + // Clear any existing options and make sure we don't save an old + // selection when "OK" is clicked. + element.textContent = ''; + this.noteTakingAppsAvailable_ = false; + this.selectedNoteTakingAppId_ = null; + + // Disable the menu and use it to display an informative message if + // Android is starting or no note-taking apps are installed. + if (waitingForAndroid) { + element.appendChild(new Option( + loadTimeData.getString('stylusNoteTakingAppWaitingForAndroid'), '', + false, false)); + } else if (apps.length == 0) { + element.appendChild(new Option( + loadTimeData.getString('stylusNoteTakingAppNoneAvailable'), '', + false, false)); + } else { + for (var i = 0; i < apps.length; i++) { + var app = apps[i]; + element.appendChild( + new Option(app.name, app.id, app.preferred, app.preferred)); + } + this.noteTakingAppsAvailable_ = true; + } + + this.updateNoteTakingAppsSelectDisabled_(); + }, + + /** + * Updates the disabled state of the note-taking app select element. + * @private + */ + updateNoteTakingAppsSelectDisabled_: function() { + $('stylus-note-taking-app-select').disabled = + !this.stylusToolsEnabled_ || !this.noteTakingAppsAvailable_; + }, + + /** + * Records the user's selection of a note-taking app so it can be saved when + * the "OK" button is clicked. + * @private + */ + handleNoteTakingAppSelected_: function() { + this.selectedNoteTakingAppId_ = $('stylus-note-taking-app-select').value; }, }; + cr.makePublic(StylusOverlay, [ + 'updateNoteTakingApps', + ]); + // Export return { StylusOverlay: StylusOverlay
diff --git a/chrome/browser/resources/pdf/gesture_detector.js b/chrome/browser/resources/pdf/gesture_detector.js index a64729e..fde4650d 100644 --- a/chrome/browser/resources/pdf/gesture_detector.js +++ b/chrome/browser/resources/pdf/gesture_detector.js
@@ -16,10 +16,14 @@ constructor(element) { this.element_ = element; - this.element_.addEventListener('touchstart', this.onTouchStart_.bind(this)); - this.element_.addEventListener('touchmove', this.onTouch_.bind(this)); - this.element_.addEventListener('touchend', this.onTouch_.bind(this)); - this.element_.addEventListener('touchcancel', this.onTouch_.bind(this)); + this.element_.addEventListener( + 'touchstart', this.onTouchStart_.bind(this), { passive: false }); + this.element_.addEventListener( + 'touchmove', this.onTouch_.bind(this), { passive: true }); + this.element_.addEventListener( + 'touchend', this.onTouch_.bind(this), { passive: true }); + this.element_.addEventListener( + 'touchcancel', this.onTouch_.bind(this), { passive: true }); this.pinchStartEvent_ = null; this.lastEvent_ = null;
diff --git a/chrome/browser/resources/plugins.js b/chrome/browser/resources/plugins.js index bffd6bb..f57f133 100644 --- a/chrome/browser/resources/plugins.js +++ b/chrome/browser/resources/plugins.js
@@ -228,18 +228,15 @@ function initializeProxies() { return importModules([ 'mojo/public/js/bindings', - 'mojo/public/js/connection', 'chrome/browser/ui/webui/plugins/plugins.mojom', 'content/public/renderer/frame_interfaces', ]).then(function(modules) { var bindings = modules[0]; - var connection = modules[1]; - var pluginsMojom = modules[2]; - var frameInterfaces = modules[3]; + var pluginsMojom = modules[1]; + var frameInterfaces = modules[2]; - browserProxy = connection.bindHandleToProxy( - frameInterfaces.getInterface(pluginsMojom.PluginsPageHandler.name), - pluginsMojom.PluginsPageHandler); + browserProxy = new pluginsMojom.PluginsPageHandlerPtr( + frameInterfaces.getInterface(pluginsMojom.PluginsPageHandler.name)); /** @constructor */ var PluginsPageImpl = function() {
diff --git a/chrome/browser/resources/settings/people_page/import_data_dialog.js b/chrome/browser/resources/settings/people_page/import_data_dialog.js index e035c28..058a27f 100644 --- a/chrome/browser/resources/settings/people_page/import_data_dialog.js +++ b/chrome/browser/resources/settings/people_page/import_data_dialog.js
@@ -58,6 +58,10 @@ function(data) { this.browserProfiles_ = data; this.selected_ = this.browserProfiles_[0]; + + // Show the dialog only after the browser profiles data is populated + // to avoid UI flicker. + this.$.dialog.showModal(); }.bind(this)); this.addWebUIListener( @@ -68,8 +72,6 @@ if (this.hasImportStatus_(settings.ImportDataStatus.FAILED)) this.closeDialog_(); }.bind(this)); - - this.$.dialog.showModal(); }, /** @private */
diff --git a/chrome/browser/resources/settings/people_page/people_page.js b/chrome/browser/resources/settings/people_page/people_page.js index 1a18e028..5698401 100644 --- a/chrome/browser/resources/settings/people_page/people_page.js +++ b/chrome/browser/resources/settings/people_page/people_page.js
@@ -338,6 +338,9 @@ switch (this.syncStatus.statusAction) { case settings.StatusAction.REAUTHENTICATE: + this.syncBrowserProxy_.startSignIn(); + break; + case settings.StatusAction.SIGNOUT_AND_SIGNIN: <if expr="chromeos"> this.syncBrowserProxy_.attemptUserExit(); </if>
diff --git a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js index f71f2ad0..be5c2b6 100644 --- a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js +++ b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
@@ -35,10 +35,11 @@ * @enum {string} */ settings.StatusAction = { - NO_ACTION: 'noAction', // No action to take. - REAUTHENTICATE: 'reauthenticate', // User needs to reauthenticate. - UPGRADE_CLIENT: 'upgradeClient', // User needs to upgrade the client. - ENTER_PASSPHRASE: 'enterPassphrase', // User needs to enter passphrase. + NO_ACTION: 'noAction', // No action to take. + REAUTHENTICATE: 'reauthenticate', // User needs to reauthenticate. + SIGNOUT_AND_SIGNIN: 'signOutAndSignIn', // User needs to sign out and sign in. + UPGRADE_CLIENT: 'upgradeClient', // User needs to upgrade the client. + ENTER_PASSPHRASE: 'enterPassphrase', // User needs to enter passphrase. }; /** @@ -180,10 +181,7 @@ <if expr="not chromeos"> /** @override */ startSignIn: function() { - // TODO(tommycli): Currently this is always false, but this will become - // a parameter once supervised users are implemented in MD Settings. - var creatingSupervisedUser = false; - chrome.send('SyncSetupStartSignIn', [creatingSupervisedUser]); + chrome.send('SyncSetupStartSignIn'); }, /** @override */
diff --git a/chrome/browser/resources/usb_internals/usb_internals.js b/chrome/browser/resources/usb_internals/usb_internals.js index a1c1a28..bed9e59 100644 --- a/chrome/browser/resources/usb_internals/usb_internals.js +++ b/chrome/browser/resources/usb_internals/usb_internals.js
@@ -61,17 +61,14 @@ function initializeProxies() { return importModules([ - 'mojo/public/js/connection', 'chrome/browser/ui/webui/usb_internals/usb_internals.mojom', 'content/public/renderer/frame_interfaces', ]).then(function(modules) { - let connection = modules[0]; - let mojom = modules[1]; - let frameInterfaces = modules[2]; + let mojom = modules[0]; + let frameInterfaces = modules[1]; - pageHandler = connection.bindHandleToProxy( - frameInterfaces.getInterface(mojom.UsbInternalsPageHandler.name), - mojom.UsbInternalsPageHandler); + pageHandler = new mojom.UsbInternalsPageHandlerPtr( + frameInterfaces.getInterface(mojom.UsbInternalsPageHandler.name)); }); }
diff --git a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc index de3332f3..740af35 100644 --- a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc +++ b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
@@ -394,11 +394,8 @@ false /* expect cert status error */); } -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, SHA1Broken) { +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, SHA1Certificate) { ASSERT_TRUE(https_server_.Start()); - // The test server uses a long-lived cert by default, so a SHA1 - // signature in it will register as a "broken" condition rather than - // "warning". SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, net::OK); @@ -406,7 +403,7 @@ https_server_.GetURL("/ssl/google.html")); CheckSecurityInfoForSecure( browser()->tab_strip_model()->GetActiveWebContents(), - security_state::DANGEROUS, security_state::DEPRECATED_SHA1_MAJOR, + security_state::NONE, security_state::DEPRECATED_SHA1_MAJOR, security_state::CONTENT_STATUS_NONE, false, false /* expect cert status error */); } @@ -591,12 +588,9 @@ } // Same as the test above but with a long-lived SHA1 cert. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, MixedContentWithBrokenSHA1) { +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, MixedContentWithSHA1Cert) { ASSERT_TRUE(embedded_test_server()->Start()); ASSERT_TRUE(https_server_.Start()); - // The test server uses a long-lived cert by default, so a SHA1 - // signature in it will register as a "broken" condition rather than - // "warning". SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, net::OK); @@ -615,7 +609,7 @@ https_server_.GetURL(replacement_path)); CheckSecurityInfoForSecure( browser()->tab_strip_model()->GetActiveWebContents(), - security_state::DANGEROUS, security_state::DEPRECATED_SHA1_MAJOR, + security_state::NONE, security_state::DEPRECATED_SHA1_MAJOR, security_state::CONTENT_STATUS_DISPLAYED, false, false /* expect cert status error */); @@ -627,7 +621,7 @@ https_server_.GetURL(replacement_path)); CheckSecurityInfoForSecure( browser()->tab_strip_model()->GetActiveWebContents(), - security_state::DANGEROUS, security_state::DEPRECATED_SHA1_MAJOR, + security_state::NONE, security_state::DEPRECATED_SHA1_MAJOR, security_state::CONTENT_STATUS_NONE, false, false /* expect cert status error */); // Load the insecure image. @@ -638,7 +632,7 @@ EXPECT_TRUE(js_result); CheckSecurityInfoForSecure( browser()->tab_strip_model()->GetActiveWebContents(), - security_state::DANGEROUS, security_state::DEPRECATED_SHA1_MAJOR, + security_state::NONE, security_state::DEPRECATED_SHA1_MAJOR, security_state::CONTENT_STATUS_DISPLAYED, false, false /* expect cert status error */);
diff --git a/chrome/browser/sync/sync_ui_util.h b/chrome/browser/sync/sync_ui_util.h index ca78bd9..9f976db 100644 --- a/chrome/browser/sync/sync_ui_util.h +++ b/chrome/browser/sync/sync_ui_util.h
@@ -29,10 +29,11 @@ // The action associated with the sync status. enum ActionType { - NO_ACTION, // No action to take. - REAUTHENTICATE, // User needs to reauthenticate. - UPGRADE_CLIENT, // User needs to upgrade the client. - ENTER_PASSPHRASE, // User needs to enter their passphrase. + NO_ACTION, // No action to take. + REAUTHENTICATE, // User needs to reauthenticate. + SIGNOUT_AND_SIGNIN, // User needs to sign out and sign in. + UPGRADE_CLIENT, // User needs to upgrade the client. + ENTER_PASSPHRASE, // User needs to enter their passphrase. }; enum StatusLabelStyle {
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc index 2464dfa..844ad12 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
@@ -53,9 +53,12 @@ namespace { -const char kAAccountIdString[] = "{\"email\":\"A\",\"gaia_id\":\"\"}"; -const char kBAccountIdString[] = "{\"email\":\"B\",\"gaia_id\":\"\"}"; -const char kArrowBAccountIdString[] = "->{\"email\":\"B\",\"gaia_id\":\"\"}"; +const char kAAccountIdString[] = + "{\"account_type\":\"unknown\",\"email\":\"A\"}"; +const char kBAccountIdString[] = + "{\"account_type\":\"unknown\",\"email\":\"B\"}"; +const char kArrowBAccountIdString[] = + "->{\"account_type\":\"unknown\",\"email\":\"B\"}"; // TOOD(beng): This implementation seems only superficially different to the // production impl. Evaluate whether or not we can just use that
diff --git a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm index 8b72196..19d1190 100644 --- a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm +++ b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm
@@ -38,6 +38,7 @@ [label setDrawsBackground:NO]; [label setBezeled:NO]; [label setStringValue:title]; + [label setAlignment:NSNaturalTextAlignment]; [label setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; [label sizeToFit]; [view addSubview:label.get()];
diff --git a/chrome/browser/ui/cocoa/passwords/confirmation_password_saved_view_controller.mm b/chrome/browser/ui/cocoa/passwords/confirmation_password_saved_view_controller.mm index 9727315c..fe02247 100644 --- a/chrome/browser/ui/cocoa/passwords/confirmation_password_saved_view_controller.mm +++ b/chrome/browser/ui/cocoa/passwords/confirmation_password_saved_view_controller.mm
@@ -13,6 +13,7 @@ #include "chrome/grit/generated_resources.h" #include "components/strings/grit/components_strings.h" #include "skia/ext/skia_utils_mac.h" +#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" #import "ui/base/cocoa/controls/hyperlink_text_view.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" @@ -64,6 +65,10 @@ NSTextField* titleLabel = [self addTitleLabel:base::SysUTF16ToNSString(self.model->title()) toView:view]; + // Title should occupy the whole width to that it's aligned properly for RTL. + [titleLabel setFrameSize:NSMakeSize(kDesiredBubbleWidth - 2 * kFramePadding, + 0)]; + [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleLabel]; // Text. confirmationText_.reset([[HyperlinkTextView alloc] initWithFrame:NSZeroRect]);
diff --git a/chrome/browser/ui/cocoa/passwords/credential_item_button.mm b/chrome/browser/ui/cocoa/passwords/credential_item_button.mm index 27c200d..24d401e 100644 --- a/chrome/browser/ui/cocoa/passwords/credential_item_button.mm +++ b/chrome/browser/ui/cocoa/passwords/credential_item_button.mm
@@ -4,6 +4,7 @@ #import "chrome/browser/ui/cocoa/passwords/credential_item_button.h" +#include "base/i18n/rtl.h" #import "base/mac/scoped_nsobject.h" #include "base/strings/sys_string_conversions.h" #include "chrome/browser/ui/cocoa/passwords/passwords_bubble_utils.h" @@ -50,7 +51,8 @@ // in -drawImage, so it must be added when drawing the title. NSRect marginRect; NSDivideRect(frame, &marginRect, &frame, marginSpacing_, NSMinXEdge); - NSDivideRect(frame, &marginRect, &frame, imageTitleSpacing_, NSMinXEdge); + NSDivideRect(frame, &marginRect, &frame, imageTitleSpacing_, + base::i18n::IsRTL() ? NSMaxXEdge : NSMinXEdge); NSDivideRect(frame, &marginRect, &frame, marginSpacing_, NSMaxXEdge); return [super drawTitle:title withFrame:frame inView:controlView]; @@ -59,7 +61,10 @@ - (void)drawImage:(NSImage*)image withFrame:(NSRect)frame inView:(NSView*)controlView { - frame.origin.x = marginSpacing_; + if (base::i18n::IsRTL()) + frame.origin.x -= marginSpacing_; + else + frame.origin.x += marginSpacing_; gfx::ScopedNSGraphicsContextSaveGState scopedGState; NSBezierPath* path = [NSBezierPath bezierPathWithOvalInRect:frame]; [path addClip]; @@ -116,8 +121,7 @@ .GetPrimaryFont() .GetNativeFont()]; [self setButtonType:NSMomentaryLightButton]; - [self setImagePosition:NSImageLeft]; - [self setAlignment:NSLeftTextAlignment]; + [self setImagePosition:base::i18n::IsRTL() ? NSImageRight : NSImageLeft]; } return self; }
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_view_controller.mm index 5e25aac3..6436e43 100644 --- a/chrome/browser/ui/cocoa/passwords/manage_passwords_view_controller.mm +++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_view_controller.mm
@@ -84,10 +84,8 @@ [view addSubview:contentView]; // Wrap the title if necessary to match the width of the content view. - if (NSWidth([titleLabel frame]) > NSWidth([contentView frame])) { - [titleLabel setFrameSize:NSMakeSize(NSWidth([contentView frame]), 0)]; - [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleLabel]; - } + [titleLabel setFrameSize:NSMakeSize(NSWidth([contentView frame]), 0)]; + [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleLabel]; // Done button. doneButton_.reset([[self addButton:l10n_util::GetNSString(IDS_DONE)
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc index 595fdf7..7879306 100644 --- a/chrome/browser/ui/startup/startup_browser_creator.cc +++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -262,34 +262,36 @@ static_cast<int>(output_string.size())); } -// Shows the User Manager on startup if the last used profile must sign in or -// if the last used profile was the guest or system profile. -// Returns true if the User Manager was shown, false otherwise. -bool ShowUserManagerOnStartupIfNeeded( - Profile* last_used_profile, const base::CommandLine& command_line) { +// Returns whether |profile| can be opened during Chrome startup without +// explicit user action. +bool ProfileCanBeAutoOpened(Profile* profile) { #if defined(OS_CHROMEOS) - // ChromeOS never shows the User Manager on startup. - return false; + // On ChromeOS, ther user has alrady chosen and logged into the profile + // before Chrome starts up. + return true; #else + // Profiles that require signin are not available. ProfileAttributesEntry* entry = nullptr; - bool has_entry = - g_browser_process->profile_manager() + if (g_browser_process->profile_manager() ->GetProfileAttributesStorage() - .GetProfileAttributesWithPath(last_used_profile->GetPath(), &entry); - - if (!has_entry || !entry->IsSigninRequired()) { - // Signin is not required. However, guest, system or locked profiles cannot - // be re-opened on startup. The only exception is if there's already a Guest - // window open in a separate process (for example, launching a new browser - // after clicking on a downloaded file in Guest mode). - if ((!last_used_profile->IsGuestSession() && - !last_used_profile->IsSystemProfile()) || - (chrome::GetBrowserCount(last_used_profile->GetOffTheRecordProfile()) > - 0)) { - return false; - } + .GetProfileAttributesWithPath(profile->GetPath(), &entry) && + entry->IsSigninRequired()) { + return false; } + // Guest or system profiles are not available unless a separate process + // already has a window open for the profile. + return (!profile->IsGuestSession() && !profile->IsSystemProfile()) || + (chrome::GetBrowserCount(profile->GetOffTheRecordProfile()) > 0); +#endif +} + +// Returns whether the User Manager was shown. +bool ShowUserManagerOnStartupIfNeeded(Profile* last_used_profile, + const base::CommandLine& command_line) { + if (ProfileCanBeAutoOpened(last_used_profile)) + return false; + // Show the User Manager. profiles::UserManagerAction action = command_line.HasSwitch(switches::kShowAppList) ? @@ -298,7 +300,6 @@ UserManager::Show( base::FilePath(), profiles::USER_MANAGER_NO_TUTORIAL, action); return true; -#endif } } // namespace @@ -588,11 +589,16 @@ } bool silent_launch = false; + bool can_use_last_profile = + (ProfileCanBeAutoOpened(last_used_profile) && + !IncognitoModePrefs::ShouldLaunchIncognito( + command_line, last_used_profile->GetPrefs())); #if BUILDFLAG(ENABLE_PRINT_PREVIEW) // If we are just displaying a print dialog we shouldn't open browser // windows. if (command_line.HasSwitch(switches::kCloudPrintFile) && + can_use_last_profile && print_dialog_cloud::CreatePrintDialogFromCommandLine(last_used_profile, command_line)) { silent_launch = true; @@ -684,8 +690,7 @@ return true; if (command_line.HasSwitch(extensions::switches::kLoadApps) && - !IncognitoModePrefs::ShouldLaunchIncognito( - command_line, last_used_profile->GetPrefs())) { + can_use_last_profile) { if (!ProcessLoadApps(command_line, cur_dir, last_used_profile)) return false; @@ -699,9 +704,7 @@ } // Check for --load-and-launch-app. - if (command_line.HasSwitch(apps::kLoadAndLaunchApp) && - !IncognitoModePrefs::ShouldLaunchIncognito( - command_line, last_used_profile->GetPrefs())) { + if (command_line.HasSwitch(apps::kLoadAndLaunchApp) && can_use_last_profile) { base::CommandLine::StringType path = command_line.GetSwitchValueNative(apps::kLoadAndLaunchApp);
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc index 4d1ef42..d4d62e3 100644 --- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc +++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -74,7 +74,7 @@ } ui::ModalType SigninViewControllerDelegateViews::GetModalType() const { - return ui::MODAL_TYPE_CHILD; + return ui::MODAL_TYPE_WINDOW; } bool SigninViewControllerDelegateViews::ShouldShowCloseButton() const { @@ -107,9 +107,12 @@ } void SigninViewControllerDelegateViews::DisplayModal() { - modal_signin_widget_ = constrained_window::ShowWebModalDialogViews( - this, browser_->tab_strip_model()->GetActiveWebContents()); - content_view_->RequestFocus(); + gfx::NativeWindow window = browser_->tab_strip_model() + ->GetActiveWebContents() + ->GetTopLevelNativeWindow(); + modal_signin_widget_ = + constrained_window::CreateBrowserModalDialogViews(this, window); + modal_signin_widget_->Show(); } // static
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc index 5660e1b..460bf6e 100644 --- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -88,9 +88,9 @@ // If there's a populated email, we must check first that this user is using // SAML in order to decide whether to show the interstitial page. - const user_manager::User* user = - user_manager::UserManager::Get()->FindUser( - user_manager::known_user::GetAccountId(email, std::string())); + const user_manager::User* user = user_manager::UserManager::Get()->FindUser( + user_manager::known_user::GetAccountId(email, std::string() /* id */, + AccountType::UNKNOWN)); if (user && user->using_saml()) return GAIA_SCREEN_MODE_SAML_INTERSTITIAL; @@ -406,7 +406,7 @@ void GaiaScreenHandler::HandleIdentifierEntered(const std::string& user_email) { if (!Delegate()->IsUserWhitelisted(user_manager::known_user::GetAccountId( - user_email, std::string() /* gaia_id */))) + user_email, std::string() /* id */, AccountType::UNKNOWN))) ShowWhitelistCheckFailedError(); } @@ -451,12 +451,13 @@ AccountId GaiaScreenHandler::GetAccountId( const std::string& authenticated_email, - const std::string& gaia_id) const { + const std::string& id, + const AccountType& account_type) const { const std::string canonicalized_email = gaia::CanonicalizeEmail(gaia::SanitizeEmail(authenticated_email)); - const AccountId account_id = - user_manager::known_user::GetAccountId(authenticated_email, gaia_id); + const AccountId account_id = user_manager::known_user::GetAccountId( + authenticated_email, id, account_type); if (account_id.GetUserEmail() != canonicalized_email) { LOG(WARNING) << "Existing user '" << account_id.GetUserEmail() @@ -481,7 +482,7 @@ const std::string sanitized_email = gaia::SanitizeEmail(email); Delegate()->SetDisplayEmail(sanitized_email); - UserContext user_context(GetAccountId(email, gaia_id)); + UserContext user_context(GetAccountId(email, gaia_id, AccountType::GOOGLE)); user_context.SetKey(Key(password)); user_context.SetAuthCode(auth_code); user_context.SetAuthFlow(using_saml @@ -566,7 +567,8 @@ DCHECK(!gaia_id.empty()); const std::string sanitized_email = gaia::SanitizeEmail(typed_email); Delegate()->SetDisplayEmail(sanitized_email); - UserContext user_context(GetAccountId(typed_email, gaia_id)); + UserContext user_context( + GetAccountId(typed_email, gaia_id, AccountType::GOOGLE)); user_context.SetKey(Key(password)); user_context.SetAuthFlow(using_saml ? UserContext::AUTH_FLOW_GAIA_WITH_SAML @@ -705,7 +707,7 @@ Delegate()->LoadSigninWallpaper(); } else { Delegate()->LoadWallpaper(user_manager::known_user::GetAccountId( - populated_email_, std::string())); + populated_email_, std::string() /* id */, AccountType::UNKNOWN)); } input_method::InputMethodManager* imm =
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h index 21c5497..45d3fa2 100644 --- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -179,7 +179,8 @@ // Returns user canonical e-mail. Finds already used account alias, if // user has already signed in. AccountId GetAccountId(const std::string& authenticated_email, - const std::string& gaia_id) const; + const std::string& id, + const AccountType& account_type) const; bool offline_login_is_active() const { return offline_login_is_active_; } void set_offline_login_is_active(bool offline_login_is_active) {
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc index 8627e66..77eb09d 100644 --- a/chrome/browser/ui/webui/options/browser_options_handler.cc +++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -184,6 +184,8 @@ switch (action_type) { case sync_ui_util::REAUTHENTICATE: return "reauthenticate"; + case sync_ui_util::SIGNOUT_AND_SIGNIN: + return "signOutAndSignIn"; case sync_ui_util::UPGRADE_CLIENT: return "upgradeClient"; case sync_ui_util::ENTER_PASSPHRASE:
diff --git a/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc b/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc index 63b9768..d832ba9 100644 --- a/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc
@@ -5,15 +5,30 @@ #include "chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h" #include "ash/common/system/chromeos/palette/palette_utils.h" +#include "base/values.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" namespace chromeos { namespace options { -OptionsStylusHandler::OptionsStylusHandler() {} +namespace { -OptionsStylusHandler::~OptionsStylusHandler() {} +// Keys in objects passed to updateNoteTakingApps_. +constexpr char kAppNameKey[] = "name"; +constexpr char kAppIdKey[] = "id"; +constexpr char kAppPreferredKey[] = "preferred"; + +} // namespace + +OptionsStylusHandler::OptionsStylusHandler() : weak_ptr_factory_(this) { + NoteTakingHelper::Get()->AddObserver(this); +} + +OptionsStylusHandler::~OptionsStylusHandler() { + NoteTakingHelper::Get()->RemoveObserver(this); +} void OptionsStylusHandler::GetLocalizedValues( base::DictionaryValue* localized_strings) { @@ -34,14 +49,82 @@ localized_strings->SetString( "stylusFindMoreApps", l10n_util::GetStringUTF16(IDS_SETTINGS_STYLUS_FIND_MORE_APPS)); + localized_strings->SetString( + "stylusNoteTakingApp", + l10n_util::GetStringUTF16(IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_LABEL)); + localized_strings->SetString( + "stylusNoteTakingAppNoneAvailable", + l10n_util::GetStringUTF16( + IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_NONE_AVAILABLE)); + localized_strings->SetString( + "stylusNoteTakingAppWaitingForAndroid", + l10n_util::GetStringUTF16( + IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_WAITING_FOR_ANDROID)); localized_strings->SetBoolean("showStylusSettings", ash::IsPaletteFeatureEnabled()); } -void OptionsStylusHandler::InitializePage() {} +void OptionsStylusHandler::InitializePage() { + UpdateNoteTakingApps(); +} -void OptionsStylusHandler::RegisterMessages() {} +void OptionsStylusHandler::RegisterMessages() { + web_ui()->RegisterMessageCallback( + "setPreferredNoteTakingApp", + base::Bind(&OptionsStylusHandler::SetPreferredNoteTakingApp, + weak_ptr_factory_.GetWeakPtr())); +} + +void OptionsStylusHandler::OnAvailableNoteTakingAppsUpdated() { + UpdateNoteTakingApps(); +} + +void OptionsStylusHandler::UpdateNoteTakingApps() { + bool waiting_for_android = false; + note_taking_app_ids_.clear(); + base::ListValue apps_list; + + NoteTakingHelper* helper = NoteTakingHelper::Get(); + if (helper->android_enabled() && !helper->android_apps_received()) { + // If Android is enabled but not ready yet, let the JS know so it can + // disable the menu and display an explanatory message. + waiting_for_android = true; + } else { + for (const NoteTakingAppInfo& info : + NoteTakingHelper::Get()->GetAvailableApps( + Profile::FromWebUI(web_ui()))) { + std::unique_ptr<base::DictionaryValue> dict( + new base::DictionaryValue()); + dict->SetString(kAppNameKey, info.name); + dict->SetString(kAppIdKey, info.app_id); + dict->SetBoolean(kAppPreferredKey, info.preferred); + apps_list.Append(std::move(dict)); + + note_taking_app_ids_.insert(info.app_id); + } + } + + web_ui()->CallJavascriptFunctionUnsafe( + "StylusOverlay.updateNoteTakingApps", apps_list, + base::FundamentalValue(waiting_for_android)); +} + +void OptionsStylusHandler::SetPreferredNoteTakingApp( + const base::ListValue* args) { + std::string app_id; + CHECK(args->GetString(0, &app_id)); + + // Sanity check: make sure that the ID we got back from WebUI is in the + // currently-available set. + if (!note_taking_app_ids_.count(app_id)) { + LOG(ERROR) << "Got unknown note-taking-app ID \"" << app_id << "\""; + return; + } + + NoteTakingHelper::Get()->SetPreferredApp(Profile::FromWebUI(web_ui()), + app_id); +} } // namespace options } // namespace chromeos
diff --git a/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h b/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h index 1e5f3c6..a35595a 100644 --- a/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h +++ b/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h
@@ -5,14 +5,24 @@ #ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_OPTIONS_STYLUS_HANDLER_H_ #define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_OPTIONS_STYLUS_HANDLER_H_ +#include <set> +#include <string> + #include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/chromeos/note_taking_helper.h" #include "chrome/browser/ui/webui/options/options_ui.h" +namespace base { +class ListValue; +} // namespace base + namespace chromeos { namespace options { // Stylus-specific options C++ code. -class OptionsStylusHandler : public ::options::OptionsPageUIHandler { +class OptionsStylusHandler : public ::options::OptionsPageUIHandler, + public NoteTakingHelper::Observer { public: OptionsStylusHandler(); ~OptionsStylusHandler() override; @@ -22,7 +32,22 @@ void InitializePage() override; void RegisterMessages() override; + // NoteTakingHelper::Observer implementation. + void OnAvailableNoteTakingAppsUpdated() override; + private: + // Updates the note-taking app menu in the stylus overlay to display the + // currently-available set of apps. + void UpdateNoteTakingApps(); + + // Called from WebUI when the user selects a note-taking app. + void SetPreferredNoteTakingApp(const base::ListValue* args); + + // IDs of available note-taking apps. + std::set<std::string> note_taking_app_ids_; + + base::WeakPtrFactory<OptionsStylusHandler> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(OptionsStylusHandler); };
diff --git a/chrome/browser/ui/webui/options/chromeos/user_image_source.cc b/chrome/browser/ui/webui/options/chromeos/user_image_source.cc index d302cbf..c5858ea 100644 --- a/chrome/browser/ui/webui/options/chromeos/user_image_source.cc +++ b/chrome/browser/ui/webui/options/chromeos/user_image_source.cc
@@ -38,7 +38,7 @@ if (!status) { LOG(WARNING) << "Failed to deserialize account_id."; account_id = user_manager::known_user::GetAccountId( - serialized_account_id, std::string() /* gaia_id */); + serialized_account_id, std::string() /* id */, AccountType::UNKNOWN); } *email = account_id.GetUserEmail(); }
diff --git a/chrome/browser/ui/webui/options/sync_setup_handler.cc b/chrome/browser/ui/webui/options/sync_setup_handler.cc index 8d995e3..d9f8054 100644 --- a/chrome/browser/ui/webui/options/sync_setup_handler.cc +++ b/chrome/browser/ui/webui/options/sync_setup_handler.cc
@@ -647,8 +647,11 @@ #if !defined(OS_CHROMEOS) void SyncSetupHandler::HandleStartSignin(const base::ListValue* args) { - // Should only be called if the user is not already signed in. - DCHECK(!SigninManagerFactory::GetForProfile(GetProfile())->IsAuthenticated()); + // Should only be called if the user is not already signed in or has an auth + // error. + DCHECK( + !SigninManagerFactory::GetForProfile(GetProfile())->IsAuthenticated() || + SigninErrorControllerFactory::GetForProfile(GetProfile())->HasError()); bool creating_supervised_user = false; args->GetBoolean(0, &creating_supervised_user); OpenSyncSetup(creating_supervised_user);
diff --git a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc index 2e7c957f..2d068a4 100644 --- a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc +++ b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
@@ -169,6 +169,7 @@ if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableNativeCups)) { AddPrintersToList(prefs_->GetPrinters(), &printer_list); + AddPrintersToList(prefs_->GetRecommendedPrinters(), &printer_list); } content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc index 642b7c8..35e1d2d 100644 --- a/chrome/browser/ui/webui/settings/people_handler.cc +++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -156,6 +156,8 @@ switch (action_type) { case sync_ui_util::REAUTHENTICATE: return "reauthenticate"; + case sync_ui_util::SIGNOUT_AND_SIGNIN: + return "signOutAndSignIn"; case sync_ui_util::UPGRADE_CLIENT: return "upgradeClient"; case sync_ui_util::ENTER_PASSPHRASE: @@ -505,7 +507,7 @@ return; } - OpenSyncSetup(false /* creating_supervised_user */); + OpenSyncSetup(); } #if defined(OS_CHROMEOS) @@ -521,11 +523,12 @@ void PeopleHandler::HandleStartSignin(const base::ListValue* args) { AllowJavascript(); - // Should only be called if the user is not already signed in. - DCHECK(!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated()); - bool creating_supervised_user = false; - args->GetBoolean(0, &creating_supervised_user); - OpenSyncSetup(creating_supervised_user); + // Should only be called if the user is not already signed in or has an auth + // error. + DCHECK(!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated() || + SigninErrorControllerFactory::GetForProfile(profile_)->HasError()); + + OpenSyncSetup(); } void PeopleHandler::HandleStopSyncing(const base::ListValue* args) { @@ -623,7 +626,7 @@ configuring_sync_ = false; } -void PeopleHandler::OpenSyncSetup(bool creating_supervised_user) { +void PeopleHandler::OpenSyncSetup() { // Notify services that login UI is now active. GetLoginUIService()->SetLoginUI(this); @@ -643,18 +646,14 @@ // sync configure UI, not login UI). // 7) User re-enables sync after disabling it via advanced settings. #if !defined(OS_CHROMEOS) - SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_); - if (!signin->IsAuthenticated() || + if (!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated() || SigninErrorControllerFactory::GetForProfile(profile_)->HasError()) { // User is not logged in (cases 1-2), or login has been specially requested // because previously working credentials have expired (case 3). Close sync // setup including any visible overlays, and display the gaia auth page. // Control will be returned to the sync settings page once auth is complete. CloseUI(); - DisplayGaiaLogin( - creating_supervised_user ? - signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER : - signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS); + DisplayGaiaLogin(signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS); return; } #endif
diff --git a/chrome/browser/ui/webui/settings/people_handler.h b/chrome/browser/ui/webui/settings/people_handler.h index da243e0..9b367b7 100644 --- a/chrome/browser/ui/webui/settings/people_handler.h +++ b/chrome/browser/ui/webui/settings/people_handler.h
@@ -60,7 +60,7 @@ ~PeopleHandler() override; // Initializes the sync setup flow and shows the setup UI. - void OpenSyncSetup(bool creating_supervised_user); + void OpenSyncSetup(); // Terminates the sync setup flow. void CloseSyncSetup();
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc index 273f55bf..487eff2 100644 --- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -396,7 +396,7 @@ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false)); SetDefaultExpectationsForConfigPage(); - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); EXPECT_EQ(1U, web_ui_.call_data().size()); ExpectPageStatusChanged(PeopleHandler::kSpinnerPageStatus); @@ -432,7 +432,7 @@ .WillOnce(Return(false)) .WillRepeatedly(Return(true)); SetDefaultExpectationsForConfigPage(); - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); // It's important to tell sync the user cancelled the setup flow before we // tell it we're through with the setup progress. @@ -452,7 +452,7 @@ error_ = GoogleServiceAuthError::AuthErrorNone(); EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false)); - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); ExpectPageStatusChanged(PeopleHandler::kSpinnerPageStatus); Mock::VerifyAndClearExpectations(mock_pss_); error_ = GoogleServiceAuthError( @@ -479,7 +479,7 @@ .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false)); // Open the web UI. - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); ASSERT_FALSE(handler_->is_configuring_sync()); } @@ -489,7 +489,7 @@ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false)); // Open the web UI. - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); ASSERT_FALSE(handler_->is_configuring_sync()); } @@ -498,7 +498,7 @@ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false)); // Open the web UI. - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); ASSERT_FALSE(handler_->is_configuring_sync()); } @@ -698,7 +698,7 @@ SetupInitializedProfileSyncService(); // This should display the sync setup dialog (not login). SetDefaultExpectationsForConfigPage(); - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); ExpectSyncPrefsChanged(); } @@ -733,7 +733,7 @@ // On ChromeOS, this should display the spinner while we try to startup the // sync engine, and on desktop this displays the login dialog. - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); // Sync setup is closed when re-auth is in progress. EXPECT_EQ(NULL, @@ -752,7 +752,7 @@ SetupInitializedProfileSyncService(); SetDefaultExpectationsForConfigPage(); // This should display the sync setup dialog (not login). - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged(); CheckBool(dictionary, "syncAllDataTypes", true); @@ -782,7 +782,7 @@ sync_prefs.SetKeepEverythingSynced(false); SetDefaultExpectationsForConfigPage(); // This should display the sync setup dialog (not login). - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged(); CheckConfigDataTypeArguments(dictionary, CHOOSE_WHAT_TO_SYNC, GetAllTypes()); @@ -806,7 +806,7 @@ WillRepeatedly(Return(types)); // This should display the sync setup dialog (not login). - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); // Close the config overlay. LoginUIServiceFactory::GetForProfile(profile_)->LoginUIClosed( @@ -830,7 +830,7 @@ SetDefaultExpectationsForConfigPage(); // This should display the sync setup dialog (not login). - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged(); CheckBool(dictionary, "passphraseRequired", true); @@ -846,7 +846,7 @@ SetDefaultExpectationsForConfigPage(); // This should display the sync setup dialog (not login). - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged(); CheckBool(dictionary, "passphraseRequired", true); @@ -864,7 +864,7 @@ .WillRepeatedly(Return(true)); // This should display the sync setup dialog (not login). - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged(); CheckBool(dictionary, "encryptAllData", true); @@ -881,7 +881,7 @@ .WillRepeatedly(Return(false)); // This should display the sync setup dialog (not login). - handler_->OpenSyncSetup(false /* creating_supervised_user */); + handler_->OpenSyncSetup(); const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged(); CheckBool(dictionary, "encryptAllData", false);
diff --git a/chrome/test/data/pdf/gesture_detector_test.js b/chrome/test/data/pdf/gesture_detector_test.js index b73b85a..b238294 100644 --- a/chrome/test/data/pdf/gesture_detector_test.js +++ b/chrome/test/data/pdf/gesture_detector_test.js
@@ -7,21 +7,25 @@ class StubElement { constructor() { - this.listeners_ = new Map([ + this.listeners = new Map([ ['touchstart', []], ['touchmove', []], ['touchend', []], ['touchcancel', []] ]); + + this.listenerOptions = new Map(); } - addEventListener(type, listener) { - if (this.listeners_.has(type)) - this.listeners_.get(type).push(listener); + addEventListener(type, listener, options) { + if (this.listeners.has(type)) { + this.listeners.get(type).push(listener); + this.listenerOptions.set(listener, options); + } } sendEvent(event) { - for (let l of this.listeners_.get(event.type)) + for (let l of this.listeners.get(event.type)) l(event); } } @@ -178,6 +182,17 @@ let gestureDetector = new GestureDetector(stubElement); let pinchListener = new PinchListener(gestureDetector); + // Ensure that the touchstart listener is not passive, otherwise the + // call to preventDefault will be ignored. Since listeners could default + // to being passive, we must set the value explicitly + // (see crbug.com/675730). + for (let l of stubElement.listeners.get('touchstart')) { + let options = stubElement.listenerOptions.get(l); + chrome.test.assertTrue(!!options && + typeof(options.passive) == 'boolean'); + chrome.test.assertFalse(options.passive); + } + let pinchStartEvent = new MockTouchEvent('touchstart', [ {clientX: 0, clientY: 0}, {clientX: 0, clientY: 2}
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 4c5cf7d..00dd1324 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2044,6 +2044,23 @@ ] }, + "RoamingProfileSupportEnabled": { + "os": ["win"], + "test_policy": { "RoamingProfileSupportEnabled": true }, + "pref_mappings": [ + { "pref": "sync.enable_local_sync_backend" } + ] + }, + + "RoamingProfileLocation": { + "os": ["win"], + "test_policy": { "RoamingProfileLocation": "${roaming_app_data}" }, + "pref_mappings": [ + { "pref": "sync.local_sync_backend_dir" } + ] + }, + + "----- Chrome OS policies ------------------------------------------------": {}, "ChromeOsLockOnIdleSuspend": {
diff --git a/chrome/test/data/webui/bluetooth_internals_browsertest.js b/chrome/test/data/webui/bluetooth_internals_browsertest.js index 0e948f1..2bfa1804b 100644 --- a/chrome/test/data/webui/bluetooth_internals_browsertest.js +++ b/chrome/test/data/webui/bluetooth_internals_browsertest.js
@@ -51,9 +51,7 @@ 'device/bluetooth/public/interfaces/adapter.mojom', 'device/bluetooth/public/interfaces/device.mojom', 'mojo/public/js/bindings', - 'mojo/public/js/connection', - ]).then(function([frameInterfaces, adapter, device, bindings, - connection]) { + ]).then(function([frameInterfaces, adapter, device, bindings]) { /** * A test adapter factory proxy for the chrome://bluetooth-internals * page. @@ -66,6 +64,7 @@ 'getAdapter', ]); + this.binding = new bindings.Binding(adapter.AdapterFactory, this); this.adapter = new TestAdapter(); this.adapterBinding_ = new bindings.Binding(adapter.Adapter, this.adapter); @@ -88,14 +87,12 @@ * Must be used to create message pipe handle from test code. * * @constructor - * @extends {adapter.Adapter.stubClass} */ var TestAdapter = function() { this.proxy = new TestAdapterProxy(); }; TestAdapter.prototype = { - __proto__: adapter.Adapter.stubClass.prototype, getInfo: function() { return this.proxy.getInfo(); }, getDevices: function() { return this.proxy.getDevices(); }, setClient: function(client) { return this.proxy.setClient(client); } @@ -146,10 +143,8 @@ frameInterfaces.addInterfaceOverrideForTesting( adapter.AdapterFactory.name, function(handle) { - var stub = connection.bindHandleToStub( - handle, adapter.AdapterFactory); - this.adapterFactory = new TestAdapterFactoryProxy(); + this.adapterFactory.binding.bind(handle); this.adapterFactory.adapter.proxy.setTestDevices([ this.fakeDeviceInfo1(), @@ -158,8 +153,6 @@ this.adapterFactory.adapter.proxy.setTestAdapter( this.fakeAdapterInfo()); - bindings.StubBindings(stub).delegate = this.adapterFactory; - this.setupResolver.resolve(); }.bind(this));
diff --git a/chrome/test/data/webui/plugins_browsertest.js b/chrome/test/data/webui/plugins_browsertest.js index 6f613ea..1fc8ac7 100644 --- a/chrome/test/data/webui/plugins_browsertest.js +++ b/chrome/test/data/webui/plugins_browsertest.js
@@ -55,6 +55,8 @@ 'saveShowDetailsToPrefs', ]); + this.bindingSet = null; + /** * The data to be returned by |getPluginsData_|. * @private @@ -96,20 +98,19 @@ window.setupFn = function() { return importModules([ 'mojo/public/js/bindings', - 'mojo/public/js/connection', 'chrome/browser/ui/webui/plugins/plugins.mojom', 'content/public/renderer/frame_interfaces', ]).then(function(modules) { var bindings = modules[0]; - var connection = modules[1]; - var pluginsMojom = modules[2]; - var frameInterfaces = modules[3]; + var pluginsMojom = modules[1]; + var frameInterfaces = modules[2]; + this.browserProxy.bindingSet = new bindings.BindingSet( + pluginsMojom.PluginsPageHandler); frameInterfaces.addInterfaceOverrideForTesting( pluginsMojom.PluginsPageHandler.name, function(handle) { - var stub = connection.bindHandleToStub( - handle, pluginsMojom.PluginsPageHandler); - bindings.StubBindings(stub).delegate = this.browserProxy; + this.browserProxy.bindingSet.addBinding(this.browserProxy, + handle); }.bind(this)); return this.setupFnResolver.promise; }.bind(this));
diff --git a/chrome/test/data/webui/settings/category_default_setting_tests.js b/chrome/test/data/webui/settings/category_default_setting_tests.js index 306e535..58a8b071 100644 --- a/chrome/test/data/webui/settings/category_default_setting_tests.js +++ b/chrome/test/data/webui/settings/category_default_setting_tests.js
@@ -193,7 +193,6 @@ assertEquals(category, contentType); assertTrue(testElement.categoryEnabled); - assertTrue(testElement.categoryEnabled); assertFalse(secondaryToggle.disabled); assertTrue(secondaryToggle.checked);
diff --git a/chrome/test/data/webui/settings/import_data_dialog_test.js b/chrome/test/data/webui/settings/import_data_dialog_test.js index 703a00f..d053879 100644 --- a/chrome/test/data/webui/settings/import_data_dialog_test.js +++ b/chrome/test/data/webui/settings/import_data_dialog_test.js
@@ -95,8 +95,8 @@ dialog = document.createElement('settings-import-data-dialog'); dialog.set('prefs', prefs); document.body.appendChild(dialog); - assertTrue(dialog.$.dialog.open); return browserProxy.whenCalled('initializeImportDialog').then(function() { + assertTrue(dialog.$.dialog.open); Polymer.dom.flush(); }); });
diff --git a/chrome/test/media_router/BUILD.gn b/chrome/test/media_router/BUILD.gn index f58d024..cf283c4 100644 --- a/chrome/test/media_router/BUILD.gn +++ b/chrome/test/media_router/BUILD.gn
@@ -15,6 +15,8 @@ "internal/", "telemetry/", "../../../third_party/catapult/", + "../../../tools/json_comment_eater/", + "../../../tools/json_to_struct/", "../../../tools/perf/", "../../../tools/variations/", ]
diff --git a/chrome/tools/build/win/FILES.cfg b/chrome/tools/build/win/FILES.cfg index a4da7ad4..58a351c 100644 --- a/chrome/tools/build/win/FILES.cfg +++ b/chrome/tools/build/win/FILES.cfg
@@ -444,6 +444,7 @@ 'filename': 'gcp_portmon.dll.pdb', 'buildtype': ['official'], 'archive': 'cloud_print.zip', + 'optional': ['official'], }, { 'filename': 'gcp_portmon64.dll',
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index d5d122c..d929769 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -9105.0.0 \ No newline at end of file +9110.0.0 \ No newline at end of file
diff --git a/chromeos/cryptohome/cryptohome_parameters.cc b/chromeos/cryptohome/cryptohome_parameters.cc index 9ce9c91..4f6d557 100644 --- a/chromeos/cryptohome/cryptohome_parameters.cc +++ b/chromeos/cryptohome/cryptohome_parameters.cc
@@ -19,7 +19,7 @@ const std::string GetCryptohomeId(const AccountId& account_id) { // Guest/kiosk/managed/public accounts have empty GaiaId. Default to email. - if (account_id.GetGaiaId().empty()) + if (account_id.GetAccountType() == AccountType::UNKNOWN) return account_id.GetUserEmail(); // Migrated if (GetGaiaIdMigrationStatus(account_id)) @@ -56,7 +56,7 @@ // A LOT of tests start with --login_user <user>, and not registing this user // before. So we might have "known_user" entry without gaia_id. for (const AccountId& known_id : known_account_ids) { - if (!known_id.GetGaiaId().empty() && known_id.GetAccountIdKey() == id_) { + if (known_id.HasAccountIdKey() && known_id.GetAccountIdKey() == id_) { return known_id; } } @@ -67,8 +67,8 @@ } } - return user_manager::known_user::GetAccountId(id_, - std::string() /* gaia_id */); + return user_manager::known_user::GetAccountId(id_, std::string() /* id */, + AccountType::UNKNOWN); } KeyDefinition::AuthorizationData::Secret::Secret() : encrypt(false),
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc index 9fc700a..389dd6c 100644 --- a/chromeos/login/auth/cryptohome_authenticator.cc +++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -220,12 +220,12 @@ } const bool already_migrated = cryptohome::GetGaiaIdMigrationStatus( attempt->user_context.GetAccountId()); - const bool has_gaia_id = - !attempt->user_context.GetAccountId().GetGaiaId().empty(); + const bool has_account_key = + attempt->user_context.GetAccountId().HasAccountIdKey(); bool need_migration = false; if (!create_if_nonexistent && !already_migrated) { - if (has_gaia_id) { + if (has_account_key) { need_migration = true; } else { LOG(WARNING) << "Account '" @@ -248,7 +248,7 @@ create_if_nonexistent)); return; } - if (!already_migrated && has_gaia_id) { + if (!already_migrated && has_account_key) { // Mark new users migrated. cryptohome::SetGaiaIdMigrationStatusDone( attempt->user_context.GetAccountId());
diff --git a/chromeos/printing/ppd_provider.cc b/chromeos/printing/ppd_provider.cc index f05b53f0..133f771 100644 --- a/chromeos/printing/ppd_provider.cc +++ b/chromeos/printing/ppd_provider.cc
@@ -283,11 +283,13 @@ uint64_t* last_updated_time) { std::string contents; if (!ValidateAndGetResponseAsString(*source, &contents)) { + LOG(WARNING) << "Response not validated"; return false; } auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents)); if (dict == nullptr) { + LOG(WARNING) << "Response not a dictionary"; return false; } std::string last_updated_time_string; @@ -299,6 +301,7 @@ } if (ppd_contents->size() > options_.max_ppd_contents_size_) { + LOG(WARNING) << "PPD too large"; // PPD is too big. // // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to @@ -414,9 +417,9 @@ // return true, otherwise return false. bool ValidateAndGetResponseAsString(const net::URLFetcher& fetcher, std::string* contents) { - return ((fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) && - (fetcher.GetResponseCode() == net::HTTP_OK) && - fetcher.GetResponseAsString(contents)); + return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) && + (fetcher.GetResponseCode() == net::HTTP_OK) && + fetcher.GetResponseAsString(contents); } // API key for accessing quirks server.
diff --git a/chromeos/printing/printer_configuration.cc b/chromeos/printing/printer_configuration.cc index b5c8c3f..9b6f663 100644 --- a/chromeos/printing/printer_configuration.cc +++ b/chromeos/printing/printer_configuration.cc
@@ -10,11 +10,11 @@ namespace chromeos { -Printer::Printer() { +Printer::Printer() : source_(SRC_USER_PREFS) { id_ = base::GenerateGUID(); } -Printer::Printer(const std::string& id) : id_(id) { +Printer::Printer(const std::string& id) : id_(id), source_(SRC_USER_PREFS) { if (id_.empty()) id_ = base::GenerateGUID(); }
diff --git a/chromeos/printing/printer_configuration.h b/chromeos/printing/printer_configuration.h index 233a09f0..5223753 100644 --- a/chromeos/printing/printer_configuration.h +++ b/chromeos/printing/printer_configuration.h
@@ -43,6 +43,12 @@ std::string effective_model; }; + // The location where the printer is stored. + enum Source { + SRC_USER_PREFS, + SRC_POLICY, + }; + // Constructs a printer object that is completely empty. Printer(); @@ -90,6 +96,9 @@ // |uri_|. bool IsIppEverywhere() const; + Source source() const { return source_; } + void set_source(const Source source) { source_ = source; } + private: // Globally unique identifier. Empty indicates a new printer. std::string id_; @@ -115,6 +124,9 @@ // The UUID from an autoconf protocol for deduplication. Could be empty. std::string uuid_; + + // The datastore which holds this printer. + Source source_; }; } // namespace chromeos
diff --git a/chromeos/printing/printer_translator.cc b/chromeos/printing/printer_translator.cc index 9f64668a..4574bf6 100644 --- a/chromeos/printing/printer_translator.cc +++ b/chromeos/printing/printer_translator.cc
@@ -33,6 +33,9 @@ const char kPpdReference[] = "ppd_reference"; const char kUUID[] = "uuid"; +// The name of the PpdResource for policy printers. +const char kPpdResource[] = "ppd_resource"; + Printer::PpdReference DictionaryToPpdReference( const base::DictionaryValue* value) { Printer::PpdReference ppd; @@ -58,15 +61,11 @@ return dictionary; } -} // namespace - -namespace printing { - -const char kPrinterId[] = "id"; - -std::unique_ptr<Printer> PrefToPrinter(const DictionaryValue& value) { +// Converts |value| into a Printer object for the fields that are shared +// between pref printers and policy printers. +std::unique_ptr<Printer> DictionaryToPrinter(const DictionaryValue& value) { std::string id; - if (!value.GetString(kPrinterId, &id)) { + if (!value.GetString(printing::kPrinterId, &id)) { LOG(WARNING) << "Record id required"; return nullptr; } @@ -97,6 +96,23 @@ if (value.GetString(kUUID, &uuid)) printer->set_uuid(uuid); + return printer; +} + +} // namespace + +namespace printing { + +const char kPrinterId[] = "id"; + +std::unique_ptr<Printer> PrefToPrinter(const DictionaryValue& value) { + if (!value.HasKey(kPrinterId)) { + LOG(WARNING) << "Record id required"; + return nullptr; + } + + std::unique_ptr<Printer> printer = DictionaryToPrinter(value); + const DictionaryValue* ppd; if (value.GetDictionary(kPpdReference, &ppd)) { *printer->mutable_ppd_reference() = DictionaryToPpdReference(ppd); @@ -122,6 +138,17 @@ return dictionary; } -} // namespace printing +std::unique_ptr<Printer> RecommendedPrinterToPrinter( + const base::DictionaryValue& pref) { + std::unique_ptr<Printer> printer = DictionaryToPrinter(pref); + const DictionaryValue* ppd; + if (pref.GetDictionary(kPpdResource, &ppd)) { + *printer->mutable_ppd_reference() = DictionaryToPpdReference(ppd); + } + + return printer; +} + +} // namespace printing } // namespace chromeos
diff --git a/chromeos/printing/printer_translator.h b/chromeos/printing/printer_translator.h index 14283ab..1efa84e 100644 --- a/chromeos/printing/printer_translator.h +++ b/chromeos/printing/printer_translator.h
@@ -28,6 +28,11 @@ CHROMEOS_EXPORT std::unique_ptr<base::DictionaryValue> PrinterToPref( const Printer& printer); +// Returns a new printer populated with the fields from |pref|. Processes +// dictionaries from policy i.e. cPanel. +CHROMEOS_EXPORT std::unique_ptr<Printer> RecommendedPrinterToPrinter( + const base::DictionaryValue& pref); + } // namespace printing } // namespace chromeos
diff --git a/chromeos/tpm/tpm_token_info_getter_unittest.cc b/chromeos/tpm/tpm_token_info_getter_unittest.cc index cff50eaf..6e6a9d9 100644 --- a/chromeos/tpm/tpm_token_info_getter_unittest.cc +++ b/chromeos/tpm/tpm_token_info_getter_unittest.cc
@@ -259,7 +259,7 @@ class UserTPMTokenInfoGetterTest : public testing::Test { public: UserTPMTokenInfoGetterTest() - : account_id_(AccountId::FromUserEmail("user")) {} + : account_id_(AccountId::FromUserEmail("user@gmail.com")) {} ~UserTPMTokenInfoGetterTest() override {} void SetUp() override {
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc index 09de1ca8..b4868426 100644 --- a/components/history/core/browser/history_service.cc +++ b/components/history/core/browser/history_service.cc
@@ -886,7 +886,7 @@ base::TaskTraits() .WithPriority(base::TaskPriority::USER_BLOCKING) .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN) - .WithFileIO()); + .MayBlock().WithSyncPrimitives()); } // Create the history backend.
diff --git a/components/ntp_snippets/BUILD.gn b/components/ntp_snippets/BUILD.gn index c3477b30..370e2a4 100644 --- a/components/ntp_snippets/BUILD.gn +++ b/components/ntp_snippets/BUILD.gn
@@ -21,6 +21,8 @@ "category_info.cc", "category_info.h", "category_rankers/category_ranker.h", + "category_rankers/click_based_category_ranker.cc", + "category_rankers/click_based_category_ranker.h", "category_rankers/constant_category_ranker.cc", "category_rankers/constant_category_ranker.h", "category_status.cc", @@ -126,6 +128,7 @@ testonly = true sources = [ "bookmarks/bookmark_last_visit_utils_unittest.cc", + "category_rankers/click_based_category_ranker_unittest.cc", "category_rankers/constant_category_ranker_unittest.cc", "category_unittest.cc", "content_suggestions_service_unittest.cc",
diff --git a/components/ntp_snippets/OWNERS b/components/ntp_snippets/OWNERS index 5facc94e..d4be5146 100644 --- a/components/ntp_snippets/OWNERS +++ b/components/ntp_snippets/OWNERS
@@ -8,3 +8,4 @@ # Fallback, in case all of the above are OOO: markusheintz@chromium.org +vitaliii@chromium.org
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker.cc b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc new file mode 100644 index 0000000..45da64b --- /dev/null +++ b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
@@ -0,0 +1,236 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/ntp_snippets/category_rankers/click_based_category_ranker.h" + +#include <algorithm> + +#include "base/memory/ptr_util.h" +#include "base/values.h" +#include "components/ntp_snippets/category_rankers/constant_category_ranker.h" +#include "components/ntp_snippets/pref_names.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" + +namespace ntp_snippets { + +namespace { + +// In order to increase stability and predictability of the order, an extra +// level of "confidence" is required before moving a category upwards. In other +// words, the category is moved not when it reaches the previous one, but rather +// when it leads by some amount. We refer to this required extra "confidence" as +// a passing margin. Each position has its own passing margin. The category is +// moved upwards (i.e. passes another category) when it has at least passing +// margin of the previous category position more clicks. +const int kPassingMargin = 5; + +// The first categories get more attention and, therefore, here more stability +// is needed. The passing margin of such categories is increased and they are +// referred to as top categories (with extra margin). Only category position +// defines whether a category is top, but not its content. +const int kNumTopCategoriesWithExtraMargin = 3; + +// The increase of passing margin for each top category compared to the next +// category (e.g. the first top category has passing margin larger by this value +// than the second top category, the last top category has it larger by this +// value than the first non-top category). +const int kExtraPassingMargin = 2; + +const char kCategoryIdKey[] = "category"; +const char kClicksKey[] = "clicks"; + +} // namespace + +ClickBasedCategoryRanker::ClickBasedCategoryRanker(PrefService* pref_service) + : pref_service_(pref_service) { + if (!ReadOrderFromPrefs(&ordered_categories_)) { + // TODO(crbug.com/676273): Handle adding new hardcoded KnownCategories to + // existing order from prefs. Currently such new category is completely + // ignored and may be never shown. + RestoreDefaultOrder(); + } +} + +ClickBasedCategoryRanker::~ClickBasedCategoryRanker() = default; + +bool ClickBasedCategoryRanker::Compare(Category left, Category right) const { + if (!ContainsCategory(left)) { + LOG(DFATAL) << "The category with ID " << left.id() + << " has not been added using AppendCategoryIfNecessary."; + } + if (!ContainsCategory(right)) { + LOG(DFATAL) << "The category with ID " << right.id() + << " has not been added using AppendCategoryIfNecessary."; + } + if (left == right) { + return false; + } + for (const RankedCategory& ranked_category : ordered_categories_) { + if (ranked_category.category == left) { + return true; + } + if (ranked_category.category == right) { + return false; + } + } + // This fallback is provided only to satisfy "Compare" contract if by mistake + // categories are not added using AppendCategoryIfNecessary. One should not + // rely on this, instead the order must be defined explicitly using + // AppendCategoryIfNecessary. + return left.id() < right.id(); +} + +void ClickBasedCategoryRanker::ClearHistory(base::Time begin, base::Time end) { + // TODO(crbug.com/675953): Preserve remote categories with 0 counts instead of + // removing them. + RestoreDefaultOrder(); +} + +void ClickBasedCategoryRanker::AppendCategoryIfNecessary(Category category) { + if (!ContainsCategory(category)) { + ordered_categories_.push_back(RankedCategory(category, /*clicks=*/0)); + } +} + +// TODO(vitaliii): Listen to dismissed sections and penalise them. +void ClickBasedCategoryRanker::OnSuggestionOpened(Category category) { + if (!ContainsCategory(category)) { + LOG(DFATAL) << "The category with ID " << category.id() + << " has not been added using AppendCategoryIfNecessary."; + return; + } + + std::vector<RankedCategory>::iterator current = FindCategory(category); + current->clicks++; + + // TODO(crbug.com/676279): Prevent overflow. + if (current != ordered_categories_.begin()) { + std::vector<RankedCategory>::iterator previous = current - 1; + const int passing_margin = GetPositionPassingMargin(previous); + if (current->clicks >= previous->clicks + passing_margin) { + // It is intended to move only by one position at a time in order to avoid + // dramatic changes, which could confuse the user. + std::swap(*current, *previous); + } + } + + StoreOrderToPrefs(ordered_categories_); +} + +// static +void ClickBasedCategoryRanker::RegisterProfilePrefs( + PrefRegistrySimple* registry) { + registry->RegisterListPref(prefs::kClickBasedCategoryRankerOrderWithClicks); +} + +// static +int ClickBasedCategoryRanker::GetPassingMargin() { + return kPassingMargin; +} + +// static +int ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin() { + return kNumTopCategoriesWithExtraMargin; +} + +ClickBasedCategoryRanker::RankedCategory::RankedCategory(Category category, + int clicks) + : category(category), clicks(clicks) {} + +// Returns passing margin for a given position taking into account whether it is +// a top category. +int ClickBasedCategoryRanker::GetPositionPassingMargin( + std::vector<RankedCategory>::const_iterator category_position) const { + int index = category_position - ordered_categories_.cbegin(); + int passing_margin_increase = 0; + if (index < kNumTopCategoriesWithExtraMargin) { + passing_margin_increase = + kExtraPassingMargin * (kNumTopCategoriesWithExtraMargin - index); + } + return kPassingMargin + passing_margin_increase; +} + +void ClickBasedCategoryRanker::RestoreDefaultOrder() { + ordered_categories_.clear(); + + std::vector<KnownCategories> ordered_known_categories = + ConstantCategoryRanker::GetKnownCategoriesDefaultOrder(); + + for (KnownCategories known_category : ordered_known_categories) { + AppendKnownCategory(known_category); + } + + StoreOrderToPrefs(ordered_categories_); +} + +void ClickBasedCategoryRanker::AppendKnownCategory( + KnownCategories known_category) { + Category category = Category::FromKnownCategory(known_category); + DCHECK(!ContainsCategory(category)); + ordered_categories_.push_back(RankedCategory(category, /*clicks=*/0)); +} + +bool ClickBasedCategoryRanker::ReadOrderFromPrefs( + std::vector<RankedCategory>* result_categories) { + result_categories->clear(); + const base::ListValue* list = + pref_service_->GetList(prefs::kClickBasedCategoryRankerOrderWithClicks); + if (!list || list->GetSize() == 0) { + return false; + } + + for (const std::unique_ptr<base::Value>& value : *list) { + base::DictionaryValue* dictionary; + if (!value->GetAsDictionary(&dictionary)) { + LOG(DFATAL) << "Failed to parse category data from prefs param " + << prefs::kClickBasedCategoryRankerOrderWithClicks + << " into dictionary."; + return false; + } + int category_id, clicks; + if (!dictionary->GetInteger(kCategoryIdKey, &category_id)) { + LOG(DFATAL) << "Dictionary does not have '" << kCategoryIdKey << "' key."; + return false; + } + if (!dictionary->GetInteger(kClicksKey, &clicks)) { + LOG(DFATAL) << "Dictionary does not have '" << kClicksKey << "' key."; + return false; + } + Category category = Category::FromIDValue(category_id); + result_categories->push_back(RankedCategory(category, clicks)); + } + return true; +} + +void ClickBasedCategoryRanker::StoreOrderToPrefs( + const std::vector<RankedCategory>& ordered_categories) { + base::ListValue list; + for (const RankedCategory& category : ordered_categories) { + auto dictionary = base::MakeUnique<base::DictionaryValue>(); + dictionary->SetInteger(kCategoryIdKey, category.category.id()); + dictionary->SetInteger(kClicksKey, category.clicks); + list.Append(std::move(dictionary)); + } + pref_service_->Set(prefs::kClickBasedCategoryRankerOrderWithClicks, list); +} + +std::vector<ClickBasedCategoryRanker::RankedCategory>::iterator +ClickBasedCategoryRanker::FindCategory(Category category) { + return std::find_if(ordered_categories_.begin(), ordered_categories_.end(), + [category](const RankedCategory& ranked_category) { + return category == ranked_category.category; + }); +} + +bool ClickBasedCategoryRanker::ContainsCategory(Category category) const { + for (const auto& ranked_category : ordered_categories_) { + if (category == ranked_category.category) { + return true; + } + } + return false; +} + +} // namespace ntp_snippets
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker.h b/components/ntp_snippets/category_rankers/click_based_category_ranker.h new file mode 100644 index 0000000..4b3a8a2 --- /dev/null +++ b/components/ntp_snippets/category_rankers/click_based_category_ranker.h
@@ -0,0 +1,72 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CLICK_BASED_SECTION_RANKER_H_ +#define COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CLICK_BASED_SECTION_RANKER_H_ + +#include <vector> + +#include "base/time/time.h" +#include "components/ntp_snippets/category.h" +#include "components/ntp_snippets/category_rankers/category_ranker.h" + +class PrefRegistrySimple; +class PrefService; + +namespace ntp_snippets { + +// An implementation of a CategoryRanker based on a number of clicks per +// category. Initial order is hardcoded, but sections with more clicks are moved +// to the top. The new remote categories must be registered using +// AppendCategoryIfNecessary. All other categories must be hardcoded in the +// initial order. The order and category usage data are persisted in prefs and +// reloaded on startup. TODO(crbug.com/675929): Remove unused categories from +// prefs. +class ClickBasedCategoryRanker : public CategoryRanker { + public: + explicit ClickBasedCategoryRanker(PrefService* pref_service); + ~ClickBasedCategoryRanker() override; + + // CategoryRanker implementation. + bool Compare(Category left, Category right) const override; + void ClearHistory(base::Time begin, base::Time end) override; + void AppendCategoryIfNecessary(Category category) override; + void OnSuggestionOpened(Category category) override; + + static void RegisterProfilePrefs(PrefRegistrySimple* registry); + + // Returns passing margin, i.e. a number of extra clicks required to move a + // category upwards. For testing only. + static int GetPassingMargin(); + + // Returns number of top categories with extra margin (i.e. with increased + // passing margin). For testing only. + static int GetNumTopCategoriesWithExtraMargin(); + + private: + struct RankedCategory { + Category category; + int clicks; + + RankedCategory(Category category, int clicks); + }; + + int GetPositionPassingMargin( + std::vector<RankedCategory>::const_iterator category_position) const; + void RestoreDefaultOrder(); + void AppendKnownCategory(KnownCategories known_category); + bool ReadOrderFromPrefs(std::vector<RankedCategory>* result_categories); + void StoreOrderToPrefs(const std::vector<RankedCategory>& ordered_categories); + std::vector<RankedCategory>::iterator FindCategory(Category category); + bool ContainsCategory(Category category) const; + + std::vector<RankedCategory> ordered_categories_; + PrefService* pref_service_; + + DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRanker); +}; + +} // namespace ntp_snippets + +#endif // COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CLICK_BASED_SECTION_RANKER_H_
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc b/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc new file mode 100644 index 0000000..fc1cb55 --- /dev/null +++ b/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
@@ -0,0 +1,229 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/ntp_snippets/category_rankers/click_based_category_ranker.h" + +#include "base/memory/ptr_util.h" +#include "components/ntp_snippets/category.h" +#include "components/ntp_snippets/category_rankers/constant_category_ranker.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ntp_snippets { + +class ClickBasedCategoryRankerTest : public testing::Test { + public: + ClickBasedCategoryRankerTest() + : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()), + unused_remote_category_id_( + static_cast<int>(KnownCategories::LAST_KNOWN_REMOTE_CATEGORY) + 1) { + ClickBasedCategoryRanker::RegisterProfilePrefs(pref_service_->registry()); + + ranker_ = base::MakeUnique<ClickBasedCategoryRanker>(pref_service_.get()); + } + + int GetUnusedRemoteCategoryID() { return unused_remote_category_id_++; } + + Category GetUnusedRemoteCategory() { + return Category::FromIDValue(GetUnusedRemoteCategoryID()); + } + + bool CompareCategories(const Category& left, const Category& right) { + return ranker()->Compare(left, right); + } + + Category AddUnusedRemoteCategory() { + Category category = GetUnusedRemoteCategory(); + ranker()->AppendCategoryIfNecessary(category); + return category; + } + + void AddUnusedRemoteCategories(int quantity) { + for (int i = 0; i < quantity; ++i) { + AddUnusedRemoteCategory(); + } + } + + void ResetRanker() { + ranker_ = base::MakeUnique<ClickBasedCategoryRanker>(pref_service_.get()); + } + + void NotifyOnSuggestionOpened(int times, Category category) { + for (int i = 0; i < times; ++i) { + ranker()->OnSuggestionOpened(category); + } + } + + ClickBasedCategoryRanker* ranker() { return ranker_.get(); } + + private: + std::unique_ptr<TestingPrefServiceSimple> pref_service_; + int unused_remote_category_id_; + std::unique_ptr<ClickBasedCategoryRanker> ranker_; + + DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRankerTest); +}; + +TEST_F(ClickBasedCategoryRankerTest, ShouldSortRemoteCategoriesByWhenAdded) { + const Category first = GetUnusedRemoteCategory(); + const Category second = GetUnusedRemoteCategory(); + // Categories are added in decreasing id order to test that they are not + // compared by id. + ranker()->AppendCategoryIfNecessary(second); + ranker()->AppendCategoryIfNecessary(first); + EXPECT_TRUE(CompareCategories(second, first)); + EXPECT_FALSE(CompareCategories(first, second)); +} + +TEST_F(ClickBasedCategoryRankerTest, ShouldSortLocalCategoriesBeforeRemote) { + const Category remote_category = AddUnusedRemoteCategory(); + const Category local_category = + Category::FromKnownCategory(KnownCategories::BOOKMARKS); + EXPECT_TRUE(CompareCategories(local_category, remote_category)); + EXPECT_FALSE(CompareCategories(remote_category, local_category)); +} + +TEST_F(ClickBasedCategoryRankerTest, + CompareShouldReturnFalseForSameCategories) { + const Category remote_category = AddUnusedRemoteCategory(); + EXPECT_FALSE(CompareCategories(remote_category, remote_category)); + + const Category local_category = + Category::FromKnownCategory(KnownCategories::BOOKMARKS); + EXPECT_FALSE(CompareCategories(local_category, local_category)); +} + +TEST_F(ClickBasedCategoryRankerTest, + AddingMoreRemoteCategoriesShouldNotChangePreviousOrder) { + AddUnusedRemoteCategories(3); + + Category first = AddUnusedRemoteCategory(); + Category second = AddUnusedRemoteCategory(); + + ASSERT_TRUE(CompareCategories(first, second)); + ASSERT_FALSE(CompareCategories(second, first)); + + AddUnusedRemoteCategories(3); + + EXPECT_TRUE(CompareCategories(first, second)); + EXPECT_FALSE(CompareCategories(second, first)); +} + +TEST_F(ClickBasedCategoryRankerTest, ShouldChangeOrderOfNonTopCategories) { + // Add dummy remote categories to ensure that the following categories are not + // in the top anymore. + AddUnusedRemoteCategories( + ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin()); + + Category first = AddUnusedRemoteCategory(); + Category second = AddUnusedRemoteCategory(); + + ASSERT_TRUE(CompareCategories(first, second)); + ASSERT_FALSE(CompareCategories(second, first)); + + NotifyOnSuggestionOpened( + /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second); + + EXPECT_TRUE(CompareCategories(second, first)); + EXPECT_FALSE(CompareCategories(first, second)); +} + +TEST_F(ClickBasedCategoryRankerTest, + ShouldNotChangeOrderRightAfterOrderChange) { + // Add dummy remote categories to ensure that the following categories are not + // in the top anymore. + AddUnusedRemoteCategories( + ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin()); + + // Two non-top categories are added. + Category first = AddUnusedRemoteCategory(); + Category second = AddUnusedRemoteCategory(); + ASSERT_TRUE(CompareCategories(first, second)); + ASSERT_FALSE(CompareCategories(second, first)); + // Their order is changed. + NotifyOnSuggestionOpened( + /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second); + ASSERT_TRUE(CompareCategories(second, first)); + ASSERT_FALSE(CompareCategories(first, second)); + + // Click on the lower category. + NotifyOnSuggestionOpened(/*times=*/1, first); + + // Order should not change. + EXPECT_TRUE(CompareCategories(second, first)); + EXPECT_FALSE(CompareCategories(first, second)); +} + +TEST_F(ClickBasedCategoryRankerTest, + ShouldNotMoveCategoryMoreThanOncePerClick) { + // Add dummy remote categories to ensure that the following categories are not + // in the top anymore. + AddUnusedRemoteCategories( + ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin()); + + // Non-top categories are added. + Category first = AddUnusedRemoteCategory(); + Category second = AddUnusedRemoteCategory(); + Category third = AddUnusedRemoteCategory(); + + // Move the third category up. + NotifyOnSuggestionOpened( + /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), third); + EXPECT_TRUE(CompareCategories(third, second)); + // But only on one position even though the first category has low counts. + EXPECT_TRUE(CompareCategories(first, third)); + // However, another click must move it further. + NotifyOnSuggestionOpened(/*times=*/1, third); + EXPECT_TRUE(CompareCategories(third, first)); +} + +TEST_F(ClickBasedCategoryRankerTest, + ShouldNotMoveTopCategoryRightAfterThreshold) { + ASSERT_GE(ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin(), 1); + + // At least one top category is added from the default order. + std::vector<KnownCategories> default_order = + ConstantCategoryRanker::GetKnownCategoriesDefaultOrder(); + Category first = Category::FromKnownCategory(default_order[0]); + Category second = Category::FromKnownCategory(default_order[1]); + + // Try to move the second category up as if the first category was non-top. + NotifyOnSuggestionOpened( + /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second); + + // Nothing should change, because the first category is top. + EXPECT_TRUE(CompareCategories(first, second)); +} + +TEST_F(ClickBasedCategoryRankerTest, ShouldPersistOrderAndClicksWhenRestarted) { + // Add dummy remote categories to ensure that the following categories are not + // in the top anymore. + AddUnusedRemoteCategories( + ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin()); + + // Non-top categories are added. + Category first = AddUnusedRemoteCategory(); + Category second = AddUnusedRemoteCategory(); + Category third = AddUnusedRemoteCategory(); + + // Change the order. + NotifyOnSuggestionOpened( + /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), third); + ASSERT_TRUE(CompareCategories(third, second)); + ASSERT_TRUE(CompareCategories(first, third)); + + // Simulate Chrome restart. + ResetRanker(); + + // The old order must be preserved. + EXPECT_TRUE(CompareCategories(third, second)); + + // Clicks must be preserved as well. + NotifyOnSuggestionOpened( + /*times=*/1, third); + EXPECT_TRUE(CompareCategories(third, first)); +} + +} // namespace ntp_snippets
diff --git a/components/ntp_snippets/category_rankers/constant_category_ranker.cc b/components/ntp_snippets/category_rankers/constant_category_ranker.cc index 9fb648a7..859cf81 100644 --- a/components/ntp_snippets/category_rankers/constant_category_ranker.cc +++ b/components/ntp_snippets/category_rankers/constant_category_ranker.cc
@@ -9,22 +9,11 @@ namespace ntp_snippets { ConstantCategoryRanker::ConstantCategoryRanker() { - // Add all local categories in a fixed order. - AppendKnownCategory(KnownCategories::PHYSICAL_WEB_PAGES); - AppendKnownCategory(KnownCategories::DOWNLOADS); - AppendKnownCategory(KnownCategories::RECENT_TABS); - AppendKnownCategory(KnownCategories::FOREIGN_TABS); - AppendKnownCategory(KnownCategories::BOOKMARKS); - - DCHECK_EQ(static_cast<size_t>(KnownCategories::LOCAL_CATEGORIES_COUNT), - ordered_categories_.size()); - - // Known remote categories come after. Other remote categories will be ordered - // after these depending on when providers notify us about them using - // AppendCategoryIfNecessary. - // TODO(treib): Consider not adding ARTICLES here, so that providers can - // define the order themselves. - AppendKnownCategory(KnownCategories::ARTICLES); + std::vector<KnownCategories> ordered_known_categories = + GetKnownCategoriesDefaultOrder(); + for (KnownCategories known_category : ordered_known_categories) { + AppendKnownCategory(known_category); + } } ConstantCategoryRanker::~ConstantCategoryRanker() = default; @@ -71,6 +60,31 @@ // Ignored. The order is constant. } +// static +std::vector<KnownCategories> +ConstantCategoryRanker::GetKnownCategoriesDefaultOrder() { + std::vector<KnownCategories> categories; + + // Add all local categories in a fixed order. + categories.push_back(KnownCategories::PHYSICAL_WEB_PAGES); + categories.push_back(KnownCategories::DOWNLOADS); + categories.push_back(KnownCategories::RECENT_TABS); + categories.push_back(KnownCategories::FOREIGN_TABS); + categories.push_back(KnownCategories::BOOKMARKS); + + DCHECK_EQ(static_cast<size_t>(KnownCategories::LOCAL_CATEGORIES_COUNT), + categories.size()); + + // Known remote categories come after. Other remote categories will be ordered + // after these depending on when providers notify us about them using + // AppendCategoryIfNecessary. + // TODO(treib): Consider not adding ARTICLES here, so that providers can + // define the order themselves. + categories.push_back(KnownCategories::ARTICLES); + + return categories; +} + void ConstantCategoryRanker::AppendKnownCategory( KnownCategories known_category) { Category category = Category::FromKnownCategory(known_category);
diff --git a/components/ntp_snippets/category_rankers/constant_category_ranker.h b/components/ntp_snippets/category_rankers/constant_category_ranker.h index c01f0b9..6d2a143 100644 --- a/components/ntp_snippets/category_rankers/constant_category_ranker.h +++ b/components/ntp_snippets/category_rankers/constant_category_ranker.h
@@ -29,6 +29,8 @@ void AppendCategoryIfNecessary(Category category) override; void OnSuggestionOpened(Category category) override; + static std::vector<KnownCategories> GetKnownCategoriesDefaultOrder(); + private: void AppendKnownCategory(KnownCategories known_category);
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc index 15f1d02..2d054885 100644 --- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc +++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
@@ -242,10 +242,7 @@ ContentSuggestion PhysicalWebPageSuggestionsProvider::ConvertPhysicalWebPage( const DictionaryValue& page) const { std::string scanned_url, raw_resolved_url, title, description; - int scan_timestamp; bool success = page.GetString(physical_web::kScannedUrlKey, &scanned_url); - success = page.GetInteger(physical_web::kScanTimestampKey, &scan_timestamp) && - success; success = page.GetString(physical_web::kResolvedUrlKey, &raw_resolved_url) && success; success = page.GetString(physical_web::kTitleKey, &title) && success; @@ -260,10 +257,6 @@ resolved_url); DCHECK(base::IsStringUTF8(title)); suggestion.set_title(base::UTF8ToUTF16(title)); - // TODO(vitaliii): Set the time properly once the proper value is provided - // (see crbug.com/667722). - suggestion.set_publish_date( - base::Time::FromTimeT(static_cast<time_t>(scan_timestamp))); suggestion.set_publisher_name(base::UTF8ToUTF16(resolved_url.host())); DCHECK(base::IsStringUTF8(description)); suggestion.set_snippet_text(base::UTF8ToUTF16(description));
diff --git a/components/ntp_snippets/pref_names.cc b/components/ntp_snippets/pref_names.cc index fbf5505..dced63df 100644 --- a/components/ntp_snippets/pref_names.cc +++ b/components/ntp_snippets/pref_names.cc
@@ -61,5 +61,8 @@ const char kUserClassifierLastTimeToUseSuggestions[] = "ntp_suggestions.user_classifier.last_time_to_use_suggestions"; +const char kClickBasedCategoryRankerOrderWithClicks[] = + "ntp_suggestions.click_based_category_ranker.category_order_with_clicks"; + } // namespace prefs } // namespace ntp_snippets
diff --git a/components/ntp_snippets/pref_names.h b/components/ntp_snippets/pref_names.h index c0ddda0..5ff759f 100644 --- a/components/ntp_snippets/pref_names.h +++ b/components/ntp_snippets/pref_names.h
@@ -69,6 +69,9 @@ // The pref name for the last time content suggestions were used by the user. extern const char kUserClassifierLastTimeToUseSuggestions[]; +// The pref name for the current order of categories and their clicks. +extern const char kClickBasedCategoryRankerOrderWithClicks[]; + } // namespace prefs } // namespace ntp_snippets
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index 7ca12a6..7938dd0 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -139,7 +139,7 @@ # persistent IDs for all fields (but not for groups!) are needed. These are # specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs, # because doing so would break the deployed wire format! -# For your editing convenience: highest ID currently used: 357 +# For your editing convenience: highest ID currently used: 359 # # Placeholders: # The following placeholder strings are automatically substituted: @@ -1474,6 +1474,52 @@ 'arc_support': 'Disabling Google Sync will cause Android Backup and Restore to not function properly.', }, { + 'name': 'RoamingProfileSupportEnabled', + 'type': 'main', + 'schema': { 'type': 'boolean' }, + 'supported_on': ['chrome.win:57-'], + 'features': { + 'dynamic_refresh': False, + 'per_profile': False, + }, + 'example_value': True, + 'id': 358, + 'caption': '''Enable the creation of roaming copies for <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profile data.''', + 'tags': ['local-data-access'], + 'desc': '''Enables the creation of roaming copies for <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profile data. + + If you enable this setting, the settings stored in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profiles like bookmarks, autofill data, passwords, etc. will also be written to a file stored in the Roaming user profile folder or a location specified by the Administrator through the <ph name="ROAMINGPROFILELOCATION_POLICY_NAME">$1<ex>RoamingProfileLocation</ex></ph> policy. + + Google Sync is automatically disabled when this policy is enabled. + + To prevent accidental data leaks it is advisable to also set the <ph name="SIGNINALLOWED_POLICY_NAME">$1<ex>SigninAllowed</ex></ph> policy to False. + + If this policy is disabled or left not set only the regular local profiles will be used.''', + 'label': '''Enable the creation of roaming copies for <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profile data.''', + }, + { + 'name': 'RoamingProfileLocation', + 'type': 'string', + 'schema': { 'type': 'string' }, + 'supported_on': ['chrome.win:57-'], + 'features': { + 'dynamic_refresh': False, + 'per_profile': False, + }, + 'example_value': '${roaming_app_data}\\chrome-profile', + 'id': 359, + 'caption': '''Set the roaming profile directory''', + 'tags': ['local-data-access'], + 'desc': '''Configures the directory that <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will use for storing the roaming copy of the profiles. + + If you set this policy, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will use the provided directory to store the roaming copy of the profiles if the <ph name="ROAMINGPROFILESUPPORTENABLED_POLICY_NAME">$1<ex>RoamingProfileSupportEnabled</ex></ph> policy has been enabled. If the <ph name="ROAMINGPROFILESUPPORTENABLED_POLICY_NAME">$1<ex>RoamingProfileSupportEnabled</ex></ph> policy is disabled or left unset the value stored in this policy is not used. + + See https://www.chromium.org/administrators/policy-list-3/user-data-directory-variables for a list of variables that can be used. + + If this policy is left not set the default roaming profile path will be used.''', + 'label': '''Set the roaming profile directory''', + }, + { 'name': 'SigninAllowed', 'type': 'main', 'schema': { 'type': 'boolean' }, @@ -8060,7 +8106,7 @@ 'type': 'main', 'future': True, 'schema': { 'type': 'boolean' }, - 'supported_on': ['chrome.*:55-'], + 'supported_on': ['chrome.*:55-', 'android:57-'], 'features': { 'dynamic_refresh': False, 'per_profile': False,
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc index 328871f5..4d525b1 100644 --- a/components/security_state/core/security_state.cc +++ b/components/security_state/core/security_state.cc
@@ -171,9 +171,11 @@ if (used_policy_installed_certificate) return SECURE_WITH_POLICY_INSTALLED_CERT; - if (sha1_status == DEPRECATED_SHA1_MAJOR) - return DANGEROUS; - if (sha1_status == DEPRECATED_SHA1_MINOR) + // In most cases, SHA1 use is treated as a certificate error, in which case + // DANGEROUS will have been returned above. If SHA1 is permitted, we downgrade + // the security level. + if (sha1_status == DEPRECATED_SHA1_MAJOR || + sha1_status == DEPRECATED_SHA1_MINOR) return NONE; // Active mixed content is handled above.
diff --git a/components/signin/core/account_id/account_id.cc b/components/signin/core/account_id/account_id.cc index 00ceb2e2..1ffac80d 100644 --- a/components/signin/core/account_id/account_id.cc +++ b/components/signin/core/account_id/account_id.cc
@@ -16,25 +16,20 @@ namespace { -// Known account types. -const char kGoogle[] = "google"; - // Serialization keys const char kGaiaIdKey[] = "gaia_id"; const char kEmailKey[] = "email"; +const char kObjGuid[] = "obj_guid"; +const char kAccountTypeKey[] = "account_type"; + +// Serialization values for account type. +const char kGoogle[] = "google"; +const char kAd[] = "ad"; +const char kUnknown[] = "unknown"; // Prefix for GetAccountIdKey(). const char kKeyGaiaIdPrefix[] = "g-"; - -struct GoogleStringSingleton { - GoogleStringSingleton() : google(kGoogle) {} - - static GoogleStringSingleton* GetInstance() { - return base::Singleton<GoogleStringSingleton>::get(); - } - - const std::string google; -}; +const char kKeyAdIdPrefix[] = "a-"; } // anonymous namespace @@ -49,26 +44,46 @@ AccountId::AccountId() {} -AccountId::AccountId(const std::string& gaia_id, const std::string& user_email) - : gaia_id_(gaia_id), user_email_(user_email) { +AccountId::AccountId(const std::string& id, + const std::string& user_email, + const AccountType& account_type) + : id_(id), user_email_(user_email), account_type_(account_type) { + DCHECK(account_type != AccountType::UNKNOWN || id.empty()); + DCHECK(account_type != AccountType::ACTIVE_DIRECTORY || !id.empty()); // Fail if e-mail looks similar to GaiaIdKey. LOG_ASSERT(!base::StartsWith(user_email, kKeyGaiaIdPrefix, base::CompareCase::SENSITIVE) || user_email.find('@') != std::string::npos) - << "Bad e-mail: '" << user_email << "' with gaia_id='" << gaia_id << "'"; + << "Bad e-mail: '" << user_email << "' with gaia_id='" << id << "'"; // TODO(alemate): DCHECK(!email.empty()); // TODO(alemate): check gaia_id is not empty once it is required. } AccountId::AccountId(const AccountId& other) - : gaia_id_(other.gaia_id_), user_email_(other.user_email_) {} + : id_(other.id_), + user_email_(other.user_email_), + account_type_(other.account_type_) {} bool AccountId::operator==(const AccountId& other) const { - return (this == &other) || - (gaia_id_ == other.gaia_id_ && user_email_ == other.user_email_) || - (!gaia_id_.empty() && gaia_id_ == other.gaia_id_) || - (!user_email_.empty() && user_email_ == other.user_email_); + if (this == &other) + return true; + if (account_type_ == AccountType::UNKNOWN || + other.account_type_ == AccountType::UNKNOWN) + return user_email_ == other.user_email_; + if (account_type_ != other.account_type_) + return false; + switch (account_type_) { + case AccountType::GOOGLE: + return (id_ == other.id_ && user_email_ == other.user_email_) || + (!id_.empty() && id_ == other.id_) || + (!user_email_.empty() && user_email_ == other.user_email_); + case AccountType::ACTIVE_DIRECTORY: + return id_ == other.id_ && user_email_ == other.user_email_; + default: + NOTREACHED() << "Unknown account type"; + } + return false; } bool AccountId::operator!=(const AccountId& other) const { @@ -81,45 +96,70 @@ } bool AccountId::empty() const { - return gaia_id_.empty() && user_email_.empty(); + return id_.empty() && user_email_.empty() && + account_type_ == AccountType::UNKNOWN; } bool AccountId::is_valid() const { - return /* !gaia_id_.empty() && */ !user_email_.empty(); + switch (account_type_) { + case AccountType::GOOGLE: + return /* !id_.empty() && */ !user_email_.empty(); + case AccountType::ACTIVE_DIRECTORY: + return !id_.empty() && !user_email_.empty(); + case AccountType::UNKNOWN: + return id_.empty() && !user_email_.empty(); + } + NOTREACHED(); + return false; } void AccountId::clear() { - gaia_id_.clear(); + id_.clear(); user_email_.clear(); + account_type_ = AccountType::UNKNOWN; } -const std::string& AccountId::GetAccountType() const { - return GoogleStringSingleton::GetInstance()->google; +AccountType AccountId::GetAccountType() const { + return account_type_; } const std::string& AccountId::GetGaiaId() const { - return gaia_id_; + if (account_type_ != AccountType::GOOGLE) + NOTIMPLEMENTED() << "Failed to get gaia_id for non-Google account."; + return id_; +} + +const std::string& AccountId::GetObjGuid() const { + if (account_type_ != AccountType::ACTIVE_DIRECTORY) + NOTIMPLEMENTED() + << "Failed to get obj_guid for non-Active Directory account."; + return id_; } const std::string& AccountId::GetUserEmail() const { return user_email_; } -const std::string AccountId::GetAccountIdKey() const { -#ifdef NDEBUG - if (gaia_id_.empty()) - LOG(FATAL) << "GetAccountIdKey(): no gaia id for " << Serialize(); - -#else - CHECK(!gaia_id_.empty()); -#endif - - return std::string(kKeyGaiaIdPrefix) + gaia_id_; +bool AccountId::HasAccountIdKey() const { + return account_type_ != AccountType::UNKNOWN && !id_.empty(); } -void AccountId::SetGaiaId(const std::string& gaia_id) { - DCHECK(!gaia_id.empty()); - gaia_id_ = gaia_id; +const std::string AccountId::GetAccountIdKey() const { +#ifdef NDEBUG + if (id_.empty()) + LOG(FATAL) << "GetAccountIdKey(): no id for " << Serialize(); +#else + CHECK(!id_.empty()); +#endif + switch (GetAccountType()) { + case AccountType::GOOGLE: + return std::string(kKeyGaiaIdPrefix) + id_; + case AccountType::ACTIVE_DIRECTORY: + return std::string(kKeyAdIdPrefix) + id_; + default: + NOTREACHED() << "Unknown account type"; + } + return std::string(); } void AccountId::SetUserEmail(const std::string& email) { @@ -130,24 +170,75 @@ // static AccountId AccountId::FromUserEmail(const std::string& email) { // TODO(alemate): DCHECK(!email.empty()); - return AccountId(std::string() /* gaia_id */, email); + return AccountId(std::string() /* id */, email, AccountType::UNKNOWN); } +// static AccountId AccountId::FromGaiaId(const std::string& gaia_id) { DCHECK(!gaia_id.empty()); - return AccountId(gaia_id, std::string() /* email */); + return AccountId(gaia_id, std::string() /* email */, AccountType::GOOGLE); } // static AccountId AccountId::FromUserEmailGaiaId(const std::string& email, const std::string& gaia_id) { DCHECK(!(email.empty() && gaia_id.empty())); - return AccountId(gaia_id, email); + return AccountId(gaia_id, email, AccountType::GOOGLE); +} + +// static +AccountId AccountId::AdFromUserEmailObjGuid(const std::string& email, + const std::string& obj_guid) { + DCHECK(!email.empty() && !obj_guid.empty()); + return AccountId(obj_guid, email, AccountType::ACTIVE_DIRECTORY); +} + +// static +AccountId AccountId::AdFromObjGuid(const std::string& obj_guid) { + DCHECK(!obj_guid.empty()); + return AccountId(obj_guid, std::string() /* email */, + AccountType::ACTIVE_DIRECTORY); +} + +// static +AccountType AccountId::StringToAccountType( + const std::string& account_type_string) { + if (account_type_string == kGoogle) + return AccountType::GOOGLE; + if (account_type_string == kAd) + return AccountType::ACTIVE_DIRECTORY; + if (account_type_string == kUnknown) + return AccountType::UNKNOWN; + NOTREACHED() << "Unknown account type " << account_type_string; + return AccountType::UNKNOWN; +} + +// static +std::string AccountId::AccountTypeToString(const AccountType& account_type) { + switch (account_type) { + case AccountType::GOOGLE: + return kGoogle; + case AccountType::ACTIVE_DIRECTORY: + return kAd; + case AccountType::UNKNOWN: + return kUnknown; + } + return std::string(); } std::string AccountId::Serialize() const { base::DictionaryValue value; - value.SetString(kGaiaIdKey, gaia_id_); + switch (GetAccountType()) { + case AccountType::GOOGLE: + value.SetString(kGaiaIdKey, id_); + break; + case AccountType::ACTIVE_DIRECTORY: + value.SetString(kObjGuid, id_); + break; + case AccountType::UNKNOWN: + break; + } + value.SetString(kAccountTypeKey, AccountTypeToString(GetAccountType())); value.SetString(kEmailKey, user_email_); std::string serialized; @@ -167,23 +258,65 @@ std::string gaia_id; std::string user_email; + std::string obj_guid; + std::string account_type_string; + AccountType account_type = AccountType::GOOGLE; const bool found_gaia_id = dictionary_value->GetString(kGaiaIdKey, &gaia_id); const bool found_user_email = dictionary_value->GetString(kEmailKey, &user_email); + const bool found_obj_guid = dictionary_value->GetString(kObjGuid, &obj_guid); + const bool found_account_type = + dictionary_value->GetString(kAccountTypeKey, &account_type_string); + if (found_account_type) + account_type = StringToAccountType(account_type_string); - if (!found_gaia_id) - LOG(ERROR) << "gaia_id is not found in '" << serialized << "'"; + switch (account_type) { + case AccountType::GOOGLE: + if (found_obj_guid) + DLOG(ERROR) << "AccountType is 'google' but obj_guid is found in '" + << serialized << "'"; - if (!found_user_email) - LOG(ERROR) << "user_email is not found in '" << serialized << "'"; + if (!found_gaia_id) + DLOG(ERROR) << "gaia_id is not found in '" << serialized << "'"; - if (!found_gaia_id && !found_user_email) - return false; + if (!found_user_email) + DLOG(ERROR) << "user_email is not found in '" << serialized << "'"; - *account_id = FromUserEmailGaiaId(user_email, gaia_id); + if (!found_gaia_id && !found_user_email) + return false; - return true; + *account_id = FromUserEmailGaiaId(user_email, gaia_id); + return true; + + case AccountType::ACTIVE_DIRECTORY: + if (found_gaia_id) + DLOG(ERROR) + << "AccountType is 'active directory' but gaia_id is found in '" + << serialized << "'"; + + if (!found_obj_guid) { + DLOG(ERROR) << "obj_guid is not found in '" << serialized << "'"; + return false; + } + + if (!found_user_email) { + DLOG(ERROR) << "user_email is not found in '" << serialized << "'"; + } + + if (!found_obj_guid || !found_user_email) + return false; + + *account_id = AdFromUserEmailObjGuid(user_email, obj_guid); + return true; + + case AccountType::UNKNOWN: + if (!found_user_email) + return false; + *account_id = FromUserEmail(user_email); + return true; + } + return false; } const AccountId& EmptyAccountId() {
diff --git a/components/signin/core/account_id/account_id.h b/components/signin/core/account_id/account_id.h index 7cba697..4bc0205 100644 --- a/components/signin/core/account_id/account_id.h +++ b/components/signin/core/account_id/account_id.h
@@ -10,18 +10,28 @@ #include <string> #include "base/containers/hash_tables.h" +enum class AccountType { UNKNOWN, GOOGLE, ACTIVE_DIRECTORY }; + // Type that contains enough information to identify user. // // TODO(alemate): we are in the process of moving away from std::string as a // type for storing user identifier to AccountId. At this time GaiaId is mostly // empty, so this type is used as a replacement for e-mail string. // But in near future AccountId will become full feature user identifier. +// TODO(alemate): Rename functions and fields to reflect different types of +// accounts. (see crbug.com/672253) class AccountId { public: struct EmptyAccountId; AccountId(const AccountId& other); + // If any of the comparable AccountIds has AccountType == UNKNOWN then it + // compares emails. + // If both are not UNKNOWN and not equal then it returns false. + // If AccountType == GOOGLE then it checks if either ids or emails are equal. + // If AccountType == ACTIVE_DIRECTORY then it checks if ids and emails are + // equal. bool operator==(const AccountId& other) const; bool operator!=(const AccountId& other) const; bool operator<(const AccountId& right) const; @@ -31,28 +41,45 @@ bool is_valid() const; void clear(); - const std::string& GetAccountType() const; + AccountType GetAccountType() const; const std::string& GetGaiaId() const; + const std::string& GetObjGuid() const; // Users of AccountId should make no assumptions on the format of email. // I.e. it cannot be used as account identifier, because it is (in general) // non-comparable. const std::string& GetUserEmail() const; + // Returns true if |GetAccountIdKey| would return valid key. + bool HasAccountIdKey() const; // This returns prefixed some string that can be used as a storage key. // You should make no assumptions on the format of this string. const std::string GetAccountIdKey() const; - void SetGaiaId(const std::string& gaia_id); void SetUserEmail(const std::string& email); // This method is to be used during transition period only. + // AccountId with UNKNOWN AccountType; static AccountId FromUserEmail(const std::string& user_email); + // AccountId with GOOGLE AccountType; // This method is to be used during transition period only. static AccountId FromGaiaId(const std::string& gaia_id); // This method is the preferred way to construct AccountId if you have // full account information. + // AccountId with GOOGLE AccountType; static AccountId FromUserEmailGaiaId(const std::string& user_email, const std::string& gaia_id); + // These methods are used to construct Active Directory AccountIds. + // AccountId with ACTIVE_DIRECTORY AccountType; + static AccountId AdFromUserEmailObjGuid(const std::string& email, + const std::string& obj_guid); + // AccountId with ACTIVE_DIRECTORY AccountType; + static AccountId AdFromObjGuid(const std::string& obj_guid); + + // Translation functions between AccountType and std::string. Used for + // serialization. + static AccountType StringToAccountType( + const std::string& account_type_string); + static std::string AccountTypeToString(const AccountType& account_type); // These are (for now) unstable and cannot be used to store serialized data to // persistent storage. Only in-memory storage is safe. @@ -64,10 +91,13 @@ private: AccountId(); - AccountId(const std::string& gaia_id, const std::string& user_email); + AccountId(const std::string& id, + const std::string& user_email, + const AccountType& account_type); - std::string gaia_id_; + std::string id_; std::string user_email_; + AccountType account_type_ = AccountType::UNKNOWN; }; // Returns a reference to a singleton.
diff --git a/components/user_manager/known_user.cc b/components/user_manager/known_user.cc index 8ad90614..815d105e 100644 --- a/components/user_manager/known_user.cc +++ b/components/user_manager/known_user.cc
@@ -32,6 +32,12 @@ // Key of obfuscated GAIA id value. const char kGAIAIdKey[] = "gaia_id"; +// Key of obfuscated object guid value for Active Directory accounts. +const char kObjGuidKey[] = "obj_guid"; + +// Key of account type. +const char kAccountTypeKey[] = "account_type"; + // Key of whether this user ID refers to a SAML user. const char kUsingSAMLKey[] = "using_saml"; @@ -58,11 +64,29 @@ bool UserMatches(const AccountId& account_id, const base::DictionaryValue& dict) { std::string value; + if (account_id.GetAccountType() != AccountType::UNKNOWN && + dict.GetString(kAccountTypeKey, &value) && + account_id.GetAccountType() != AccountId::StringToAccountType(value)) { + return false; + } // TODO(alemate): update code once user id is really a struct. - bool has_gaia_id = dict.GetString(kGAIAIdKey, &value); - if (has_gaia_id && account_id.GetGaiaId() == value) - return true; + switch (account_id.GetAccountType()) { + case AccountType::GOOGLE: { + bool has_gaia_id = dict.GetString(kGAIAIdKey, &value); + if (has_gaia_id && account_id.GetGaiaId() == value) + return true; + break; + } + case AccountType::ACTIVE_DIRECTORY: { + bool has_obj_guid = dict.GetString(kObjGuidKey, &value); + if (has_obj_guid && account_id.GetObjGuid() == value) + return true; + break; + } + case AccountType::UNKNOWN: { + } + } bool has_email = dict.GetString(kCanonicalEmail, &value); if (has_email && account_id.GetUserEmail() == value) @@ -76,8 +100,20 @@ if (!account_id.GetUserEmail().empty()) dict.SetString(kCanonicalEmail, account_id.GetUserEmail()); - if (!account_id.GetGaiaId().empty()) - dict.SetString(kGAIAIdKey, account_id.GetGaiaId()); + switch (account_id.GetAccountType()) { + case AccountType::GOOGLE: + if (!account_id.GetGaiaId().empty()) + dict.SetString(kGAIAIdKey, account_id.GetGaiaId()); + break; + case AccountType::ACTIVE_DIRECTORY: + if (!account_id.GetObjGuid().empty()) + dict.SetString(kObjGuidKey, account_id.GetObjGuid()); + break; + case AccountType::UNKNOWN: + return; + } + dict.SetString(kAccountTypeKey, + AccountId::AccountTypeToString(account_id.GetAccountType())); } } // namespace @@ -216,48 +252,76 @@ } AccountId GetAccountId(const std::string& user_email, - const std::string& gaia_id) { + const std::string& id, + const AccountType& account_type) { + DCHECK((id.empty() && account_type == AccountType::UNKNOWN) || + (!id.empty() && account_type != AccountType::UNKNOWN)); // In tests empty accounts are possible. - if (user_email.empty() && gaia_id.empty()) + if (user_email.empty() && id.empty() && + account_type == AccountType::UNKNOWN) { return EmptyAccountId(); + } AccountId result(EmptyAccountId()); // UserManager is usually NULL in unit tests. - if (UserManager::IsInitialized() && - UserManager::Get()->GetPlatformKnownUserId(user_email, gaia_id, - &result)) { + if (account_type == AccountType::UNKNOWN && UserManager::IsInitialized() && + UserManager::Get()->GetPlatformKnownUserId(user_email, id, &result)) { return result; } - // We can have several users with the same gaia_id but different e-mails. - // The opposite case is not possible. std::string stored_gaia_id; + std::string stored_obj_guid; const std::string sanitized_email = user_email.empty() ? std::string() : gaia::CanonicalizeEmail(gaia::SanitizeEmail(user_email)); - if (!sanitized_email.empty() && - GetStringPref(AccountId::FromUserEmail(sanitized_email), kGAIAIdKey, - &stored_gaia_id)) { - if (!gaia_id.empty() && gaia_id != stored_gaia_id) - LOG(ERROR) << "User gaia id has changed. Sync will not work."; + if (!sanitized_email.empty()) { + if (GetStringPref(AccountId::FromUserEmail(sanitized_email), kGAIAIdKey, + &stored_gaia_id)) { + if (!id.empty()) { + DCHECK(account_type == AccountType::GOOGLE); + if (id != stored_gaia_id) + LOG(ERROR) << "User gaia id has changed. Sync will not work."; + } - // gaia_id is associated with cryptohome. - return AccountId::FromUserEmailGaiaId(sanitized_email, stored_gaia_id); + // gaia_id is associated with cryptohome. + return AccountId::FromUserEmailGaiaId(sanitized_email, stored_gaia_id); + } + + if (GetStringPref(AccountId::FromUserEmail(sanitized_email), kObjGuidKey, + &stored_obj_guid)) { + if (!id.empty()) { + DCHECK(account_type == AccountType::ACTIVE_DIRECTORY); + if (id != stored_obj_guid) + LOG(ERROR) << "User object guid has changed. Sync will not work."; + } + + // obj_guid is associated with cryptohome. + return AccountId::AdFromUserEmailObjGuid(sanitized_email, + stored_obj_guid); + } } std::string stored_email; - // GetStringPref() returns the first user record that matches - // given ID. So we will get the first one if there are multiples. - if (!gaia_id.empty() && GetStringPref(AccountId::FromGaiaId(gaia_id), - kCanonicalEmail, &stored_email)) { - return AccountId::FromUserEmailGaiaId(stored_email, gaia_id); + switch (account_type) { + case AccountType::GOOGLE: + if (GetStringPref(AccountId::FromGaiaId(id), kCanonicalEmail, + &stored_email)) { + return AccountId::FromUserEmailGaiaId(stored_email, id); + } + return AccountId::FromUserEmailGaiaId(sanitized_email, id); + case AccountType::ACTIVE_DIRECTORY: + if (GetStringPref(AccountId::AdFromObjGuid(id), kCanonicalEmail, + &stored_email)) { + return AccountId::AdFromUserEmailObjGuid(stored_email, id); + } + return AccountId::AdFromUserEmailObjGuid(sanitized_email, id); + case AccountType::UNKNOWN: + return AccountId::FromUserEmail(sanitized_email); } - - return (gaia_id.empty() - ? AccountId::FromUserEmail(user_email) - : AccountId::FromUserEmailGaiaId(user_email, gaia_id)); + NOTREACHED(); + return EmptyAccountId(); } std::vector<AccountId> GetKnownAccountIds() { @@ -274,10 +338,30 @@ if (known_users->GetDictionary(i, &element)) { std::string email; std::string gaia_id; + std::string obj_guid; const bool has_email = element->GetString(kCanonicalEmail, &email); const bool has_gaia_id = element->GetString(kGAIAIdKey, &gaia_id); - if (has_email || has_gaia_id) - result.push_back(AccountId::FromUserEmailGaiaId(email, gaia_id)); + const bool has_obj_guid = element->GetString(kObjGuidKey, &obj_guid); + AccountType account_type = AccountType::GOOGLE; + std::string account_type_string; + if (element->GetString(kAccountTypeKey, &account_type_string)) { + account_type = AccountId::StringToAccountType(account_type_string); + } + switch (account_type) { + case AccountType::GOOGLE: + if (has_email || has_gaia_id) { + result.push_back(AccountId::FromUserEmailGaiaId(email, gaia_id)); + } + break; + case AccountType::ACTIVE_DIRECTORY: + if (has_email && has_obj_guid) { + result.push_back( + AccountId::AdFromUserEmailObjGuid(email, obj_guid)); + } + break; + default: + NOTREACHED() << "Unknown account type"; + } } } return result; @@ -304,6 +388,23 @@ void UpdateGaiaID(const AccountId& account_id, const std::string& gaia_id) { SetStringPref(account_id, kGAIAIdKey, gaia_id); + SetStringPref(account_id, kAccountTypeKey, + AccountId::AccountTypeToString(AccountType::GOOGLE)); +} + +void UpdateId(const AccountId& account_id) { + switch (account_id.GetAccountType()) { + case AccountType::GOOGLE: + SetStringPref(account_id, kGAIAIdKey, account_id.GetGaiaId()); + break; + case AccountType::ACTIVE_DIRECTORY: + SetStringPref(account_id, kObjGuidKey, account_id.GetObjGuid()); + break; + case AccountType::UNKNOWN: + return; + } + SetStringPref(account_id, kAccountTypeKey, + AccountId::AccountTypeToString(account_id.GetAccountType())); } bool FindGaiaID(const AccountId& account_id, std::string* out_value) {
diff --git a/components/user_manager/known_user.h b/components/user_manager/known_user.h index c0dbd9cd..245ed57 100644 --- a/components/user_manager/known_user.h +++ b/components/user_manager/known_user.h
@@ -11,6 +11,7 @@ #include "components/user_manager/user_manager_export.h" class AccountId; +enum class AccountType; class PrefRegistrySimple; namespace base { @@ -74,7 +75,8 @@ // gaia_id. // This is a temporary call while migrating to AccountId. AccountId USER_MANAGER_EXPORT GetAccountId(const std::string& user_email, - const std::string& gaia_id); + const std::string& id, + const AccountType& account_type); // Returns true if |subsystem| data was migrated to GaiaId for the |account_id|. bool USER_MANAGER_EXPORT GetGaiaIdMigrationStatus(const AccountId& account_id, @@ -91,6 +93,10 @@ void USER_MANAGER_EXPORT UpdateGaiaID(const AccountId& account_id, const std::string& gaia_id); +// Updates |account_id.account_type_| and |account_id.GetGaiaId()| or +// |account_id.GetObjGuid()| for user with |account_id|. +void USER_MANAGER_EXPORT UpdateId(const AccountId& account_id); + // Find GAIA ID for user with |account_id|, fill in |out_value| and return // true // if GAIA ID was found or false otherwise.
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc index 6710f25d..24772d6b 100644 --- a/components/user_manager/user_manager_base.cc +++ b/components/user_manager/user_manager_base.cc
@@ -494,7 +494,8 @@ continue; } - const AccountId account_id = known_user::GetAccountId(email, std::string()); + const AccountId account_id = known_user::GetAccountId( + email, std::string() /* id */, AccountType::UNKNOWN); if (existing_users.find(account_id) != existing_users.end() || !users_set->insert(account_id).second) {
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 3e1a862b..f619693 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -77,8 +77,6 @@ "//device/power_save_blocker", "//device/screen_orientation/public/interfaces", "//device/sensors/public/cpp", - "//device/time_zone_monitor", - "//device/time_zone_monitor/public/interfaces", "//device/vibration", "//device/wake_lock", "//gin", @@ -107,7 +105,7 @@ "//printing/features", "//services/catalog/public/interfaces:constants", "//services/device:lib", - "//services/device/public/cpp", + "//services/device/public/interfaces", "//services/file:lib", "//services/file/public/interfaces", "//services/service_manager", @@ -393,11 +391,14 @@ "cache_storage/cache_storage_cache.h", "cache_storage/cache_storage_cache_handle.cc", "cache_storage/cache_storage_cache_handle.h", + "cache_storage/cache_storage_cache_observer.h", "cache_storage/cache_storage_context_impl.cc", "cache_storage/cache_storage_context_impl.h", "cache_storage/cache_storage_dispatcher_host.cc", "cache_storage/cache_storage_dispatcher_host.h", "cache_storage/cache_storage_histogram_macros.h", + "cache_storage/cache_storage_index.cc", + "cache_storage/cache_storage_index.h", "cache_storage/cache_storage_manager.cc", "cache_storage/cache_storage_manager.h", "cache_storage/cache_storage_operation.cc", @@ -527,6 +528,8 @@ "dom_storage/dom_storage_session.h", "dom_storage/dom_storage_task_runner.cc", "dom_storage/dom_storage_task_runner.h", + "dom_storage/local_storage_context_mojo.cc", + "dom_storage/local_storage_context_mojo.h", "dom_storage/local_storage_database_adapter.cc", "dom_storage/local_storage_database_adapter.h", "dom_storage/session_storage_database.cc",
diff --git a/content/browser/DEPS b/content/browser/DEPS index c4c944f..e544256 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS
@@ -25,7 +25,6 @@ "+device/power_save_blocker", "+device/screen_orientation/public/interfaces", "+device/sensors/public", - "+device/time_zone_monitor", # For TimeZoneMonitor creation. "+device/vibration", # For Vibration API "+device/vr", # For WebVR API "+device/wake_lock",
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc index 30ff8d4..1e7f2c3 100644 --- a/content/browser/browser_context.cc +++ b/content/browser/browser_context.cc
@@ -40,7 +40,7 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "services/device/device_service.h" -#include "services/device/public/cpp/constants.h" +#include "services/device/public/interfaces/constants.mojom.h" #include "services/file/file_service.h" #include "services/file/public/interfaces/constants.mojom.h" #include "services/file/user_id_map.h" @@ -464,7 +464,7 @@ info.factory = base::Bind(&device::CreateDeviceService, BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE)); - connection->AddEmbeddedService(device::kDeviceServiceName, info); + connection->AddEmbeddedService(device::mojom::kServiceName, info); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kMojoLocalStorage)) {
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index 80a7c51..ef5fea2 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc
@@ -92,7 +92,6 @@ #include "content/public/common/result_codes.h" #include "device/battery/battery_status_service.h" #include "device/gamepad/gamepad_service.h" -#include "device/time_zone_monitor/time_zone_monitor.h" #include "media/base/media.h" #include "media/base/user_input_monitor.h" #include "media/midi/midi_service.h" @@ -1540,13 +1539,6 @@ { TRACE_EVENT0("startup", - "BrowserMainLoop::BrowserThreadsStarted::TimeZoneMonitor"); - time_zone_monitor_ = - device::TimeZoneMonitor::Create(file_thread_->task_runner()); - } - - { - TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted::SaveFileManager"); save_file_manager_ = new SaveFileManager(); }
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h index ad6c04c..1c4dcbe 100644 --- a/content/browser/browser_main_loop.h +++ b/content/browser/browser_main_loop.h
@@ -35,10 +35,6 @@ } // namespace trace_event } // namespace base -namespace device { -class TimeZoneMonitor; -} - namespace discardable_memory { class DiscardableSharedMemoryManager; } @@ -146,9 +142,6 @@ media::UserInputMonitor* user_input_monitor() const { return user_input_monitor_.get(); } - device::TimeZoneMonitor* time_zone_monitor() const { - return time_zone_monitor_.get(); - } discardable_memory::DiscardableSharedMemoryManager* discardable_shared_memory_manager() const { return discardable_shared_memory_manager_.get(); @@ -319,7 +312,6 @@ std::unique_ptr<ResourceDispatcherHostImpl> resource_dispatcher_host_; std::unique_ptr<MediaStreamManager> media_stream_manager_; std::unique_ptr<SpeechRecognitionManagerImpl> speech_recognition_manager_; - std::unique_ptr<device::TimeZoneMonitor> time_zone_monitor_; std::unique_ptr<discardable_memory::DiscardableSharedMemoryManager> discardable_shared_memory_manager_; scoped_refptr<SaveFileManager> save_file_manager_;
diff --git a/content/browser/cache_storage/README.md b/content/browser/cache_storage/README.md index 014b9882..7b3758d 100644 --- a/content/browser/cache_storage/README.md +++ b/content/browser/cache_storage/README.md
@@ -62,6 +62,13 @@ 3. Owned by `CacheStorage` and deleted either when `CacheStorage` deletes or when the last `CacheStorageCacheHandle` for the cache is gone. +### CacheStorageIndex +1. Manages an ordered collection of metadata + (CacheStorageIndex::CacheStorageMetadata) for each CacheStorageCache owned + by a given CacheStorage instance. +2. Is serialized by CacheStorage::CacheLoader (WriteIndex/LoadIndex) as a + Protobuf file. + ### CacheStorageCacheHandle 1. Holds a weak reference to a `CacheStorageCache`. 2. When the last `CacheStorageCacheHandle` to a `CacheStorageCache` is
diff --git a/content/browser/cache_storage/cache_storage.cc b/content/browser/cache_storage/cache_storage.cc index 63b03c5..d32fc3d9 100644 --- a/content/browser/cache_storage/cache_storage.cc +++ b/content/browser/cache_storage/cache_storage.cc
@@ -28,6 +28,7 @@ #include "content/browser/cache_storage/cache_storage.pb.h" #include "content/browser/cache_storage/cache_storage_cache.h" #include "content/browser/cache_storage/cache_storage_cache_handle.h" +#include "content/browser/cache_storage/cache_storage_index.h" #include "content/browser/cache_storage/cache_storage_scheduler.h" #include "content/public/browser/browser_thread.h" #include "net/base/directory_lister.h" @@ -47,24 +48,18 @@ return valued_hexed_hash; } -void SizeRetrievedFromCache( - std::unique_ptr<CacheStorageCacheHandle> cache_handle, - const base::Closure& closure, - int64_t* accumulator, - int64_t size) { - *accumulator += size; - closure.Run(); -} - void SizeRetrievedFromAllCaches(std::unique_ptr<int64_t> accumulator, const CacheStorage::SizeCallback& callback) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, *accumulator)); } +void DoNothingWithBool(bool success) {} + } // namespace const char CacheStorage::kIndexFileName[] = "index.txt"; +const int64_t CacheStorage::kSizeUnknown; struct CacheStorage::CacheMatchResponse { CacheMatchResponse() = default; @@ -81,8 +76,8 @@ typedef base::Callback<void(std::unique_ptr<CacheStorageCache>)> CacheCallback; typedef base::Callback<void(bool)> BoolCallback; - typedef base::Callback<void(std::unique_ptr<std::vector<std::string>>)> - StringVectorCallback; + using CacheStorageIndexLoadCallback = + base::Callback<void(std::unique_ptr<CacheStorageIndex>)>; CacheLoader( base::SequencedTaskRunner* cache_task_runner, @@ -105,7 +100,8 @@ // Creates a CacheStorageCache with the given name. It does not attempt to // load the backend, that happens lazily when the cache is used. virtual std::unique_ptr<CacheStorageCache> CreateCache( - const std::string& cache_name) = 0; + const std::string& cache_name, + int64_t cache_size) = 0; // Deletes any pre-existing cache of the same name and then loads it. virtual void PrepareNewCacheDestination(const std::string& cache_name, @@ -115,13 +111,12 @@ // removing the cache's directory. virtual void CleanUpDeletedCache(CacheStorageCache* cache) = 0; - // Writes the cache names (and sizes) to disk if applicable. - virtual void WriteIndex(const StringVector& cache_names, + // Writes the cache index to disk if applicable. + virtual void WriteIndex(const CacheStorageIndex& index, const BoolCallback& callback) = 0; - // Loads the cache names from disk if applicable. - virtual void LoadIndex(std::unique_ptr<std::vector<std::string>> cache_names, - const StringVectorCallback& callback) = 0; + // Loads the cache index from disk if applicable. + virtual void LoadIndex(const CacheStorageIndexLoadCallback& callback) = 0; // Called when CacheStorage has created a cache. Used to hold onto a handle to // the cache if necessary. @@ -168,8 +163,8 @@ cache_storage, origin) {} - std::unique_ptr<CacheStorageCache> CreateCache( - const std::string& cache_name) override { + std::unique_ptr<CacheStorageCache> CreateCache(const std::string& cache_name, + int64_t cache_size) override { return CacheStorageCache::CreateMemoryCache( origin_, cache_name, cache_storage_, request_context_getter_, quota_manager_proxy_, blob_context_); @@ -177,20 +172,20 @@ void PrepareNewCacheDestination(const std::string& cache_name, const CacheCallback& callback) override { - std::unique_ptr<CacheStorageCache> cache = CreateCache(cache_name); + std::unique_ptr<CacheStorageCache> cache = + CreateCache(cache_name, 0 /*cache_size*/); callback.Run(std::move(cache)); } void CleanUpDeletedCache(CacheStorageCache* cache) override {} - void WriteIndex(const StringVector& cache_names, + void WriteIndex(const CacheStorageIndex& index, const BoolCallback& callback) override { callback.Run(true); } - void LoadIndex(std::unique_ptr<std::vector<std::string>> cache_names, - const StringVectorCallback& callback) override { - callback.Run(std::move(cache_names)); + void LoadIndex(const CacheStorageIndexLoadCallback& callback) override { + callback.Run(base::MakeUnique<CacheStorageIndex>()); } void NotifyCacheCreated( @@ -236,8 +231,8 @@ origin_path_(origin_path), weak_ptr_factory_(this) {} - std::unique_ptr<CacheStorageCache> CreateCache( - const std::string& cache_name) override { + std::unique_ptr<CacheStorageCache> CreateCache(const std::string& cache_name, + int64_t cache_size) override { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(base::ContainsKey(cache_name_to_cache_dir_, cache_name)); @@ -245,7 +240,8 @@ base::FilePath cache_path = origin_path_.AppendASCII(cache_dir); return CacheStorageCache::CreatePersistentCache( origin_, cache_name, cache_storage_, cache_path, - request_context_getter_, quota_manager_proxy_, blob_context_); + request_context_getter_, quota_manager_proxy_, blob_context_, + cache_size); } void PrepareNewCacheDestination(const std::string& cache_name, @@ -282,7 +278,7 @@ } cache_name_to_cache_dir_[cache_name] = cache_dir; - callback.Run(CreateCache(cache_name)); + callback.Run(CreateCache(cache_name, CacheStorage::kSizeUnknown)); } void CleanUpDeletedCache(CacheStorageCache* cache) override { @@ -302,26 +298,30 @@ base::DeleteFile(cache_path, true /* recursive */); } - void WriteIndex(const StringVector& cache_names, + void WriteIndex(const CacheStorageIndex& index, const BoolCallback& callback) override { DCHECK_CURRENTLY_ON(BrowserThread::IO); // 1. Create the index file as a string. (WriteIndex) // 2. Write the file to disk. (WriteIndexWriteToFileInPool) - proto::CacheStorageIndex index; - index.set_origin(origin_.spec()); + proto::CacheStorageIndex protobuf_index; + protobuf_index.set_origin(origin_.spec()); - for (size_t i = 0u, max = cache_names.size(); i < max; ++i) { - DCHECK(base::ContainsKey(cache_name_to_cache_dir_, cache_names[i])); + for (const auto& cache_metadata : index.ordered_cache_metadata()) { + DCHECK(base::ContainsKey(cache_name_to_cache_dir_, cache_metadata.name)); - proto::CacheStorageIndex::Cache* index_cache = index.add_cache(); - index_cache->set_name(cache_names[i]); - index_cache->set_cache_dir(cache_name_to_cache_dir_[cache_names[i]]); + proto::CacheStorageIndex::Cache* index_cache = protobuf_index.add_cache(); + index_cache->set_name(cache_metadata.name); + index_cache->set_cache_dir(cache_name_to_cache_dir_[cache_metadata.name]); + if (cache_metadata.size == CacheStorage::kSizeUnknown) + index_cache->clear_size(); + else + index_cache->set_size(cache_metadata.size); } std::string serialized; - bool success = index.SerializeToString(&serialized); + bool success = protobuf_index.SerializeToString(&serialized); DCHECK(success); base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp"); @@ -348,47 +348,38 @@ return base::ReplaceFile(tmp_path, index_path, NULL); } - void LoadIndex(std::unique_ptr<std::vector<std::string>> names, - const StringVectorCallback& callback) override { + void LoadIndex(const CacheStorageIndexLoadCallback& callback) override { DCHECK_CURRENTLY_ON(BrowserThread::IO); - // 1. Read the file from disk. (LoadIndexReadFileInPool) - // 2. Parse file and return the names of the caches (LoadIndexDidReadFile) - - base::FilePath index_path = - origin_path_.AppendASCII(CacheStorage::kIndexFileName); - PostTaskAndReplyWithResult( cache_task_runner_.get(), FROM_HERE, - base::Bind(&SimpleCacheLoader::ReadAndMigrateIndexInPool, index_path), - base::Bind(&SimpleCacheLoader::LoadIndexDidReadFile, - weak_ptr_factory_.GetWeakPtr(), base::Passed(&names), - callback)); + base::Bind(&SimpleCacheLoader::ReadAndMigrateIndexInPool, origin_path_), + base::Bind(&SimpleCacheLoader::LoadIndexDidReadIndex, + weak_ptr_factory_.GetWeakPtr(), callback)); } - void LoadIndexDidReadFile(std::unique_ptr<std::vector<std::string>> names, - const StringVectorCallback& callback, - const std::string& serialized) { + void LoadIndexDidReadIndex(const CacheStorageIndexLoadCallback& callback, + proto::CacheStorageIndex protobuf_index) { DCHECK_CURRENTLY_ON(BrowserThread::IO); std::unique_ptr<std::set<std::string>> cache_dirs( new std::set<std::string>); - proto::CacheStorageIndex index; - if (index.ParseFromString(serialized)) { - for (int i = 0, max = index.cache_size(); i < max; ++i) { - const proto::CacheStorageIndex::Cache& cache = index.cache(i); - DCHECK(cache.has_cache_dir()); - names->push_back(cache.name()); - cache_name_to_cache_dir_[cache.name()] = cache.cache_dir(); - cache_dirs->insert(cache.cache_dir()); - } + auto index = base::MakeUnique<CacheStorageIndex>(); + for (int i = 0, max = protobuf_index.cache_size(); i < max; ++i) { + const proto::CacheStorageIndex::Cache& cache = protobuf_index.cache(i); + DCHECK(cache.has_cache_dir()); + int64_t cache_size = + cache.has_size() ? cache.size() : CacheStorage::kSizeUnknown; + index->Insert(CacheStorageIndex::CacheMetadata(cache.name(), cache_size)); + cache_name_to_cache_dir_[cache.name()] = cache.cache_dir(); + cache_dirs->insert(cache.cache_dir()); } cache_task_runner_->PostTask( FROM_HERE, base::Bind(&DeleteUnreferencedCachesInPool, origin_path_, base::Passed(&cache_dirs))); - callback.Run(std::move(names)); + callback.Run(std::move(index)); } void NotifyCacheDoomed( @@ -424,22 +415,41 @@ } // Runs on cache_task_runner_ - static std::string MigrateCachesIfNecessaryInPool( - const std::string& body, - const base::FilePath& index_path) { - proto::CacheStorageIndex index; - if (!index.ParseFromString(body)) - return body; + static proto::CacheStorageIndex ReadAndMigrateIndexInPool( + const base::FilePath& origin_path) { + const base::FilePath index_path = + origin_path.AppendASCII(CacheStorage::kIndexFileName); - base::FilePath origin_path = index_path.DirName(); - bool index_is_dirty = false; - const std::string kBadIndexState(""); + proto::CacheStorageIndex index; + std::string body; + if (!base::ReadFileToString(index_path, &body) || + !index.ParseFromString(body)) + return proto::CacheStorageIndex(); + body.clear(); + + base::File::Info file_info; + base::Time index_last_modified; + if (GetFileInfo(index_path, &file_info)) + index_last_modified = file_info.last_modified; + bool index_modified = false; // Look for caches that have no cache_dir. Give any such caches a directory // with a random name and move them there. Then, rewrite the index file. + // Additionally invalidate the size of any index entries where the cache was + // modified after the index (making it out-of-date). for (int i = 0, max = index.cache_size(); i < max; ++i) { const proto::CacheStorageIndex::Cache& cache = index.cache(i); - if (!cache.has_cache_dir()) { + if (cache.has_cache_dir()) { + if (cache.has_size()) { + base::FilePath cache_dir = origin_path.AppendASCII(cache.cache_dir()); + if (!GetFileInfo(cache_dir, &file_info) || + index_last_modified <= file_info.last_modified) { + // Index is older than this cache, so invalidate index entries that + // may change as a result of cache operations. + index.mutable_cache(i)->clear_size(); + } + } + } else { // Find a new home for the cache. base::FilePath legacy_cache_path = origin_path.AppendASCII(HexedHash(cache.name())); @@ -454,34 +464,24 @@ // If the move fails then the cache is in a bad state. Return an empty // index so that the CacheStorage can start fresh. The unreferenced // caches will be discarded later in initialization. - return kBadIndexState; + return proto::CacheStorageIndex(); } index.mutable_cache(i)->set_cache_dir(cache_dir); - index_is_dirty = true; + index.mutable_cache(i)->clear_size(); + index_modified = true; } } - if (index_is_dirty) { - std::string new_body; - if (!index.SerializeToString(&new_body)) - return kBadIndexState; - if (base::WriteFile(index_path, new_body.c_str(), new_body.size()) != - base::checked_cast<int>(new_body.size())) - return kBadIndexState; - return new_body; + if (index_modified) { + if (!index.SerializeToString(&body)) + return proto::CacheStorageIndex(); + if (base::WriteFile(index_path, body.c_str(), body.size()) != + base::checked_cast<int>(body.size())) + return proto::CacheStorageIndex(); } - return body; - } - - // Runs on cache_task_runner_ - static std::string ReadAndMigrateIndexInPool( - const base::FilePath& index_path) { - std::string body; - base::ReadFileToString(index_path, &body); - - return MigrateCachesIfNecessaryInPool(body, index_path); + return index; } const base::FilePath origin_path_; @@ -570,7 +570,7 @@ cache_name, scheduler_->WrapCallbackToRunNext(callback))); } -void CacheStorage::EnumerateCaches(const StringsCallback& callback) { +void CacheStorage::EnumerateCaches(const IndexCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!initialized_) @@ -646,6 +646,50 @@ scheduler_->WrapCallbackToRunNext(callback))); } +void CacheStorage::ScheduleWriteIndex() { + static const int64_t kWriteIndexDelaySecs = 5; + DCHECK_CURRENTLY_ON(BrowserThread::IO); + index_write_task_.Reset(base::Bind(&CacheStorage::WriteIndex, + weak_factory_.GetWeakPtr(), + base::Bind(&DoNothingWithBool))); + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, index_write_task_.callback(), + base::TimeDelta::FromSeconds(kWriteIndexDelaySecs)); +} + +void CacheStorage::WriteIndex(const base::Callback<void(bool)>& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + scheduler_->ScheduleOperation( + base::Bind(&CacheStorage::WriteIndexImpl, weak_factory_.GetWeakPtr(), + scheduler_->WrapCallbackToRunNext(callback))); +} + +void CacheStorage::WriteIndexImpl(const base::Callback<void(bool)>& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + cache_loader_->WriteIndex(*cache_index_, callback); +} + +bool CacheStorage::InitiateScheduledIndexWriteForTest( + const base::Callback<void(bool)>& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (index_write_pending()) { + index_write_task_.Cancel(); + WriteIndex(callback); + return true; + } + callback.Run(true /* success */); + return false; +} + +void CacheStorage::CacheSizeUpdated(const CacheStorageCache* cache, + int64_t size) { + // Should not be called for doomed caches. + DCHECK(!base::ContainsKey(doomed_caches_, + const_cast<CacheStorageCache*>(cache))); + cache_index_->SetCacheSize(cache->cache_name(), size); + ScheduleWriteIndex(); +} + void CacheStorage::StartAsyncOperationForTesting() { scheduler_->ScheduleOperation(base::Bind(&base::DoNothing)); } @@ -674,29 +718,28 @@ DCHECK(!initialized_); DCHECK(initializing_); - // 1. Get the list of cache names (async call) + // 1. Get the cache index (async call) // 2. For each cache name, load the cache (async call) // 3. Once each load is complete, update the map variables. // 4. Call the list of waiting callbacks. - std::unique_ptr<std::vector<std::string>> indexed_cache_names( - new std::vector<std::string>()); - - cache_loader_->LoadIndex(std::move(indexed_cache_names), - base::Bind(&CacheStorage::LazyInitDidLoadIndex, + cache_loader_->LoadIndex(base::Bind(&CacheStorage::LazyInitDidLoadIndex, weak_factory_.GetWeakPtr())); } void CacheStorage::LazyInitDidLoadIndex( - std::unique_ptr<std::vector<std::string>> indexed_cache_names) { + std::unique_ptr<CacheStorageIndex> index) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(cache_map_.empty()); - for (size_t i = 0u, max = indexed_cache_names->size(); i < max; ++i) { - cache_map_.insert(std::make_pair(indexed_cache_names->at(i), + for (const auto& cache_metadata : index->ordered_cache_metadata()) { + cache_map_.insert(std::make_pair(cache_metadata.name, std::unique_ptr<CacheStorageCache>())); - ordered_cache_names_.push_back(indexed_cache_names->at(i)); } + DCHECK(!cache_index_); + cache_index_ = std::move(index); + initializing_ = false; initialized_ = true; @@ -735,13 +778,13 @@ CacheStorageCache* cache_ptr = cache.get(); cache_map_.insert(std::make_pair(cache_name, std::move(cache))); - ordered_cache_names_.push_back(cache_name); + cache_index_->Insert( + CacheStorageIndex::CacheMetadata(cache_name, cache_ptr->cache_size())); cache_loader_->WriteIndex( - ordered_cache_names_, - base::Bind(&CacheStorage::CreateCacheDidWriteIndex, - weak_factory_.GetWeakPtr(), callback, - base::Passed(CreateCacheHandle(cache_ptr)))); + *cache_index_, base::Bind(&CacheStorage::CreateCacheDidWriteIndex, + weak_factory_.GetWeakPtr(), callback, + base::Passed(CreateCacheHandle(cache_ptr)))); cache_loader_->NotifyCacheCreated(cache_name, CreateCacheHandle(cache_ptr)); } @@ -766,45 +809,43 @@ void CacheStorage::DeleteCacheImpl(const std::string& cache_name, const BoolAndErrorCallback& callback) { - if (!GetLoadedCache(cache_name)) { + std::unique_ptr<CacheStorageCacheHandle> cache_handle = + GetLoadedCache(cache_name); + if (!cache_handle) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, false, CACHE_STORAGE_ERROR_NOT_FOUND)); return; } - // Delete the name from ordered_cache_names_. - StringVector original_ordered_cache_names = ordered_cache_names_; - StringVector::iterator iter = std::find( - ordered_cache_names_.begin(), ordered_cache_names_.end(), cache_name); - DCHECK(iter != ordered_cache_names_.end()); - ordered_cache_names_.erase(iter); - - cache_loader_->WriteIndex(ordered_cache_names_, - base::Bind(&CacheStorage::DeleteCacheDidWriteIndex, - weak_factory_.GetWeakPtr(), cache_name, - original_ordered_cache_names, callback)); + cache_handle->value()->SetObserver(nullptr); + cache_index_->DoomCache(cache_name); + cache_loader_->WriteIndex( + *cache_index_, + base::Bind(&CacheStorage::DeleteCacheDidWriteIndex, + weak_factory_.GetWeakPtr(), + base::Passed(std::move(cache_handle)), callback)); } void CacheStorage::DeleteCacheDidWriteIndex( - const std::string& cache_name, - const StringVector& original_ordered_cache_names, + std::unique_ptr<CacheStorageCacheHandle> cache_handle, const BoolAndErrorCallback& callback, bool success) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!success) { - // Undo any changes if the change couldn't be written to disk. - ordered_cache_names_ = original_ordered_cache_names; + // Undo any changes if the index couldn't be written to disk. + cache_index_->RestoreDoomedCache(); + cache_handle->value()->SetObserver(this); callback.Run(false, CACHE_STORAGE_ERROR_STORAGE); return; } - // Make sure that a cache handle exists for the doomed cache to ensure that - // DeleteCacheFinalize is called. - std::unique_ptr<CacheStorageCacheHandle> cache_handle = - GetLoadedCache(cache_name); + cache_index_->FinalizeDoomedCache(); - CacheMap::iterator map_iter = cache_map_.find(cache_name); + CacheMap::iterator map_iter = + cache_map_.find(cache_handle->value()->cache_name()); + DCHECK(map_iter != cache_map_.end()); + doomed_caches_.insert( std::make_pair(map_iter->second.get(), std::move(map_iter->second))); cache_map_.erase(map_iter); @@ -834,8 +875,8 @@ doomed_caches_.erase(doomed_caches_iter); } -void CacheStorage::EnumerateCachesImpl(const StringsCallback& callback) { - callback.Run(ordered_cache_names_); +void CacheStorage::EnumerateCachesImpl(const IndexCallback& callback) { + callback.Run(*cache_index_); } void CacheStorage::MatchCacheImpl( @@ -876,17 +917,18 @@ const CacheStorageCacheQueryParams& match_params, const CacheStorageCache::ResponseCallback& callback) { std::vector<CacheMatchResponse>* match_responses = - new std::vector<CacheMatchResponse>(ordered_cache_names_.size()); + new std::vector<CacheMatchResponse>(cache_index_->num_entries()); base::Closure barrier_closure = base::BarrierClosure( - ordered_cache_names_.size(), + cache_index_->num_entries(), base::Bind(&CacheStorage::MatchAllCachesDidMatchAll, weak_factory_.GetWeakPtr(), base::Passed(base::WrapUnique(match_responses)), callback)); - for (size_t i = 0, max = ordered_cache_names_.size(); i < max; ++i) { + size_t idx = 0; + for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) { std::unique_ptr<CacheStorageCacheHandle> cache_handle = - GetLoadedCache(ordered_cache_names_[i]); + GetLoadedCache(cache_metadata.name); DCHECK(cache_handle); CacheStorageCache* cache_ptr = cache_handle->value(); @@ -895,7 +937,8 @@ base::Bind(&CacheStorage::MatchAllCachesDidMatch, weak_factory_.GetWeakPtr(), base::Passed(std::move(cache_handle)), - &match_responses->at(i), barrier_closure)); + &match_responses->at(idx), barrier_closure)); + idx++; } } @@ -982,8 +1025,8 @@ CacheStorageCache* cache = map_iter->second.get(); if (!cache) { - std::unique_ptr<CacheStorageCache> new_cache = - cache_loader_->CreateCache(cache_name); + std::unique_ptr<CacheStorageCache> new_cache = cache_loader_->CreateCache( + cache_name, cache_index_->GetCacheSize(cache_name)); CacheStorageCache* cache_ptr = new_cache.get(); map_iter->second = std::move(new_cache); @@ -993,6 +1036,16 @@ return CreateCacheHandle(cache); } +void CacheStorage::SizeRetrievedFromCache( + std::unique_ptr<CacheStorageCacheHandle> cache_handle, + const base::Closure& closure, + int64_t* accumulator, + int64_t size) { + cache_index_->SetCacheSize(cache_handle->value()->cache_name(), size); + *accumulator += size; + closure.Run(); +} + void CacheStorage::GetSizeThenCloseAllCachesImpl(const SizeCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(initialized_); @@ -1001,15 +1054,15 @@ int64_t* accumulator_ptr = accumulator.get(); base::Closure barrier_closure = base::BarrierClosure( - ordered_cache_names_.size(), + cache_index_->num_entries(), base::Bind(&SizeRetrievedFromAllCaches, base::Passed(std::move(accumulator)), callback)); - for (const std::string& cache_name : ordered_cache_names_) { - std::unique_ptr<CacheStorageCacheHandle> cache_handle = - GetLoadedCache(cache_name); + for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) { + auto cache_handle = GetLoadedCache(cache_metadata.name); CacheStorageCache* cache = cache_handle->value(); - cache->GetSizeThenClose(base::Bind(&SizeRetrievedFromCache, + cache->GetSizeThenClose(base::Bind(&CacheStorage::SizeRetrievedFromCache, + weak_factory_.GetWeakPtr(), base::Passed(std::move(cache_handle)), barrier_closure, accumulator_ptr)); } @@ -1019,19 +1072,31 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(initialized_); + if (cache_index_->GetStorageSize() != kSizeUnknown) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, cache_index_->GetStorageSize())); + return; + } + std::unique_ptr<int64_t> accumulator(new int64_t(0)); int64_t* accumulator_ptr = accumulator.get(); base::Closure barrier_closure = base::BarrierClosure( - ordered_cache_names_.size(), + cache_index_->num_entries(), base::Bind(&SizeRetrievedFromAllCaches, base::Passed(std::move(accumulator)), callback)); - for (const std::string& cache_name : ordered_cache_names_) { + for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) { + if (cache_metadata.size != CacheStorage::kSizeUnknown) { + *accumulator_ptr += cache_metadata.size; + barrier_closure.Run(); + continue; + } std::unique_ptr<CacheStorageCacheHandle> cache_handle = - GetLoadedCache(cache_name); + GetLoadedCache(cache_metadata.name); CacheStorageCache* cache = cache_handle->value(); - cache->Size(base::Bind(&SizeRetrievedFromCache, + cache->Size(base::Bind(&CacheStorage::SizeRetrievedFromCache, + weak_factory_.GetWeakPtr(), base::Passed(std::move(cache_handle)), barrier_closure, accumulator_ptr)); }
diff --git a/content/browser/cache_storage/cache_storage.h b/content/browser/cache_storage/cache_storage.h index e40c937..b99886a 100644 --- a/content/browser/cache_storage/cache_storage.h +++ b/content/browser/cache_storage/cache_storage.h
@@ -18,6 +18,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "content/browser/cache_storage/cache_storage_cache.h" +#include "content/browser/cache_storage/cache_storage_cache_observer.h" namespace base { class SequencedTaskRunner; @@ -33,6 +34,7 @@ namespace content { class CacheStorageCacheHandle; +class CacheStorageIndex; class CacheStorageScheduler; // TODO(jkarlin): Constrain the total bytes used per origin. @@ -40,14 +42,15 @@ // CacheStorage holds the set of caches for a given origin. It is // owned by the CacheStorageManager. This class expects to be run // on the IO thread. The asynchronous methods are executed serially. -class CONTENT_EXPORT CacheStorage { +class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver { public: - typedef std::vector<std::string> StringVector; + constexpr static int64_t kSizeUnknown = -1; + typedef base::Callback<void(bool, CacheStorageError)> BoolAndErrorCallback; typedef base::Callback<void(std::unique_ptr<CacheStorageCacheHandle>, CacheStorageError)> CacheAndErrorCallback; - using StringsCallback = base::Callback<void(const StringVector&)>; + using IndexCallback = base::Callback<void(const CacheStorageIndex&)>; using SizeCallback = base::Callback<void(int64_t)>; static const char kIndexFileName[]; @@ -86,8 +89,8 @@ void DeleteCache(const std::string& cache_name, const BoolAndErrorCallback& callback); - // Calls the callback with a vector of cache names (keys) available. - void EnumerateCaches(const StringsCallback& callback); + // Calls the callback with the cache index. + void EnumerateCaches(const IndexCallback& callback); // Calls match on the cache with the given |cache_name|. void MatchCache(const std::string& cache_name, @@ -116,9 +119,13 @@ void StartAsyncOperationForTesting(); void CompleteAsyncOperationForTesting(); + // CacheStorageCacheObserver: + void CacheSizeUpdated(const CacheStorageCache* cache, int64_t size) override; + private: friend class CacheStorageCacheHandle; friend class CacheStorageCache; + friend class CacheStorageManagerTest; class CacheLoader; class MemoryLoader; class SimpleCacheLoader; @@ -140,8 +147,7 @@ // Initializer and its callback are below. void LazyInit(); void LazyInitImpl(); - void LazyInitDidLoadIndex( - std::unique_ptr<std::vector<std::string>> indexed_cache_names); + void LazyInitDidLoadIndex(std::unique_ptr<CacheStorageIndex> index); // The Open and CreateCache callbacks are below. void OpenCacheImpl(const std::string& cache_name, @@ -162,8 +168,7 @@ void DeleteCacheImpl(const std::string& cache_name, const BoolAndErrorCallback& callback); void DeleteCacheDidWriteIndex( - const std::string& cache_name, - const StringVector& original_ordered_cache_names, + std::unique_ptr<CacheStorageCacheHandle> cache_handle, const BoolAndErrorCallback& callback, bool success); void DeleteCacheFinalize(CacheStorageCache* doomed_cache); @@ -172,7 +177,7 @@ void DeleteCacheDidCleanUp(bool success); // The EnumerateCache callbacks are below. - void EnumerateCachesImpl(const StringsCallback& callback); + void EnumerateCachesImpl(const IndexCallback& callback); // The MatchCache callbacks are below. void MatchCacheImpl(const std::string& cache_name, @@ -203,6 +208,20 @@ void GetSizeThenCloseAllCachesImpl(const SizeCallback& callback); void SizeImpl(const SizeCallback& callback); + void SizeRetrievedFromCache( + std::unique_ptr<CacheStorageCacheHandle> cache_handle, + const base::Closure& closure, + int64_t* accumulator, + int64_t size); + + void ScheduleWriteIndex(); + void WriteIndex(const base::Callback<void(bool)>& callback); + void WriteIndexImpl(const base::Callback<void(bool)>& callback); + bool index_write_pending() const { return !index_write_task_.IsCancelled(); } + // Start a scheduled index write immediately. Returns true if a write was + // scheduled, or false if not. + bool InitiateScheduledIndexWriteForTest( + const base::Callback<void(bool)>& callback); // Whether or not we've loaded the list of cache names into memory. bool initialized_; @@ -225,8 +244,8 @@ // CacheStorageCacheHandle reference counts std::map<CacheStorageCache*, size_t> cache_handle_counts_; - // The names of caches in the order that they were created. - StringVector ordered_cache_names_; + // The cache index data. + std::unique_ptr<CacheStorageIndex> cache_index_; // The file path for this CacheStorage. base::FilePath origin_path_; @@ -243,6 +262,8 @@ // The origin that this CacheStorage is associated with. GURL origin_; + base::CancelableClosure index_write_task_; + base::WeakPtrFactory<CacheStorage> weak_factory_; DISALLOW_COPY_AND_ASSIGN(CacheStorage);
diff --git a/content/browser/cache_storage/cache_storage.proto b/content/browser/cache_storage/cache_storage.proto index 703c022..06f1973ff 100644 --- a/content/browser/cache_storage/cache_storage.proto +++ b/content/browser/cache_storage/cache_storage.proto
@@ -12,6 +12,7 @@ message Cache { required string name = 1; optional string cache_dir = 2; + optional int64 size = 3; } repeated Cache cache = 1; optional string origin = 2;
diff --git a/content/browser/cache_storage/cache_storage_cache.cc b/content/browser/cache_storage/cache_storage_cache.cc index 4bc34fc..e84e4f4 100644 --- a/content/browser/cache_storage/cache_storage_cache.cc +++ b/content/browser/cache_storage/cache_storage_cache.cc
@@ -24,6 +24,7 @@ #include "content/browser/cache_storage/cache_storage.pb.h" #include "content/browser/cache_storage/cache_storage_blob_to_disk_cache.h" #include "content/browser/cache_storage/cache_storage_cache_handle.h" +#include "content/browser/cache_storage/cache_storage_cache_observer.h" #include "content/browser/cache_storage/cache_storage_scheduler.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/referrer.h" @@ -263,10 +264,11 @@ scoped_refptr<net::URLRequestContextGetter> request_context_getter, scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy, base::WeakPtr<storage::BlobStorageContext> blob_context) { - CacheStorageCache* cache = - new CacheStorageCache(origin, cache_name, base::FilePath(), cache_storage, - std::move(request_context_getter), - std::move(quota_manager_proxy), blob_context); + CacheStorageCache* cache = new CacheStorageCache( + origin, cache_name, base::FilePath(), cache_storage, + std::move(request_context_getter), std::move(quota_manager_proxy), + blob_context, 0 /* cache_size */); + cache->SetObserver(cache_storage); cache->InitBackend(); return base::WrapUnique(cache); } @@ -279,12 +281,13 @@ const base::FilePath& path, scoped_refptr<net::URLRequestContextGetter> request_context_getter, scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy, - base::WeakPtr<storage::BlobStorageContext> blob_context) { - CacheStorageCache* cache = - new CacheStorageCache(origin, cache_name, path, cache_storage, - std::move(request_context_getter), - std::move(quota_manager_proxy), blob_context); - cache->InitBackend(); + base::WeakPtr<storage::BlobStorageContext> blob_context, + int64_t cache_size) { + CacheStorageCache* cache = new CacheStorageCache( + origin, cache_name, path, cache_storage, + std::move(request_context_getter), std::move(quota_manager_proxy), + blob_context, cache_size); + cache->SetObserver(cache_storage), cache->InitBackend(); return base::WrapUnique(cache); } @@ -498,6 +501,11 @@ scheduler_->WrapCallbackToRunNext(callback)))); } +void CacheStorageCache::SetObserver(CacheStorageCacheObserver* observer) { + DCHECK((observer == nullptr) ^ (cache_observer_ == nullptr)); + cache_observer_ = observer; +} + CacheStorageCache::~CacheStorageCache() { quota_manager_proxy_->NotifyOriginNoLongerInUse(origin_); } @@ -509,7 +517,8 @@ CacheStorage* cache_storage, scoped_refptr<net::URLRequestContextGetter> request_context_getter, scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy, - base::WeakPtr<storage::BlobStorageContext> blob_context) + base::WeakPtr<storage::BlobStorageContext> blob_context, + int64_t cache_size) : origin_(origin), cache_name_(cache_name), path_(path), @@ -519,7 +528,9 @@ blob_storage_context_(blob_context), scheduler_( new CacheStorageScheduler(CacheStorageSchedulerClient::CLIENT_CACHE)), + cache_size_(cache_size), max_query_size_bytes_(kMaxQueryCacheResultBytes), + cache_observer_(nullptr), memory_only_(path.empty()), weak_ptr_factory_(this) { DCHECK(!origin_.is_empty()); @@ -1179,12 +1190,18 @@ void CacheStorageCache::UpdateCacheSizeGotSize( std::unique_ptr<CacheStorageCacheHandle> cache_handle, int current_cache_size) { + DCHECK_NE(current_cache_size, CacheStorage::kSizeUnknown); int64_t old_cache_size = cache_size_; cache_size_ = current_cache_size; + int64_t size_delta = current_cache_size - old_cache_size; + quota_manager_proxy_->NotifyStorageModified( storage::QuotaClient::kServiceWorkerCache, origin_, - storage::kStorageTypeTemporary, current_cache_size - old_cache_size); + storage::kStorageTypeTemporary, size_delta); + + if (cache_observer_) + cache_observer_->CacheSizeUpdated(this, current_cache_size); } void CacheStorageCache::Delete(const CacheStorageBatchOperation& operation, @@ -1369,6 +1386,15 @@ void CacheStorageCache::InitGotCacheSize(const base::Closure& callback, CacheStorageError cache_create_error, int cache_size) { + // Now that we know the cache size either 1) the cache size should be unknown + // (which is why the size was calculated), or 2) it must match the current + // size. If the sizes aren't equal then there is a bug in how the cache size + // is saved in the store's index. + if (cache_size_ != CacheStorage::kSizeUnknown && cache_size_ != cache_size) { + // TODO(cmumford): Add UMA for this. + LOG(ERROR) << "Cache size/index mismatch"; + DCHECK_EQ(cache_size_, cache_size); + } cache_size_ = cache_size; initializing_ = false; backend_state_ = (cache_create_error == CACHE_STORAGE_OK && backend_ && @@ -1379,6 +1405,9 @@ UMA_HISTOGRAM_ENUMERATION("ServiceWorkerCache.InitBackendResult", cache_create_error, CACHE_STORAGE_ERROR_LAST + 1); + if (cache_observer_) + cache_observer_->CacheSizeUpdated(this, cache_size_); + callback.Run(); }
diff --git a/content/browser/cache_storage/cache_storage_cache.h b/content/browser/cache_storage/cache_storage_cache.h index 5fe67d0..1d5996c 100644 --- a/content/browser/cache_storage/cache_storage_cache.h +++ b/content/browser/cache_storage/cache_storage_cache.h
@@ -33,9 +33,11 @@ } namespace content { +class CacheMetadata; class CacheStorage; class CacheStorageBlobToDiskCache; class CacheStorageCacheHandle; +class CacheStorageCacheObserver; class CacheStorageScheduler; class TestCacheStorageCache; @@ -81,7 +83,8 @@ const base::FilePath& path, scoped_refptr<net::URLRequestContextGetter> request_context_getter, scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy, - base::WeakPtr<storage::BlobStorageContext> blob_context); + base::WeakPtr<storage::BlobStorageContext> blob_context, + int64_t cache_size); // Returns ERROR_TYPE_NOT_FOUND if not found. void Match(std::unique_ptr<ServiceWorkerFetchRequest> request, @@ -156,6 +159,13 @@ std::string cache_name() const { return cache_name_; } + int64_t cache_size() const { return cache_size_; } + + // Set the one observer that will be notified of changes to this cache. + // Note: Either the observer must have a lifetime longer than this instance + // or call SetObserver(nullptr) to stop receiving notification of changes. + void SetObserver(CacheStorageCacheObserver* observer); + base::WeakPtr<CacheStorageCache> AsWeakPtr(); private: @@ -197,7 +207,8 @@ CacheStorage* cache_storage, scoped_refptr<net::URLRequestContextGetter> request_context_getter, scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy, - base::WeakPtr<storage::BlobStorageContext> blob_context); + base::WeakPtr<storage::BlobStorageContext> blob_context, + int64_t cache_size); // Returns all entries in this cache. void OpenAllEntries(const OpenAllEntriesCallback& callback); @@ -387,8 +398,9 @@ BackendState backend_state_ = BACKEND_UNINITIALIZED; std::unique_ptr<CacheStorageScheduler> scheduler_; bool initializing_ = false; - int64_t cache_size_ = 0; + int64_t cache_size_; size_t max_query_size_bytes_; + CacheStorageCacheObserver* cache_observer_; // Owns the elements of the list BlobToDiskCacheIDMap active_blob_to_disk_cache_writers_;
diff --git a/content/browser/cache_storage/cache_storage_cache_observer.h b/content/browser/cache_storage/cache_storage_cache_observer.h new file mode 100644 index 0000000..d46f7da --- /dev/null +++ b/content/browser/cache_storage/cache_storage_cache_observer.h
@@ -0,0 +1,23 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_CACHE_OBSERVER_H_ +#define CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_CACHE_OBSERVER_H_ + +#include "content/common/content_export.h" + +namespace content { + +class CacheStorageCache; + +class CONTENT_EXPORT CacheStorageCacheObserver { + public: + // The cache size has been set. + virtual void CacheSizeUpdated(const CacheStorageCache* cache, + int64_t size) = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_CACHE_OBSERVER_H_
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc index f10ab16..fd27f86 100644 --- a/content/browser/cache_storage/cache_storage_cache_unittest.cc +++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -282,7 +282,8 @@ cache_storage, request_context_getter, quota_manager_proxy, - blob_context), + blob_context, + 0 /* cache_size */), delay_backend_creation_(false) {} void CreateBackend(const ErrorCallback& callback) override {
diff --git a/content/browser/cache_storage/cache_storage_dispatcher_host.cc b/content/browser/cache_storage/cache_storage_dispatcher_host.cc index ba584c7..621af75 100644 --- a/content/browser/cache_storage/cache_storage_dispatcher_host.cc +++ b/content/browser/cache_storage/cache_storage_dispatcher_host.cc
@@ -389,10 +389,10 @@ void CacheStorageDispatcherHost::OnCacheStorageKeysCallback( int thread_id, int request_id, - const std::vector<std::string>& strings) { + const CacheStorageIndex& cache_index) { std::vector<base::string16> string16s; - for (size_t i = 0, max = strings.size(); i < max; ++i) - string16s.push_back(base::UTF8ToUTF16(strings[i])); + for (const auto& metadata : cache_index.ordered_cache_metadata()) + string16s.push_back(base::UTF8ToUTF16(metadata.name)); Send(new CacheStorageMsg_CacheStorageKeysSuccess(thread_id, request_id, string16s)); }
diff --git a/content/browser/cache_storage/cache_storage_dispatcher_host.h b/content/browser/cache_storage/cache_storage_dispatcher_host.h index e82018a..5206dac 100644 --- a/content/browser/cache_storage/cache_storage_dispatcher_host.h +++ b/content/browser/cache_storage/cache_storage_dispatcher_host.h
@@ -15,6 +15,7 @@ #include "base/macros.h" #include "content/browser/cache_storage/cache_storage.h" +#include "content/browser/cache_storage/cache_storage_index.h" #include "content/public/browser/browser_message_filter.h" namespace url { @@ -111,7 +112,7 @@ CacheStorageError error); void OnCacheStorageKeysCallback(int thread_id, int request_id, - const std::vector<std::string>& strings); + const CacheStorageIndex& cache_index); void OnCacheStorageMatchCallback( int thread_id, int request_id,
diff --git a/content/browser/cache_storage/cache_storage_index.cc b/content/browser/cache_storage/cache_storage_index.cc new file mode 100644 index 0000000..a5ddcb33 --- /dev/null +++ b/content/browser/cache_storage/cache_storage_index.cc
@@ -0,0 +1,114 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/cache_storage/cache_storage_index.h" + +#include <utility> + +namespace content { + +CacheStorageIndex::CacheStorageIndex() + : doomed_cache_metadata_("", CacheStorage::kSizeUnknown) { + ClearDoomedCache(); +} + +CacheStorageIndex::~CacheStorageIndex() = default; + +CacheStorageIndex& CacheStorageIndex::operator=(CacheStorageIndex&& rhs) { + DCHECK(!has_doomed_cache_); + ordered_cache_metadata_ = std::move(rhs.ordered_cache_metadata_); + cache_metadata_map_ = std::move(rhs.cache_metadata_map_); + storage_size_ = rhs.storage_size_; + rhs.storage_size_ = CacheStorage::kSizeUnknown; + return *this; +} + +void CacheStorageIndex::Insert(const CacheMetadata& cache_metadata) { + DCHECK(!has_doomed_cache_); + DCHECK(cache_metadata_map_.find(cache_metadata.name) == + cache_metadata_map_.end()); + ordered_cache_metadata_.push_back(cache_metadata); + cache_metadata_map_[cache_metadata.name] = --ordered_cache_metadata_.end(); + storage_size_ = CacheStorage::kSizeUnknown; +} + +void CacheStorageIndex::Delete(const std::string& cache_name) { + DCHECK(!has_doomed_cache_); + auto it = cache_metadata_map_.find(cache_name); + DCHECK(it != cache_metadata_map_.end()); + ordered_cache_metadata_.erase(it->second); + cache_metadata_map_.erase(it); + storage_size_ = CacheStorage::kSizeUnknown; +} + +bool CacheStorageIndex::SetCacheSize(const std::string& cache_name, + int64_t size) { + if (has_doomed_cache_) + DCHECK_NE(cache_name, doomed_cache_metadata_.name); + auto it = cache_metadata_map_.find(cache_name); + DCHECK(it != cache_metadata_map_.end()); + if (it->second->size == size) + return false; + it->second->size = size; + storage_size_ = CacheStorage::kSizeUnknown; + return true; +} + +int64_t CacheStorageIndex::GetCacheSize(const std::string& cache_name) const { + const auto& it = cache_metadata_map_.find(cache_name); + if (it == cache_metadata_map_.end()) + return CacheStorage::kSizeUnknown; + return it->second->size; +} + +int64_t CacheStorageIndex::GetStorageSize() { + if (storage_size_ == CacheStorage::kSizeUnknown) + UpdateStorageSize(); + return storage_size_; +} + +void CacheStorageIndex::UpdateStorageSize() { + int64_t storage_size = 0; + storage_size_ = CacheStorage::kSizeUnknown; + for (const CacheMetadata& info : ordered_cache_metadata_) { + if (info.size == CacheStorage::kSizeUnknown) + return; + storage_size += info.size; + } + storage_size_ = storage_size; +} + +void CacheStorageIndex::DoomCache(const std::string& cache_name) { + DCHECK(!has_doomed_cache_); + auto map_it = cache_metadata_map_.find(cache_name); + DCHECK(map_it != cache_metadata_map_.end()); + doomed_cache_metadata_ = std::move(*(map_it->second)); + after_doomed_cache_metadata_ = ordered_cache_metadata_.erase(map_it->second); + cache_metadata_map_.erase(map_it); + storage_size_ = CacheStorage::kSizeUnknown; + has_doomed_cache_ = true; +} + +void CacheStorageIndex::FinalizeDoomedCache() { + DCHECK(has_doomed_cache_); + ClearDoomedCache(); +} + +void CacheStorageIndex::RestoreDoomedCache() { + DCHECK(has_doomed_cache_); + const auto cache_name = doomed_cache_metadata_.name; + cache_metadata_map_[cache_name] = ordered_cache_metadata_.insert( + after_doomed_cache_metadata_, std::move(doomed_cache_metadata_)); + after_doomed_cache_metadata_ = ordered_cache_metadata_.end(); + storage_size_ = CacheStorage::kSizeUnknown; + ClearDoomedCache(); +} + +void CacheStorageIndex::ClearDoomedCache() { + doomed_cache_metadata_.name.clear(); + after_doomed_cache_metadata_ = ordered_cache_metadata_.end(); + has_doomed_cache_ = false; +} + +} // namespace content
diff --git a/content/browser/cache_storage/cache_storage_index.h b/content/browser/cache_storage/cache_storage_index.h new file mode 100644 index 0000000..3665ec6 --- /dev/null +++ b/content/browser/cache_storage/cache_storage_index.h
@@ -0,0 +1,93 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_INDEX_H_ +#define CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_INDEX_H_ + +#include <list> +#include <string> +#include <unordered_map> + +#include "base/macros.h" +#include "content/browser/cache_storage/cache_storage.h" + +namespace content { + +class CacheMetadata; + +// CacheStorageIndex maintains an ordered list of metadata (CacheMetadata) +// for each cache owned by a CacheStorage object. This class is not thread safe, +// and is owned by the CacheStorage. +class CONTENT_EXPORT CacheStorageIndex { + public: + struct CacheMetadata { + CacheMetadata(const std::string& name, int64_t size) + : name(name), size(size) {} + std::string name; + // The size (in bytes) of the cache. Set to CacheStorage::kSizeUnknown if + // size not known. + int64_t size; + }; + + CacheStorageIndex(); + ~CacheStorageIndex(); + + CacheStorageIndex& operator=(CacheStorageIndex&& rhs); + + void Insert(const CacheMetadata& cache_metadata); + void Delete(const std::string& cache_name); + + // Sets the cache size. Returns true if the new size is different than the + // current size else false. + bool SetCacheSize(const std::string& cache_name, int64_t size); + + // Return the size (in bytes) of the specified cache. Will return + // CacheStorage::kSizeUnknown if the specified cache does not exist. + int64_t GetCacheSize(const std::string& cache_name) const; + + const std::list<CacheMetadata>& ordered_cache_metadata() const { + return ordered_cache_metadata_; + } + + size_t num_entries() const { return ordered_cache_metadata_.size(); } + + // Will calculate (if necessary), and return the total sum of all cache sizes. + int64_t GetStorageSize(); + + // Mark the cache as doomed. This removes the cache metadata from the index. + // All const methods (eg: num_entries) will behave as if the doomed cache is + // not present in the index. Prior to calling any non-const method the doomed + // cache must either be finalized (by calling FinalizeDoomedCache) or restored + // (by calling RestoreDoomedCache). + // + // RestoreDoomedCache restores the metadata to the index at the original + // position prior to calling DoomCache. + void DoomCache(const std::string& cache_name); + void FinalizeDoomedCache(); + void RestoreDoomedCache(); + + private: + void UpdateStorageSize(); + void ClearDoomedCache(); + + // Use a list to keep saved iterators valid during insert/erase. + // Note: ordered by cache creation. + std::list<CacheMetadata> ordered_cache_metadata_; + std::unordered_map<std::string, std::list<CacheMetadata>::iterator> + cache_metadata_map_; + + // The total size of all caches in this store. + int64_t storage_size_ = CacheStorage::kSizeUnknown; + + // The doomed cache metadata saved when calling DoomCache. + CacheMetadata doomed_cache_metadata_; + std::list<CacheMetadata>::iterator after_doomed_cache_metadata_; + bool has_doomed_cache_ = false; + + DISALLOW_COPY_AND_ASSIGN(CacheStorageIndex); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_INDEX_H_
diff --git a/content/browser/cache_storage/cache_storage_index_unittest.cc b/content/browser/cache_storage/cache_storage_index_unittest.cc new file mode 100644 index 0000000..4e6db8e3 --- /dev/null +++ b/content/browser/cache_storage/cache_storage_index_unittest.cc
@@ -0,0 +1,164 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/cache_storage/cache_storage_index.h" + +#include <list> +#include <utility> +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +class CacheStorageIndexTest : public testing::Test { + public: + void SetUp() override {} + void TearDown() override {} + CacheStorageIndexTest() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(CacheStorageIndexTest); +}; + +TEST_F(CacheStorageIndexTest, TestDefaultConstructor) { + CacheStorageIndex index; + EXPECT_EQ(0u, index.num_entries()); + EXPECT_TRUE(index.ordered_cache_metadata().empty()); + EXPECT_EQ(0u, index.GetStorageSize()); +} + +TEST_F(CacheStorageIndexTest, TestSetCacheSize) { + CacheStorageIndex index; + index.Insert(CacheStorageIndex::CacheMetadata("foo", 12)); + index.Insert(CacheStorageIndex::CacheMetadata("bar", 19)); + index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000)); + EXPECT_EQ(3u, index.num_entries()); + ASSERT_EQ(3u, index.ordered_cache_metadata().size()); + EXPECT_EQ(1031, index.GetStorageSize()); + + EXPECT_TRUE(index.SetCacheSize("baz", 2000)); + EXPECT_EQ(2031, index.GetStorageSize()); + + EXPECT_FALSE(index.SetCacheSize("baz", 2000)); + EXPECT_EQ(2031, index.GetStorageSize()); + + EXPECT_EQ(2000, index.GetCacheSize("baz")); + EXPECT_EQ(CacheStorage::kSizeUnknown, index.GetCacheSize("<not-present>")); +} + +TEST_F(CacheStorageIndexTest, TestDoomCache) { + CacheStorageIndex index; + index.Insert(CacheStorageIndex::CacheMetadata("foo", 12)); + index.Insert(CacheStorageIndex::CacheMetadata("bar", 19)); + index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000)); + EXPECT_EQ(3u, index.num_entries()); + ASSERT_EQ(3u, index.ordered_cache_metadata().size()); + EXPECT_EQ(1031, index.GetStorageSize()); + + index.DoomCache("bar"); + EXPECT_EQ(2u, index.num_entries()); + ASSERT_EQ(2u, index.ordered_cache_metadata().size()); + EXPECT_EQ(1031 - 19, index.GetStorageSize()); + index.RestoreDoomedCache(); + EXPECT_EQ(3u, index.num_entries()); + ASSERT_EQ(3u, index.ordered_cache_metadata().size()); + auto it = index.ordered_cache_metadata().begin(); + EXPECT_EQ("foo", (it++)->name); + EXPECT_EQ("bar", (it++)->name); + EXPECT_EQ("baz", (it++)->name); + EXPECT_EQ(1031, index.GetStorageSize()); + + index.DoomCache("foo"); + EXPECT_EQ(2u, index.num_entries()); + ASSERT_EQ(2u, index.ordered_cache_metadata().size()); + EXPECT_EQ(1031 - 12, index.GetStorageSize()); + index.FinalizeDoomedCache(); + EXPECT_EQ(2u, index.num_entries()); + ASSERT_EQ(2u, index.ordered_cache_metadata().size()); + EXPECT_EQ(1031 - 12, index.GetStorageSize()); +} + +TEST_F(CacheStorageIndexTest, TestDelete) { + CacheStorageIndex index; + index.Insert(CacheStorageIndex::CacheMetadata("bar", 19)); + index.Insert(CacheStorageIndex::CacheMetadata("foo", 12)); + index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000)); + EXPECT_EQ(3u, index.num_entries()); + ASSERT_EQ(3u, index.ordered_cache_metadata().size()); + EXPECT_EQ(1031, index.GetStorageSize()); + + auto it = index.ordered_cache_metadata().begin(); + EXPECT_EQ("bar", it->name); + EXPECT_EQ(19u, it->size); + it++; + EXPECT_EQ("foo", it->name); + EXPECT_EQ(12u, it->size); + it++; + EXPECT_EQ("baz", it->name); + EXPECT_EQ(1000u, it->size); + + index.Delete("bar"); + EXPECT_EQ(2u, index.num_entries()); + ASSERT_EQ(2u, index.ordered_cache_metadata().size()); + EXPECT_EQ(1012, index.GetStorageSize()); + + it = index.ordered_cache_metadata().begin(); + EXPECT_EQ("foo", it->name); + EXPECT_EQ(12u, it->size); + it++; + EXPECT_EQ("baz", it->name); + EXPECT_EQ(1000u, it->size); + + index.Delete("baz"); + EXPECT_EQ(1u, index.num_entries()); + ASSERT_EQ(1u, index.ordered_cache_metadata().size()); + EXPECT_EQ(12, index.GetStorageSize()); + + it = index.ordered_cache_metadata().begin(); + EXPECT_EQ("foo", it->name); + EXPECT_EQ(12u, it->size); +} + +TEST_F(CacheStorageIndexTest, TestInsert) { + CacheStorageIndex index; + index.Insert(CacheStorageIndex::CacheMetadata("foo", 12)); + index.Insert(CacheStorageIndex::CacheMetadata("bar", 19)); + index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000)); + EXPECT_EQ(3u, index.num_entries()); + ASSERT_EQ(3u, index.ordered_cache_metadata().size()); + EXPECT_EQ(1031, index.GetStorageSize()); +} + +TEST_F(CacheStorageIndexTest, TestMoveOperator) { + CacheStorageIndex index; + index.Insert(CacheStorageIndex::CacheMetadata("foo", 12)); + index.Insert(CacheStorageIndex::CacheMetadata("bar", 19)); + index.Insert(CacheStorageIndex::CacheMetadata("baz", 1000)); + + CacheStorageIndex index2; + index2 = std::move(index); + + EXPECT_EQ(3u, index2.num_entries()); + EXPECT_EQ(3u, index2.ordered_cache_metadata().size()); + ASSERT_EQ(1031, index2.GetStorageSize()); + + EXPECT_EQ(0u, index.num_entries()); + EXPECT_TRUE(index.ordered_cache_metadata().empty()); + EXPECT_EQ(0u, index.GetStorageSize()); + + auto it = index2.ordered_cache_metadata().begin(); + EXPECT_EQ("foo", it->name); + EXPECT_EQ(12u, it->size); + it++; + EXPECT_EQ("bar", it->name); + EXPECT_EQ(19u, it->size); + it++; + EXPECT_EQ("baz", it->name); + EXPECT_EQ(1000u, it->size); + + EXPECT_EQ(3u, index2.num_entries()); + ASSERT_EQ(3u, index2.ordered_cache_metadata().size()); + EXPECT_EQ(1031, index2.GetStorageSize()); +} + +} // namespace content
diff --git a/content/browser/cache_storage/cache_storage_manager.cc b/content/browser/cache_storage/cache_storage_manager.cc index 6c136ae..f0516f9e 100644 --- a/content/browser/cache_storage/cache_storage_manager.cc +++ b/content/browser/cache_storage/cache_storage_manager.cc
@@ -8,7 +8,6 @@ #include <map> #include <set> -#include <string> #include <utility> #include "base/barrier_closure.h" @@ -51,8 +50,21 @@ : storage::kQuotaErrorAbort)); } -// Open the various cache directories' index files and extract their origins and -// last modified times. +// Calculate the sum of all cache sizes in this store, but only if all sizes are +// known. If one or more sizes are not known then return kSizeUnknown. +int64_t GetCacheStorageSize(const proto::CacheStorageIndex& index) { + int64_t storage_size = 0; + for (int i = 0, max = index.cache_size(); i < max; ++i) { + const proto::CacheStorageIndex::Cache& cache = index.cache(i); + if (!cache.has_size() || cache.size() == CacheStorage::kSizeUnknown) + return CacheStorage::kSizeUnknown; + storage_size += cache.size(); + } + return storage_size; +} + +// Open the various cache directories' index files and extract their origins, +// sizes (if current), and last modified times. void ListOriginsAndLastModifiedOnTaskRunner( std::vector<CacheStorageUsageInfo>* usages, base::FilePath root_path) { @@ -61,16 +73,23 @@ base::FilePath path; while (!(path = file_enum.Next()).empty()) { + base::FilePath index_path = path.AppendASCII(CacheStorage::kIndexFileName); + base::File::Info file_info; + base::Time index_last_modified; + if (GetFileInfo(index_path, &file_info)) + index_last_modified = file_info.last_modified; std::string protobuf; base::ReadFileToString(path.AppendASCII(CacheStorage::kIndexFileName), &protobuf); proto::CacheStorageIndex index; if (index.ParseFromString(protobuf)) { if (index.has_origin()) { - base::File::Info file_info; if (base::GetFileInfo(path, &file_info)) { + int64_t storage_size = CacheStorage::kSizeUnknown; + if (file_info.last_modified < index_last_modified) + storage_size = GetCacheStorageSize(index); usages->push_back(CacheStorageUsageInfo( - GURL(index.origin()), 0 /* size */, file_info.last_modified)); + GURL(index.origin()), storage_size, file_info.last_modified)); } } } @@ -117,6 +136,7 @@ int64_t size) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK_NE(size, CacheStorage::kSizeUnknown); usage->total_size_bytes = size; base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } @@ -186,7 +206,7 @@ void CacheStorageManager::EnumerateCaches( const GURL& origin, - const CacheStorage::StringsCallback& callback) { + const CacheStorage::IndexCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); @@ -276,6 +296,10 @@ callback)); for (CacheStorageUsageInfo& usage : *usages_ptr) { + if (usage.total_size_bytes != CacheStorage::kSizeUnknown) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, barrier_closure); + continue; + } CacheStorage* cache_storage = FindOrCreateCacheStorage(usage.origin); cache_storage->Size( base::Bind(&OneOriginSizeReported, barrier_closure, &usage));
diff --git a/content/browser/cache_storage/cache_storage_manager.h b/content/browser/cache_storage/cache_storage_manager.h index f9f95b0..393f1ce 100644 --- a/content/browser/cache_storage/cache_storage_manager.h +++ b/content/browser/cache_storage/cache_storage_manager.h
@@ -66,7 +66,7 @@ const std::string& cache_name, const CacheStorage::BoolAndErrorCallback& callback); void EnumerateCaches(const GURL& origin, - const CacheStorage::StringsCallback& callback); + const CacheStorage::IndexCallback& callback); void MatchCache(const GURL& origin, const std::string& cache_name, std::unique_ptr<ServiceWorkerFetchRequest> request,
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc index 6ebb69c..59ba128 100644 --- a/content/browser/cache_storage/cache_storage_manager_unittest.cc +++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -7,9 +7,11 @@ #include <stddef.h> #include <stdint.h> +#include <list> #include <set> #include <utility> +#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" @@ -22,8 +24,10 @@ #include "base/strings/string_number_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" +#include "content/browser/cache_storage/cache_storage.h" #include "content/browser/cache_storage/cache_storage.pb.h" #include "content/browser/cache_storage/cache_storage_cache_handle.h" +#include "content/browser/cache_storage/cache_storage_index.h" #include "content/browser/cache_storage/cache_storage_quota_client.h" #include "content/browser/quota/mock_quota_manager_proxy.h" #include "content/public/browser/browser_thread.h" @@ -44,6 +48,38 @@ namespace content { +namespace { + +bool IsIndexFileCurrent(const base::FilePath& cache_dir) { + base::File::Info info; + const base::FilePath index_path = + cache_dir.AppendASCII(CacheStorage::kIndexFileName); + if (!GetFileInfo(index_path, &info)) + return false; + base::Time index_last_modified = info.last_modified; + + base::FileEnumerator enumerator(cache_dir, false, + base::FileEnumerator::DIRECTORIES); + for (base::FilePath file_path = enumerator.Next(); !file_path.empty(); + file_path = enumerator.Next()) { + if (!GetFileInfo(file_path, &info)) + return false; + if (index_last_modified < info.last_modified) + return false; + } + + return true; +} + +void CopyCacheStorageIndex(CacheStorageIndex* dest, + const CacheStorageIndex& src) { + DCHECK_EQ(0U, dest->num_entries()); + for (const auto& cache_metadata : src.ordered_cache_metadata()) + dest->Insert(cache_metadata); +} + +} // anonymous namespace + // Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns // the memory. std::unique_ptr<storage::BlobProtocolHandler> CreateMockBlobProtocolHandler( @@ -65,59 +101,22 @@ origin2_("http://example2.com") {} void SetUp() override { - ChromeBlobStorageContext* blob_storage_context( - ChromeBlobStorageContext::GetFor(&browser_context_)); - // Wait for ChromeBlobStorageContext to finish initializing. - base::RunLoop().RunUntilIdle(); - - blob_storage_context_ = blob_storage_context->context(); - - url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl); - url_request_job_factory_->SetProtocolHandler( - "blob", CreateMockBlobProtocolHandler(blob_storage_context->context())); - - net::URLRequestContext* url_request_context = - BrowserContext::GetDefaultStoragePartition(&browser_context_)-> - GetURLRequestContext()->GetURLRequestContext(); - - url_request_context->set_job_factory(url_request_job_factory_.get()); - - const bool is_incognito = MemoryOnly(); base::FilePath temp_dir_path; - if (!is_incognito) { + if (!MemoryOnly()) ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - temp_dir_path = temp_dir_.GetPath(); - } - quota_policy_ = new MockSpecialStoragePolicy; - mock_quota_manager_ = new MockQuotaManager( - is_incognito, temp_dir_path, base::ThreadTaskRunnerHandle::Get().get(), - base::ThreadTaskRunnerHandle::Get().get(), quota_policy_.get()); - mock_quota_manager_->SetQuota( - GURL(origin1_), storage::kStorageTypeTemporary, 1024 * 1024 * 100); - mock_quota_manager_->SetQuota( - GURL(origin2_), storage::kStorageTypeTemporary, 1024 * 1024 * 100); - - quota_manager_proxy_ = new MockQuotaManagerProxy( - mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); - - cache_manager_ = CacheStorageManager::Create( - temp_dir_path, base::ThreadTaskRunnerHandle::Get(), - quota_manager_proxy_); - - cache_manager_->SetBlobParametersForCache( - BrowserContext::GetDefaultStoragePartition(&browser_context_)-> - GetURLRequestContext(), - blob_storage_context->context()->AsWeakPtr()); + CreateStorageManager(); } - void TearDown() override { - quota_manager_proxy_->SimulateQuotaManagerDestroyed(); - base::RunLoop().RunUntilIdle(); - } + void TearDown() override { DestroyStorageManager(); } virtual bool MemoryOnly() { return false; } + void BoolCallback(base::RunLoop* run_loop, bool value) { + callback_bool_ = value; + run_loop->Quit(); + } + void BoolAndErrorCallback(base::RunLoop* run_loop, bool value, CacheStorageError error) { @@ -135,12 +134,24 @@ run_loop->Quit(); } - void StringsCallback(base::RunLoop* run_loop, - const std::vector<std::string>& strings) { - callback_strings_ = strings; + void CacheMetadataCallback(base::RunLoop* run_loop, + const CacheStorageIndex& cache_index) { + callback_cache_index_ = CacheStorageIndex(); + CopyCacheStorageIndex(&callback_cache_index_, cache_index); run_loop->Quit(); } + const std::string& GetFirstIndexName() const { + return callback_cache_index_.ordered_cache_metadata().front().name; + } + + std::vector<std::string> GetIndexNames() const { + std::vector<std::string> cache_names; + for (const auto& metadata : callback_cache_index_.ordered_cache_metadata()) + cache_names.push_back(metadata.name); + return cache_names; + } + void CachePutCallback(base::RunLoop* run_loop, CacheStorageError error) { callback_error_ = error; run_loop->Quit(); @@ -157,6 +168,84 @@ run_loop->Quit(); } + void CreateStorageManager() { + ChromeBlobStorageContext* blob_storage_context( + ChromeBlobStorageContext::GetFor(&browser_context_)); + // Wait for ChromeBlobStorageContext to finish initializing. + base::RunLoop().RunUntilIdle(); + + blob_storage_context_ = blob_storage_context->context(); + + url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl); + url_request_job_factory_->SetProtocolHandler( + "blob", CreateMockBlobProtocolHandler(blob_storage_context->context())); + + net::URLRequestContext* url_request_context = + BrowserContext::GetDefaultStoragePartition(&browser_context_)-> + GetURLRequestContext()->GetURLRequestContext(); + + url_request_context->set_job_factory(url_request_job_factory_.get()); + + base::FilePath temp_dir_path; + if (!MemoryOnly()) + temp_dir_path = temp_dir_.GetPath(); + + quota_policy_ = new MockSpecialStoragePolicy; + mock_quota_manager_ = new MockQuotaManager( + MemoryOnly(), temp_dir_path, base::ThreadTaskRunnerHandle::Get().get(), + base::ThreadTaskRunnerHandle::Get().get(), quota_policy_.get()); + mock_quota_manager_->SetQuota( + GURL(origin1_), storage::kStorageTypeTemporary, 1024 * 1024 * 100); + mock_quota_manager_->SetQuota( + GURL(origin2_), storage::kStorageTypeTemporary, 1024 * 1024 * 100); + + quota_manager_proxy_ = new MockQuotaManagerProxy( + mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); + + cache_manager_ = CacheStorageManager::Create( + temp_dir_path, base::ThreadTaskRunnerHandle::Get(), + quota_manager_proxy_); + + cache_manager_->SetBlobParametersForCache( + BrowserContext::GetDefaultStoragePartition(&browser_context_) + ->GetURLRequestContext(), + blob_storage_context->context()->AsWeakPtr()); + } + + bool FlushCacheStorageIndex(const GURL& origin) { + callback_bool_ = false; + base::RunLoop loop; + bool write_was_scheduled = + CacheStorageForOrigin(origin)->InitiateScheduledIndexWriteForTest( + base::Bind(&CacheStorageManagerTest::BoolCallback, + base::Unretained(this), &loop)); + loop.Run(); + DCHECK(callback_bool_); + return write_was_scheduled; + } + + void DestroyStorageManager() { + if (quota_manager_proxy_) + quota_manager_proxy_->SimulateQuotaManagerDestroyed(); + base::RunLoop().RunUntilIdle(); + quota_manager_proxy_ = nullptr; + + url_request_job_factory_.reset(); + blob_storage_context_ = nullptr; + + quota_policy_ = nullptr; + mock_quota_manager_ = nullptr; + + callback_cache_handle_ = nullptr; + callback_bool_ = false; + callback_cache_handle_response_ = nullptr; + callback_data_handle_ = nullptr; + callback_cache_index_ = CacheStorageIndex(); + callback_all_origins_usage_.clear(); + + cache_manager_ = nullptr; + } + bool Open(const GURL& origin, const std::string& cache_name) { base::RunLoop loop; cache_manager_->OpenCache( @@ -198,10 +287,10 @@ size_t Keys(const GURL& origin) { base::RunLoop loop; cache_manager_->EnumerateCaches( - origin, base::Bind(&CacheStorageManagerTest::StringsCallback, + origin, base::Bind(&CacheStorageManagerTest::CacheMetadataCallback, base::Unretained(this), base::Unretained(&loop))); loop.Run(); - return callback_strings_.size(); + return callback_cache_index_.num_entries(); } bool StorageMatch(const GURL& origin, @@ -398,7 +487,7 @@ CacheStorageError callback_error_; std::unique_ptr<ServiceWorkerResponse> callback_cache_handle_response_; std::unique_ptr<storage::BlobDataHandle> callback_data_handle_; - std::vector<std::string> callback_strings_; + CacheStorageIndex callback_cache_index_; const GURL origin1_; const GURL origin2_; @@ -511,9 +600,9 @@ std::vector<std::string> expected_keys; expected_keys.push_back("foo"); expected_keys.push_back("bar"); - EXPECT_EQ(expected_keys, callback_strings_); + EXPECT_EQ(expected_keys, GetIndexNames()); EXPECT_EQ(1u, Keys(origin2_)); - EXPECT_STREQ("baz", callback_strings_[0].c_str()); + EXPECT_STREQ("baz", GetFirstIndexName().c_str()); } TEST_P(CacheStorageManagerTestP, DeletedKeysGone) { @@ -522,7 +611,7 @@ EXPECT_TRUE(Open(origin2_, "baz")); EXPECT_TRUE(Delete(origin1_, "bar")); EXPECT_EQ(1u, Keys(origin1_)); - EXPECT_STREQ("foo", callback_strings_[0].c_str()); + EXPECT_STREQ("foo", GetFirstIndexName().c_str()); } TEST_P(CacheStorageManagerTestP, StorageMatchEntryExists) { @@ -668,7 +757,7 @@ EXPECT_TRUE(Open(origin1_, "ä½ å¥½")); EXPECT_EQ(callback_cache_handle_->value(), cache_handle->value()); EXPECT_EQ(1u, Keys(origin1_)); - EXPECT_STREQ("ä½ å¥½", callback_strings_[0].c_str()); + EXPECT_STREQ("ä½ å¥½", GetFirstIndexName().c_str()); } TEST_F(CacheStorageManagerTest, EmptyKey) { @@ -678,7 +767,7 @@ EXPECT_TRUE(Open(origin1_, "")); EXPECT_EQ(cache_handle->value(), callback_cache_handle_->value()); EXPECT_EQ(1u, Keys(origin1_)); - EXPECT_STREQ("", callback_strings_[0].c_str()); + EXPECT_STREQ("", GetFirstIndexName().c_str()); EXPECT_TRUE(Has(origin1_, "")); EXPECT_TRUE(Delete(origin1_, "")); EXPECT_EQ(0u, Keys(origin1_)); @@ -696,7 +785,7 @@ std::vector<std::string> expected_keys; expected_keys.push_back("foo"); expected_keys.push_back("baz"); - EXPECT_EQ(expected_keys, callback_strings_); + EXPECT_EQ(expected_keys, GetIndexNames()); } TEST_F(CacheStorageManagerMemoryOnlyTest, DataLostWhenMemoryOnly) { @@ -713,7 +802,7 @@ const std::string bad_name = "../../../../../../../../../../../../../../foo"; EXPECT_TRUE(Open(origin1_, bad_name)); EXPECT_EQ(1u, Keys(origin1_)); - EXPECT_STREQ(bad_name.c_str(), callback_strings_[0].c_str()); + EXPECT_STREQ(bad_name.c_str(), GetFirstIndexName().c_str()); } TEST_F(CacheStorageManagerTest, BadOriginName) { @@ -722,7 +811,7 @@ GURL bad_origin("http://../../../../../../../../../../../../../../foo"); EXPECT_TRUE(Open(bad_origin, "foo")); EXPECT_EQ(1u, Keys(bad_origin)); - EXPECT_STREQ("foo", callback_strings_[0].c_str()); + EXPECT_STREQ("foo", GetFirstIndexName().c_str()); } // With a persistent cache if the client drops its reference to a @@ -776,6 +865,58 @@ EXPECT_FALSE(CacheMatch(original_handle->value(), kBazURL)); } +// Deleted caches can still be modified, but all changes will eventually be +// thrown away when all references are released. +TEST_F(CacheStorageManagerTest, DeletedCacheIgnoredInIndex) { + const GURL kFooURL("http://example.com/foo"); + const GURL kBarURL("http://example.com/bar"); + const GURL kBazURL("http://example.com/baz"); + const std::string kCacheName = "foo"; + + EXPECT_TRUE(Open(origin1_, kCacheName)); + auto original_handle = std::move(callback_cache_handle_); + EXPECT_TRUE(CachePut(original_handle->value(), kFooURL)); + EXPECT_TRUE(Delete(origin1_, kCacheName)); + + // Now a second cache using the same name, but with different data. + EXPECT_TRUE(Open(origin1_, kCacheName)); + auto new_handle = std::move(callback_cache_handle_); + EXPECT_TRUE(CachePut(new_handle->value(), kFooURL)); + EXPECT_TRUE(CachePut(new_handle->value(), kBarURL)); + EXPECT_TRUE(CachePut(new_handle->value(), kBazURL)); + auto new_cache_size = Size(origin1_); + + // Now modify the first cache. + EXPECT_TRUE(CachePut(original_handle->value(), kBarURL)); + + // Now deref both caches, and recreate the storage manager. + original_handle = nullptr; + new_handle = nullptr; + EXPECT_TRUE(FlushCacheStorageIndex(origin1_)); + DestroyStorageManager(); + CreateStorageManager(); + + EXPECT_TRUE(Open(origin1_, kCacheName)); + EXPECT_EQ(new_cache_size, Size(origin1_)); +} + +TEST_F(CacheStorageManagerTest, CacheSizeCorrectAfterReopen) { + const GURL kFooURL("http://example.com/foo"); + const std::string kCacheName = "foo"; + + EXPECT_TRUE(Open(origin1_, kCacheName)); + auto original_handle = std::move(callback_cache_handle_); + EXPECT_TRUE(CachePut(original_handle->value(), kFooURL)); + auto size_before_close = Size(origin1_); + EXPECT_GT(size_before_close, 0); + + DestroyStorageManager(); + CreateStorageManager(); + + EXPECT_TRUE(Open(origin1_, kCacheName)); + EXPECT_EQ(size_before_close, Size(origin1_)); +} + // With a memory cache the cache can't be freed from memory until the client // calls delete. TEST_F(CacheStorageManagerMemoryOnlyTest, MemoryLosesReferenceOnlyAfterDelete) { @@ -865,6 +1006,125 @@ } } +TEST_F(CacheStorageManagerTest, GetAllOriginsUsageWithOldIndex) { + // Write a single value (V1) to the cache. + const GURL kFooURL = origin1_.Resolve("foo"); + const std::string kCacheName = "foo"; + EXPECT_TRUE(Open(origin1_, kCacheName)); + std::unique_ptr<CacheStorageCacheHandle> original_handle = + std::move(callback_cache_handle_); + + EXPECT_TRUE(CachePut(original_handle->value(), kFooURL)); + int64_t cache_size_v1 = Size(origin1_); + base::FilePath storage_dir = original_handle->value()->path().DirName(); + original_handle = nullptr; + EXPECT_GE(cache_size_v1, 0); + + // Close the caches and cache manager. + EXPECT_TRUE(FlushCacheStorageIndex(origin1_)); + DestroyStorageManager(); + + // Save a copy of the V1 index. + EXPECT_TRUE(IsIndexFileCurrent(storage_dir)); + base::FilePath index_path = storage_dir.AppendASCII("index.txt"); + EXPECT_TRUE(base::PathExists(index_path)); + base::FilePath backup_index_path = storage_dir.AppendASCII("index.txt.bak"); + EXPECT_TRUE(base::CopyFile(index_path, backup_index_path)); + + // Create a new CacheStorageManager that hasn't yet loaded the origin. + CreateStorageManager(); + quota_manager_proxy_->SimulateQuotaManagerDestroyed(); + cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); + + // Create a second value (V2) in the cache. + EXPECT_TRUE(Open(origin1_, kCacheName)); + original_handle = std::move(callback_cache_handle_); + const GURL kBarURL = origin1_.Resolve("bar"); + EXPECT_TRUE(CachePut(original_handle->value(), kBarURL)); + original_handle = nullptr; + + std::vector<CacheStorageUsageInfo> usage = GetAllOriginsUsage(); + ASSERT_EQ(1ULL, usage.size()); + int64_t usage_before_close = usage[0].total_size_bytes; + EXPECT_GT(usage_before_close, 0); + + // Close the caches and cache manager. + DestroyStorageManager(); + + // Restore the index to the V1 state. Make the access/mod times of index file + // older than the other directories in the store to trigger size + // recalculation. + EXPECT_TRUE(base::CopyFile(backup_index_path, index_path)); + base::Time t = base::Time::Now() - base::TimeDelta::FromHours(1); + EXPECT_TRUE(base::TouchFile(index_path, t, t)); + EXPECT_FALSE(IsIndexFileCurrent(storage_dir)); + + CreateStorageManager(); + usage = GetAllOriginsUsage(); + ASSERT_EQ(1ULL, usage.size()); + + EXPECT_EQ(usage_before_close, usage[0].total_size_bytes); + + EXPECT_FALSE(usage[0].last_modified.is_null()); +} + +TEST_F(CacheStorageManagerTest, GetOriginSizeWithOldIndex) { + // Write a single value (V1) to the cache. + const GURL kFooURL = origin1_.Resolve("foo"); + const std::string kCacheName = "foo"; + EXPECT_TRUE(Open(origin1_, kCacheName)); + std::unique_ptr<CacheStorageCacheHandle> original_handle = + std::move(callback_cache_handle_); + + EXPECT_TRUE(CachePut(original_handle->value(), kFooURL)); + int64_t cache_size_v1 = Size(origin1_); + base::FilePath storage_dir = original_handle->value()->path().DirName(); + original_handle = nullptr; + EXPECT_GE(cache_size_v1, 0); + + // Close the caches and cache manager. + EXPECT_TRUE(FlushCacheStorageIndex(origin1_)); + DestroyStorageManager(); + + // Save a copy of the V1 index. + EXPECT_TRUE(IsIndexFileCurrent(storage_dir)); + base::FilePath index_path = storage_dir.AppendASCII("index.txt"); + EXPECT_TRUE(base::PathExists(index_path)); + base::FilePath backup_index_path = storage_dir.AppendASCII("index.txt.bak"); + EXPECT_TRUE(base::CopyFile(index_path, backup_index_path)); + + // Create a new CacheStorageManager that hasn't yet loaded the origin. + CreateStorageManager(); + quota_manager_proxy_->SimulateQuotaManagerDestroyed(); + cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); + + // Reopen the cache and write a second value (V2). + EXPECT_TRUE(Open(origin1_, kCacheName)); + original_handle = std::move(callback_cache_handle_); + const GURL kBarURL = origin1_.Resolve("bar"); + EXPECT_TRUE(CachePut(original_handle->value(), kBarURL)); + original_handle = nullptr; + int64_t cache_size_v2 = Size(origin1_); + EXPECT_GE(cache_size_v2, 0); + + // Close the caches and cache manager. + DestroyStorageManager(); + + // Restore the index to the V1 state. + EXPECT_TRUE(base::CopyFile(backup_index_path, index_path)); + + // Make the access/mod times of index file older than the other files in the + // cache to trigger size recalculation. + base::Time t = base::Time::Now() - base::TimeDelta::FromHours(1); + EXPECT_TRUE(base::TouchFile(index_path, t, t)); + EXPECT_FALSE(IsIndexFileCurrent(storage_dir)); + + // Reopen the cache and ensure the size is correct for the V2 value. + CreateStorageManager(); + EXPECT_TRUE(Open(origin1_, kCacheName)); + EXPECT_EQ(cache_size_v2, Size(origin1_)); +} + TEST_P(CacheStorageManagerTestP, GetSizeThenCloseAllCaches) { EXPECT_TRUE(Open(origin1_, "foo")); EXPECT_TRUE(CachePut(callback_cache_handle_->value(),
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.cc b/content/browser/dom_storage/dom_storage_context_wrapper.cc index 56e9fb10..60083d7e 100644 --- a/content/browser/dom_storage/dom_storage_context_wrapper.cc +++ b/content/browser/dom_storage/dom_storage_context_wrapper.cc
@@ -19,24 +19,17 @@ #include "base/strings/utf_string_conversions.h" #include "base/task_scheduler/post_task.h" #include "base/threading/thread_task_runner_handle.h" -#include "components/filesystem/public/interfaces/directory.mojom.h" -#include "components/leveldb/public/interfaces/leveldb.mojom.h" #include "content/browser/dom_storage/dom_storage_area.h" #include "content/browser/dom_storage/dom_storage_context_impl.h" #include "content/browser/dom_storage/dom_storage_task_runner.h" +#include "content/browser/dom_storage/local_storage_context_mojo.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" -#include "content/browser/leveldb_wrapper_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/local_storage_usage_info.h" #include "content/public/browser/session_storage_usage_info.h" #include "content/public/common/content_client.h" #include "content/public/common/content_features.h" -#include "mojo/common/common_type_converters.h" -#include "services/file/public/interfaces/constants.mojom.h" -#include "services/file/public/interfaces/file_system.mojom.h" -#include "services/service_manager/public/cpp/connection.h" -#include "services/service_manager/public/cpp/connector.h" namespace content { namespace { @@ -82,182 +75,6 @@ } // namespace -// Used for mojo-based LocalStorage implementation (behind --mojo-local-storage -// for now). -class DOMStorageContextWrapper::MojoState { - public: - MojoState(service_manager::Connector* connector, - const base::FilePath& subdirectory) - : connector_(connector), - // TODO(michaeln): Enable writing to disk when db is versioned, - // for now using an empty subdirectory to use an in-memory db. - // subdirectory_(subdirectory), - connection_state_(NO_CONNECTION), - weak_ptr_factory_(this) {} - - void OpenLocalStorage(const url::Origin& origin, - mojom::LevelDBWrapperRequest request); - - private: - void OnLevelDDWrapperHasNoBindings(const url::Origin& origin) { - DCHECK(level_db_wrappers_.find(origin) != level_db_wrappers_.end()); - level_db_wrappers_.erase(origin); - } - - void OnUserServiceConnectionComplete() { - CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, - file_service_connection_->GetResult()); - } - - void OnUserServiceConnectionError() { - CHECK(false); - } - - // Part of our asynchronous directory opening called from OpenLocalStorage(). - void OnDirectoryOpened(filesystem::mojom::FileError err); - void OnDatabaseOpened(leveldb::mojom::DatabaseError status); - - // The (possibly delayed) implementation of OpenLocalStorage(). Can be called - // directly from that function, or through |on_database_open_callbacks_|. - void BindLocalStorage(const url::Origin& origin, - mojom::LevelDBWrapperRequest request); - - service_manager::Connector* const connector_; - const base::FilePath subdirectory_; - - enum ConnectionState { - NO_CONNECTION, - CONNECTION_IN_PROGRESS, - CONNECTION_FINISHED - } connection_state_; - - std::unique_ptr<service_manager::Connection> file_service_connection_; - - file::mojom::FileSystemPtr file_system_; - filesystem::mojom::DirectoryPtr directory_; - - leveldb::mojom::LevelDBServicePtr leveldb_service_; - leveldb::mojom::LevelDBDatabasePtr database_; - - std::vector<base::Closure> on_database_opened_callbacks_; - - // Maps between an origin and its prefixed LevelDB view. - std::map<url::Origin, std::unique_ptr<LevelDBWrapperImpl>> level_db_wrappers_; - - base::WeakPtrFactory<MojoState> weak_ptr_factory_; -}; - -void DOMStorageContextWrapper::MojoState::OpenLocalStorage( - const url::Origin& origin, - mojom::LevelDBWrapperRequest request) { - // If we don't have a filesystem_connection_, we'll need to establish one. - if (connection_state_ == NO_CONNECTION) { - CHECK(connector_); - file_service_connection_ = - connector_->Connect(file::mojom::kServiceName); - connection_state_ = CONNECTION_IN_PROGRESS; - file_service_connection_->AddConnectionCompletedClosure( - base::Bind(&MojoState::OnUserServiceConnectionComplete, - weak_ptr_factory_.GetWeakPtr())); - file_service_connection_->SetConnectionLostClosure( - base::Bind(&MojoState::OnUserServiceConnectionError, - weak_ptr_factory_.GetWeakPtr())); - - if (!subdirectory_.empty()) { - // We were given a subdirectory to write to. Get it and use a disk backed - // database. - file_service_connection_->GetInterface(&file_system_); - file_system_->GetSubDirectory(subdirectory_.AsUTF8Unsafe(), - MakeRequest(&directory_), - base::Bind(&MojoState::OnDirectoryOpened, - weak_ptr_factory_.GetWeakPtr())); - } else { - // We were not given a subdirectory. Use a memory backed database. - file_service_connection_->GetInterface(&leveldb_service_); - leveldb_service_->OpenInMemory( - MakeRequest(&database_), base::Bind(&MojoState::OnDatabaseOpened, - weak_ptr_factory_.GetWeakPtr())); - } - } - - if (connection_state_ == CONNECTION_IN_PROGRESS) { - // Queue this OpenLocalStorage call for when we have a level db pointer. - on_database_opened_callbacks_.push_back( - base::Bind(&MojoState::BindLocalStorage, weak_ptr_factory_.GetWeakPtr(), - origin, base::Passed(&request))); - return; - } - - BindLocalStorage(origin, std::move(request)); -} - -void DOMStorageContextWrapper::MojoState::OnDirectoryOpened( - filesystem::mojom::FileError err) { - if (err != filesystem::mojom::FileError::OK) { - // We failed to open the directory; continue with startup so that we create - // the |level_db_wrappers_|. - OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR); - return; - } - - // Now that we have a directory, connect to the LevelDB service and get our - // database. - file_service_connection_->GetInterface(&leveldb_service_); - - leveldb_service_->Open( - std::move(directory_), "leveldb", MakeRequest(&database_), - base::Bind(&MojoState::OnDatabaseOpened, weak_ptr_factory_.GetWeakPtr())); -} - -void DOMStorageContextWrapper::MojoState::OnDatabaseOpened( - leveldb::mojom::DatabaseError status) { - if (status != leveldb::mojom::DatabaseError::OK) { - // If we failed to open the database, reset the service object so we pass - // null pointers to our wrappers. - database_.reset(); - leveldb_service_.reset(); - } - - // We no longer need the file service; we've either transferred |directory_| - // to the leveldb service, or we got a file error and no more is possible. - directory_.reset(); - file_system_.reset(); - - // |leveldb_| should be known to either be valid or invalid by now. Run our - // delayed bindings. - connection_state_ = CONNECTION_FINISHED; - for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) - on_database_opened_callbacks_[i].Run(); - on_database_opened_callbacks_.clear(); -} - -void DOMStorageContextWrapper::MojoState::BindLocalStorage( - const url::Origin& origin, - mojom::LevelDBWrapperRequest request) { - // Delay for a moment after a value is set in anticipation - // of other values being set, so changes are batched. - const int kCommitDefaultDelaySecs = 5; - - // To avoid excessive IO we apply limits to the amount of data being written - // and the frequency of writes. - const int kMaxBytesPerHour = kPerStorageAreaQuota; - const int kMaxCommitsPerHour = 60; - - auto found = level_db_wrappers_.find(origin); - if (found == level_db_wrappers_.end()) { - level_db_wrappers_[origin] = base::MakeUnique<LevelDBWrapperImpl>( - database_.get(), origin.Serialize(), - kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance, - base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs), kMaxBytesPerHour, - kMaxCommitsPerHour, - base::Bind(&MojoState::OnLevelDDWrapperHasNoBindings, - base::Unretained(this), origin)); - found = level_db_wrappers_.find(origin); - } - - found->second->Bind(std::move(request)); -} - DOMStorageContextWrapper::DOMStorageContextWrapper( service_manager::Connector* connector, const base::FilePath& profile_path, @@ -266,7 +83,11 @@ base::FilePath storage_dir; if (!profile_path.empty()) storage_dir = local_partition_path.AppendASCII(kLocalStorageDirectory); - mojo_state_.reset(new MojoState(connector, storage_dir)); + // TODO(michaeln): Enable writing to disk when db is versioned, + // for now using an empty subdirectory to use an in-memory db. + // subdirectory_(subdirectory), + mojo_state_.reset(new LocalStorageContextMojo( + connector, base::FilePath() /* storage_dir */)); base::FilePath data_path; if (!profile_path.empty()) @@ -282,7 +103,7 @@ base::TaskTraits dom_storage_traits = base::TaskTraits() .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN) - .WithFileIO() + .MayBlock() .WithPriority(base::TaskPriority::USER_BLOCKING); primary_sequence = base::CreateSequencedTaskRunnerWithTraits(dom_storage_traits);
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.h b/content/browser/dom_storage/dom_storage_context_wrapper.h index e6b5dbd..2a05e800 100644 --- a/content/browser/dom_storage/dom_storage_context_wrapper.h +++ b/content/browser/dom_storage/dom_storage_context_wrapper.h
@@ -33,6 +33,7 @@ namespace content { class DOMStorageContextImpl; +class LocalStorageContextMojo; // This is owned by Storage Partition and encapsulates all its dom storage // state. @@ -93,10 +94,9 @@ void PurgeMemory(DOMStorageContextImpl::PurgeOption purge_option); - // An inner class to keep all mojo-ish details together and not bleed them - // through the public interface. - class MojoState; - std::unique_ptr<MojoState> mojo_state_; + // Keep all mojo-ish details together and not bleed them through the public + // interface. + std::unique_ptr<LocalStorageContextMojo> mojo_state_; // To receive memory pressure signals. std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc new file mode 100644 index 0000000..eb34a7aa --- /dev/null +++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -0,0 +1,164 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/local_storage_context_mojo.h" + +#include "content/browser/leveldb_wrapper_impl.h" +#include "content/common/dom_storage/dom_storage_types.h" +#include "services/file/public/interfaces/constants.mojom.h" +#include "services/service_manager/public/cpp/connection.h" +#include "services/service_manager/public/cpp/connector.h" + +namespace content { + +namespace { +const char kOriginSeparator = '\x00'; +} + +LocalStorageContextMojo::LocalStorageContextMojo( + service_manager::Connector* connector, + const base::FilePath& subdirectory) + : connector_(connector), + subdirectory_(subdirectory), + weak_ptr_factory_(this) {} + +LocalStorageContextMojo::~LocalStorageContextMojo() {} + +void LocalStorageContextMojo::OpenLocalStorage( + const url::Origin& origin, + mojom::LevelDBWrapperRequest request) { + // If we don't have a filesystem_connection_, we'll need to establish one. + if (connection_state_ == NO_CONNECTION) { + CHECK(connector_); + file_service_connection_ = connector_->Connect(file::mojom::kServiceName); + connection_state_ = CONNECTION_IN_PROGRESS; + file_service_connection_->AddConnectionCompletedClosure( + base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionComplete, + weak_ptr_factory_.GetWeakPtr())); + file_service_connection_->SetConnectionLostClosure( + base::Bind(&LocalStorageContextMojo::OnUserServiceConnectionError, + weak_ptr_factory_.GetWeakPtr())); + + if (!subdirectory_.empty()) { + // We were given a subdirectory to write to. Get it and use a disk backed + // database. + file_service_connection_->GetInterface(&file_system_); + file_system_->GetSubDirectory( + subdirectory_.AsUTF8Unsafe(), MakeRequest(&directory_), + base::Bind(&LocalStorageContextMojo::OnDirectoryOpened, + weak_ptr_factory_.GetWeakPtr())); + } else { + // We were not given a subdirectory. Use a memory backed database. + file_service_connection_->GetInterface(&leveldb_service_); + leveldb_service_->OpenInMemory( + MakeRequest(&database_), + base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, + weak_ptr_factory_.GetWeakPtr())); + } + } + + if (connection_state_ == CONNECTION_IN_PROGRESS) { + // Queue this OpenLocalStorage call for when we have a level db pointer. + on_database_opened_callbacks_.push_back(base::Bind( + &LocalStorageContextMojo::BindLocalStorage, + weak_ptr_factory_.GetWeakPtr(), origin, base::Passed(&request))); + return; + } + + BindLocalStorage(origin, std::move(request)); +} + +void LocalStorageContextMojo::SetDatabaseForTesting( + leveldb::mojom::LevelDBDatabasePtr database) { + database_ = std::move(database); + OnDatabaseOpened(leveldb::mojom::DatabaseError::OK); +} + +void LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings( + const url::Origin& origin) { + DCHECK(level_db_wrappers_.find(origin) != level_db_wrappers_.end()); + level_db_wrappers_.erase(origin); +} + +void LocalStorageContextMojo::OnUserServiceConnectionComplete() { + CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, + file_service_connection_->GetResult()); +} + +void LocalStorageContextMojo::OnUserServiceConnectionError() { + CHECK(false); +} + +// Part of our asynchronous directory opening called from OpenLocalStorage(). +void LocalStorageContextMojo::OnDirectoryOpened( + filesystem::mojom::FileError err) { + if (err != filesystem::mojom::FileError::OK) { + // We failed to open the directory; continue with startup so that we create + // the |level_db_wrappers_|. + OnDatabaseOpened(leveldb::mojom::DatabaseError::IO_ERROR); + return; + } + + // Now that we have a directory, connect to the LevelDB service and get our + // database. + file_service_connection_->GetInterface(&leveldb_service_); + + leveldb_service_->Open(std::move(directory_), "leveldb", + MakeRequest(&database_), + base::Bind(&LocalStorageContextMojo::OnDatabaseOpened, + weak_ptr_factory_.GetWeakPtr())); +} + +void LocalStorageContextMojo::OnDatabaseOpened( + leveldb::mojom::DatabaseError status) { + if (status != leveldb::mojom::DatabaseError::OK) { + // If we failed to open the database, reset the service object so we pass + // null pointers to our wrappers. + database_.reset(); + leveldb_service_.reset(); + } + + // We no longer need the file service; we've either transferred |directory_| + // to the leveldb service, or we got a file error and no more is possible. + directory_.reset(); + file_system_.reset(); + + // |leveldb_| should be known to either be valid or invalid by now. Run our + // delayed bindings. + connection_state_ = CONNECTION_FINISHED; + for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) + on_database_opened_callbacks_[i].Run(); + on_database_opened_callbacks_.clear(); +} + +// The (possibly delayed) implementation of OpenLocalStorage(). Can be called +// directly from that function, or through |on_database_open_callbacks_|. +void LocalStorageContextMojo::BindLocalStorage( + const url::Origin& origin, + mojom::LevelDBWrapperRequest request) { + // Delay for a moment after a value is set in anticipation + // of other values being set, so changes are batched. + const int kCommitDefaultDelaySecs = 5; + + // To avoid excessive IO we apply limits to the amount of data being written + // and the frequency of writes. + const int kMaxBytesPerHour = kPerStorageAreaQuota; + const int kMaxCommitsPerHour = 60; + + auto found = level_db_wrappers_.find(origin); + if (found == level_db_wrappers_.end()) { + level_db_wrappers_[origin] = base::MakeUnique<LevelDBWrapperImpl>( + database_.get(), origin.Serialize() + kOriginSeparator, + kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance, + base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs), kMaxBytesPerHour, + kMaxCommitsPerHour, + base::Bind(&LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings, + base::Unretained(this), origin)); + found = level_db_wrappers_.find(origin); + } + + found->second->Bind(std::move(request)); +} + +} // namespace content
diff --git a/content/browser/dom_storage/local_storage_context_mojo.h b/content/browser/dom_storage/local_storage_context_mojo.h new file mode 100644 index 0000000..6ca0df4 --- /dev/null +++ b/content/browser/dom_storage/local_storage_context_mojo.h
@@ -0,0 +1,79 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DOM_STORAGE_LOCAL_STORAGE_CONTEXT_MOJO_H_ +#define CONTENT_BROWSER_DOM_STORAGE_LOCAL_STORAGE_CONTEXT_MOJO_H_ + +#include <memory> + +#include "base/files/file_path.h" +#include "content/common/content_export.h" +#include "content/common/leveldb_wrapper.mojom.h" +#include "services/file/public/interfaces/file_system.mojom.h" +#include "url/origin.h" + +namespace service_manager { +class Connection; +class Connector; +} + +namespace content { + +class LevelDBWrapperImpl; + +// Used for mojo-based LocalStorage implementation (behind --mojo-local-storage +// for now). +class CONTENT_EXPORT LocalStorageContextMojo { + public: + LocalStorageContextMojo(service_manager::Connector* connector, + const base::FilePath& subdirectory); + ~LocalStorageContextMojo(); + + void OpenLocalStorage(const url::Origin& origin, + mojom::LevelDBWrapperRequest request); + + void SetDatabaseForTesting(leveldb::mojom::LevelDBDatabasePtr database); + + private: + void OnLevelDBWrapperHasNoBindings(const url::Origin& origin); + void OnUserServiceConnectionComplete(); + void OnUserServiceConnectionError(); + + // Part of our asynchronous directory opening called from OpenLocalStorage(). + void OnDirectoryOpened(filesystem::mojom::FileError err); + void OnDatabaseOpened(leveldb::mojom::DatabaseError status); + + // The (possibly delayed) implementation of OpenLocalStorage(). Can be called + // directly from that function, or through |on_database_open_callbacks_|. + void BindLocalStorage(const url::Origin& origin, + mojom::LevelDBWrapperRequest request); + + service_manager::Connector* const connector_; + const base::FilePath subdirectory_; + + enum ConnectionState { + NO_CONNECTION, + CONNECTION_IN_PROGRESS, + CONNECTION_FINISHED + } connection_state_ = NO_CONNECTION; + + std::unique_ptr<service_manager::Connection> file_service_connection_; + + file::mojom::FileSystemPtr file_system_; + filesystem::mojom::DirectoryPtr directory_; + + leveldb::mojom::LevelDBServicePtr leveldb_service_; + leveldb::mojom::LevelDBDatabasePtr database_; + + std::vector<base::Closure> on_database_opened_callbacks_; + + // Maps between an origin and its prefixed LevelDB view. + std::map<url::Origin, std::unique_ptr<LevelDBWrapperImpl>> level_db_wrappers_; + + base::WeakPtrFactory<LocalStorageContextMojo> weak_ptr_factory_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOM_STORAGE_LOCAL_STORAGE_CONTEXT_MOJO_H_
diff --git a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc new file mode 100644 index 0000000..df2242f --- /dev/null +++ b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
@@ -0,0 +1,84 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/local_storage_context_mojo.h" + +#include "base/run_loop.h" +#include "components/leveldb/public/cpp/util.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/mock_leveldb_database.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "testing/gtest/include/gtest/gtest.h" + +using leveldb::StdStringToUint8Vector; +using leveldb::Uint8VectorToStdString; + +namespace content { + +namespace { + +void NoOpSuccess(bool success) {} + +} // namespace + +class LocalStorageContextMojoTest : public testing::Test { + public: + LocalStorageContextMojoTest() + : db_(&mock_data_), + db_binding_(&db_), + context_(nullptr, base::FilePath()) { + context_.SetDatabaseForTesting(db_binding_.CreateInterfacePtrAndBind()); + } + + LocalStorageContextMojo* context() { return &context_; } + const std::map<std::vector<uint8_t>, std::vector<uint8_t>>& mock_data() { + return mock_data_; + } + + private: + TestBrowserThreadBundle thread_bundle_; + std::map<std::vector<uint8_t>, std::vector<uint8_t>> mock_data_; + MockLevelDBDatabase db_; + mojo::Binding<leveldb::mojom::LevelDBDatabase> db_binding_; + + LocalStorageContextMojo context_; +}; + +TEST_F(LocalStorageContextMojoTest, Basic) { + auto key = StdStringToUint8Vector("key"); + auto value = StdStringToUint8Vector("value"); + + mojom::LevelDBWrapperPtr wrapper; + context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")), + MakeRequest(&wrapper)); + wrapper->Put(key, value, "source", base::Bind(&NoOpSuccess)); + wrapper.reset(); + + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(1u, mock_data().size()); + EXPECT_EQ(value, mock_data().begin()->second); +} + +TEST_F(LocalStorageContextMojoTest, OriginsAreIndependent) { + url::Origin origin1(GURL("http://foobar.com:123")); + url::Origin origin2(GURL("http://foobar.com:1234")); + auto key1 = StdStringToUint8Vector("4key"); + auto key2 = StdStringToUint8Vector("key"); + auto value = StdStringToUint8Vector("value"); + + mojom::LevelDBWrapperPtr wrapper; + context()->OpenLocalStorage(origin1, MakeRequest(&wrapper)); + wrapper->Put(key1, value, "source", base::Bind(&NoOpSuccess)); + wrapper.reset(); + + context()->OpenLocalStorage(origin2, MakeRequest(&wrapper)); + wrapper->Put(key2, value, "source", base::Bind(&NoOpSuccess)); + wrapper.reset(); + + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(2u, mock_data().size()); + EXPECT_EQ(value, mock_data().begin()->second); +} + +} // namespace content
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc index 55523b85..5bde4bb 100644 --- a/content/browser/frame_host/navigation_controller_impl.cc +++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -135,6 +135,35 @@ return last_entry && last_entry->GetIsOverridingUserAgent(); } +// Returns true if the PageTransition in the |entry| require this navigation to +// be treated as a reload. For e.g. navigating to the last committed url via +// the address bar or clicking on a link which results in a navigation to the +// last committed or pending navigation, etc. +bool ShouldTreatNavigationAsReload(const NavigationEntry* entry) { + if (!entry) + return false; + + // We treat (PAGE_TRANSITION_RELOAD | PAGE_TRANSITION_FROM_ADDRESS_BAR), + // PAGE_TRANSITION_TYPED or PAGE_TRANSITION_LINK transitions as navigations + // which should be treated as reloads. + if ((ui::PageTransitionCoreTypeIs(entry->GetTransitionType(), + ui::PAGE_TRANSITION_RELOAD) && + (entry->GetTransitionType() & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR))) { + return true; + } + + if (ui::PageTransitionCoreTypeIs(entry->GetTransitionType(), + ui::PAGE_TRANSITION_TYPED)) { + return true; + } + + if (ui::PageTransitionCoreTypeIs(entry->GetTransitionType(), + ui::PAGE_TRANSITION_LINK)) { + return true; + } + return false; +} + } // namespace // NavigationControllerImpl ---------------------------------------------------- @@ -212,11 +241,14 @@ NavigationControllerDelegate* delegate, BrowserContext* browser_context) : browser_context_(browser_context), - pending_entry_(NULL), + pending_entry_(nullptr), + last_pending_entry_(nullptr), failed_pending_entry_id_(0), last_committed_entry_index_(-1), pending_entry_index_(-1), transient_entry_index_(-1), + last_pending_entry_index_(-1), + last_transient_entry_index_(-1), delegate_(delegate), ssl_manager_(this), needs_reload_(false), @@ -413,6 +445,12 @@ void NavigationControllerImpl::LoadEntry( std::unique_ptr<NavigationEntryImpl> entry) { + // Remember the last pending entry for which we haven't received a response + // yet. This will be deleted in the NavigateToPendingEntry() function. + last_pending_entry_ = pending_entry_; + last_pending_entry_index_ = pending_entry_index_; + last_transient_entry_index_ = transient_entry_index_; + pending_entry_ = nullptr; // When navigating to a new page, we don't know for sure if we will actually // end up leaving the current page. The new page load could for example // result in a download or a 'no content' response (e.g., a mailto: URL). @@ -1765,10 +1803,56 @@ // navigation to succeed. The interstitial will stay visible until the // resulting DidNavigate. if (delegate_->GetInterstitialPage()) { - static_cast<InterstitialPageImpl*>(delegate_->GetInterstitialPage())-> - CancelForNavigation(); + static_cast<InterstitialPageImpl*>(delegate_->GetInterstitialPage()) + ->CancelForNavigation(); } + // The last navigation is the last pending navigation which hasn't been + // committed yet, or the last committed navigation. + NavigationEntryImpl* last_navigation = + last_pending_entry_ ? last_pending_entry_ : GetLastCommittedEntry(); + + // Convert Enter-in-omnibox to a reload. This is what Blink does in + // FrameLoader, but we want to handle it here so that if the navigation is + // redirected or handled purely on the browser side in PlzNavigate we have the + // same behaviour as Blink would. Note that we don't want to convert to a + // reload for history navigations, so this must be above the retrieval of the + // pending_entry_ below when pending_entry_index_ is used. + if (reload_type == ReloadType::NONE && last_navigation && pending_entry_ && + // Please refer to the ShouldTreatNavigationAsReload() function for info + // on which navigations are treated as reloads. In general navigating to + // the last committed or pending entry via the address bar, clicking on + // a link, etc would be treated as reloads. + ShouldTreatNavigationAsReload(pending_entry_) && + // Skip entries with SSL errors. + !last_navigation->ssl_error() && + // Ignore interstitial pages + last_transient_entry_index_ == -1 && + pending_entry_->frame_tree_node_id() == -1 && + pending_entry_->GetURL() == last_navigation->GetURL() && + !pending_entry_->GetHasPostData() && !last_navigation->GetHasPostData() && + // This check is required for cases like view-source:, etc. Here the URL + // of the navigation entry would contain the url of the page, while the + // virtual URL contains the full URL including the view-source prefix. + last_navigation->GetVirtualURL() == pending_entry_->GetVirtualURL() && + // This check is required for Android WebView loadDataWithBaseURL. Apps + // can pass in anything in the base URL and we need to ensure that these + // match before classifying it as a reload. + (pending_entry_->GetURL().SchemeIs(url::kDataScheme) && + pending_entry_->GetBaseURLForDataURL().is_valid() + ? pending_entry_->GetBaseURLForDataURL() == + last_navigation->GetBaseURLForDataURL() + : true)) { + reload_type = ReloadType::NORMAL; + } + + if (last_pending_entry_index_ == -1 && last_pending_entry_) + delete last_pending_entry_; + + last_transient_entry_index_ = -1; + last_pending_entry_ = nullptr; + last_pending_entry_index_ = -1; + // For session history navigations only the pending_entry_index_ is set. if (!pending_entry_) { CHECK_NE(pending_entry_index_, -1); @@ -2011,6 +2095,11 @@ pending_entry_index_ = -1; } +void NavigationControllerImpl::SetPendingNavigationSSLError(bool error) { + if (pending_entry_) + pending_entry_->set_ssl_error(error); +} + void NavigationControllerImpl::DiscardTransientEntry() { if (transient_entry_index_ == -1) return;
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h index 0970fb9..8412539 100644 --- a/content/browser/frame_host/navigation_controller_impl.h +++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -201,6 +201,10 @@ // entry is being discarded because it failed to load. void DiscardPendingEntry(bool was_failure); + // Sets a flag on the pending NavigationEntryImpl instance if any that the + // navigation failed due to an SSL error. + void SetPendingNavigationSSLError(bool error); + private: friend class RestoreHelper; @@ -362,6 +366,10 @@ // the memory management. NavigationEntryImpl* pending_entry_; + // Navigations could occur in succession. This field holds the last pending + // entry for which we haven't received a response yet. + NavigationEntryImpl* last_pending_entry_; + // If a new entry fails loading, details about it are temporarily held here // until the error page is shown (or 0 otherwise). // @@ -386,6 +394,13 @@ // after the transient entry will become invalid if you navigate forward. int transient_entry_index_; + // The index of the last pending entry if it is in entries, or -1 if it was + // created by LoadURL. + int last_pending_entry_index_; + + // The index of the last transient entry. Defaults to -1. + int last_transient_entry_index_; + // The delegate associated with the controller. Possibly NULL during // setup. NavigationControllerDelegate* delegate_;
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc index 080f3d2..637dec0 100644 --- a/content/browser/frame_host/navigation_controller_impl_unittest.cc +++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -218,6 +218,7 @@ void DidStartNavigationToPendingEntry(const GURL& url, ReloadType reload_type) override { navigated_url_ = url; + last_reload_type_ = reload_type; } void NavigationEntryCommitted( @@ -262,6 +263,7 @@ protected: GURL navigated_url_; size_t navigation_entry_committed_counter_; + ReloadType last_reload_type_; }; void RegisterForAllNavNotifications(TestNotificationTracker* tracker, @@ -5210,4 +5212,87 @@ EXPECT_EQ(url_b, controller.GetEntryAtIndex(2)->GetURL()); } +// Tests that successive navigations with intermittent duplicate navigations +// are correctly marked as reload in the navigation controller. +// We test the cases where in a navigation is pending/comitted before the new +// navigation is initiated. +// http://crbug.com/664319 +TEST_F(NavigationControllerTest, MultipleNavigationsAndReload) { + NavigationControllerImpl& controller = controller_impl(); + TestNotificationTracker notifications; + RegisterForAllNavNotifications(¬ifications, &controller); + + GURL initial_url("http://www.google.com"); + GURL url_1("http://foo.com"); + GURL url_2("http://foo2.com"); + + // Test 1. + // A normal navigation to initial_url should not be marked as a reload. + controller.LoadURL(initial_url, Referrer(), ui::PAGE_TRANSITION_TYPED, + std::string()); + EXPECT_EQ(initial_url, controller.GetVisibleEntry()->GetURL()); + main_test_rfh()->SimulateNavigationStart(initial_url); + EXPECT_EQ(initial_url, controller.GetVisibleEntry()->GetURL()); + + main_test_rfh()->SimulateNavigationCommit(initial_url); + EXPECT_EQ(ReloadType::NONE, last_reload_type_); + + // Test 2. + // A navigation to initial_url with the navigation commit delayed should be + // marked as a reload. + controller.LoadURL(initial_url, Referrer(), ui::PAGE_TRANSITION_TYPED, + std::string()); + + EXPECT_EQ(initial_url, controller.GetVisibleEntry()->GetURL()); + main_test_rfh()->SimulateNavigationStart(initial_url); + EXPECT_EQ(initial_url, controller.GetVisibleEntry()->GetURL()); + EXPECT_EQ(ReloadType::NORMAL, last_reload_type_); + + // Test 3. + // A navigation to url_1 while the navigation to intial_url is still pending + // should not be marked as a reload. + controller.LoadURL(url_1, Referrer(), ui::PAGE_TRANSITION_TYPED, + std::string()); + + EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL()); + main_test_rfh()->SimulateNavigationStart(url_1); + EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL()); + EXPECT_EQ(ReloadType::NONE, last_reload_type_); + + // Test 4. + // A navigation to url_1 while the previous navigation to url_1 is pending + // should be marked as reload. + controller.LoadURL(url_1, Referrer(), ui::PAGE_TRANSITION_TYPED, + std::string()); + + EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL()); + main_test_rfh()->SimulateNavigationStart(url_1); + EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL()); + EXPECT_EQ(ReloadType::NORMAL, last_reload_type_); + + main_test_rfh()->SimulateNavigationCommit(initial_url); + + // Test 5 + // A navigation to url_2 followed by a navigation to the previously pending + // url_1 should not be marked as a reload. + controller.LoadURL(url_2, Referrer(), ui::PAGE_TRANSITION_TYPED, + std::string()); + EXPECT_EQ(url_2, controller.GetVisibleEntry()->GetURL()); + main_test_rfh()->SimulateNavigationStart(url_2); + EXPECT_EQ(url_2, controller.GetVisibleEntry()->GetURL()); + EXPECT_EQ(ReloadType::NONE, last_reload_type_); + + controller.LoadURL(url_1, Referrer(), ui::PAGE_TRANSITION_TYPED, + std::string()); + + EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL()); + main_test_rfh()->SimulateNavigationStart(url_1); + EXPECT_EQ(url_1, controller.GetVisibleEntry()->GetURL()); + EXPECT_EQ(ReloadType::NONE, last_reload_type_); + + main_test_rfh()->SimulateNavigationCommit(url_2); + main_test_rfh()->SimulateNavigationCommit(url_1); + main_test_rfh()->SimulateNavigationCommit(url_1); +} + } // namespace content
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc index 336e0613..79eb126 100644 --- a/content/browser/frame_host/navigation_entry_impl.cc +++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -276,7 +276,8 @@ can_load_local_resources_(false), frame_tree_node_id_(-1), reload_type_(ReloadType::NONE), - started_from_context_menu_(false) { + started_from_context_menu_(false), + ssl_error_(false) { #if defined(OS_ANDROID) has_user_gesture_ = false; #endif
diff --git a/content/browser/frame_host/navigation_entry_impl.h b/content/browser/frame_host/navigation_entry_impl.h index 4b93695b..9782bf1 100644 --- a/content/browser/frame_host/navigation_entry_impl.h +++ b/content/browser/frame_host/navigation_entry_impl.h
@@ -388,6 +388,11 @@ // Returns the history URL for a data URL to use in Blink. GURL GetHistoryURLForDataURL() const; + // These flags are set when the navigation controller gets notified of an SSL + // error while a navigation is pending. + void set_ssl_error(bool error) { ssl_error_ = error; } + bool ssl_error() const { return ssl_error_; } + #if defined(OS_ANDROID) base::TimeTicks intent_received_timestamp() const { return intent_received_timestamp_; @@ -543,6 +548,10 @@ // time (see TabNavigation for an example of this). std::map<std::string, base::string16> extra_data_; + // Set to true if the navigation controller gets notified about a SSL error + // for a pending navigation. Defaults to false. + bool ssl_error_; + DISALLOW_COPY_AND_ASSIGN(NavigationEntryImpl); };
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc index 83ef7cac..2bacf35 100644 --- a/content/browser/gpu/gpu_data_manager_impl_private.cc +++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -47,6 +47,9 @@ #include "ui/gl/gl_switches.h" #include "ui/gl/gpu_switching_manager.h" +#if defined(USE_OZONE) +#include "ui/ozone/public/ozone_switches.h" +#endif #if defined(OS_MACOSX) #include <ApplicationServices/ApplicationServices.h> #endif // OS_MACOSX @@ -768,6 +771,12 @@ command_line->AppendSwitch(switches::kCreateDefaultGLContext); } +#if defined(USE_OZONE) + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableDrmAtomic)) { + command_line->AppendSwitch(switches::kEnableDrmAtomic); + } +#endif #if defined(OS_WIN) if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VPX_DECODE) && gpu_preferences) {
diff --git a/content/browser/leveldb_wrapper_impl.cc b/content/browser/leveldb_wrapper_impl.cc index b7cb7135..e60426a2 100644 --- a/content/browser/leveldb_wrapper_impl.cc +++ b/content/browser/leveldb_wrapper_impl.cc
@@ -34,19 +34,6 @@ LevelDBWrapperImpl::CommitBatch::CommitBatch() : clear_all_first(false) {} LevelDBWrapperImpl::CommitBatch::~CommitBatch() {} -size_t LevelDBWrapperImpl::CommitBatch::GetDataSize() const { - if (changed_values.empty()) - return 0; - - size_t count = 0; - for (const auto& pair : changed_values) { - count += pair.first.size(); - if (pair.second) - count += pair.second->size(); - } - return count; -} - LevelDBWrapperImpl::LevelDBWrapperImpl( leveldb::mojom::LevelDBDatabase* database, const std::string& prefix, @@ -122,7 +109,7 @@ if (database_) { CreateCommitBatchIfNeeded(); - commit_batch_->changed_values[key] = value; + commit_batch_->changed_keys.insert(key); } std::vector<uint8_t> old_value; @@ -164,7 +151,7 @@ if (database_) { CreateCommitBatchIfNeeded(); - commit_batch_->changed_values[key] = base::nullopt; + commit_batch_->changed_keys.insert(std::move(found->first)); } std::vector<uint8_t> old_value(std::move(found->second)); @@ -189,7 +176,7 @@ if (database_ && !map_->empty()) { CreateCommitBatchIfNeeded(); commit_batch_->clear_all_first = true; - commit_batch_->changed_values.clear(); + commit_batch_->changed_keys.clear(); } map_->clear(); bytes_used_ = 0; @@ -216,12 +203,12 @@ callback.Run(true, found->second); } -void LevelDBWrapperImpl::GetAll(const std::string& source, - const GetAllCallback& callback) { +void LevelDBWrapperImpl::GetAll( + mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo complete_callback, + const GetAllCallback& callback) { if (!map_) { - LoadMap( - base::Bind(&LevelDBWrapperImpl::GetAll, base::Unretained(this), - source, callback)); + LoadMap(base::Bind(&LevelDBWrapperImpl::GetAll, base::Unretained(this), + base::Passed(std::move(complete_callback)), callback)); return; } @@ -233,15 +220,20 @@ all.push_back(std::move(kv)); } callback.Run(leveldb::mojom::DatabaseError::OK, std::move(all)); - observers_.ForAllPtrs( - [source](mojom::LevelDBObserver* observer) { - observer->GetAllComplete(source); - }); + if (complete_callback.is_valid()) { + mojom::LevelDBWrapperGetAllCallbackAssociatedPtr complete_ptr; + complete_ptr.Bind(std::move(complete_callback)); + complete_ptr->Complete(true); + } } void LevelDBWrapperImpl::OnConnectionError() { if (!bindings_.empty()) return; + // If any tasks are waiting for load to complete, delay calling the + // no_bindings_callback_ until all those tasks have completed. + if (!on_load_complete_tasks_.empty()) + return; no_bindings_callback_.Run(); } @@ -280,6 +272,11 @@ on_load_complete_tasks_.swap(tasks); for (auto& task : tasks) task.Run(); + + // We might need to call the no_bindings_callback_ here if bindings became + // empty while waiting for load to complete. + if (bindings_.empty()) + no_bindings_callback_.Run(); } void LevelDBWrapperImpl::CreateCommitBatchIfNeeded() { @@ -324,11 +321,11 @@ void LevelDBWrapperImpl::CommitChanges() { DCHECK(database_); + DCHECK(map_); if (!commit_batch_) return; commit_rate_limiter_.add_samples(1); - data_rate_limiter_.add_samples(commit_batch_->GetDataSize()); // Commit all our changes in a single batch. std::vector<leveldb::mojom::BatchedOperationPtr> operations; @@ -339,22 +336,28 @@ item->key = prefix_; operations.push_back(std::move(item)); } - for (auto& it : commit_batch_->changed_values) { + size_t data_size = 0; + for (const auto& key: commit_batch_->changed_keys) { + data_size += key.size(); leveldb::mojom::BatchedOperationPtr item = leveldb::mojom::BatchedOperation::New(); - item->key.reserve(prefix_.size() + it.first.size()); + item->key.reserve(prefix_.size() + key.size()); item->key.insert(item->key.end(), prefix_.begin(), prefix_.end()); - item->key.insert(item->key.end(), it.first.begin(), it.first.end()); - if (!it.second) { + item->key.insert(item->key.end(), key.begin(), key.end()); + auto it = map_->find(key); + if (it == map_->end()) { item->type = leveldb::mojom::BatchOperationType::DELETE_KEY; } else { item->type = leveldb::mojom::BatchOperationType::PUT_KEY; - item->value = std::move(*(it.second)); + item->value = it->second; + data_size += it->second.size(); } operations.push_back(std::move(item)); } commit_batch_.reset(); + data_rate_limiter_.add_samples(data_size); + ++commit_batches_in_flight_; // TODO(michaeln): Currently there is no guarantee LevelDBDatabaseImp::Write
diff --git a/content/browser/leveldb_wrapper_impl.h b/content/browser/leveldb_wrapper_impl.h index 663d5d85..df6104ae 100644 --- a/content/browser/leveldb_wrapper_impl.h +++ b/content/browser/leveldb_wrapper_impl.h
@@ -33,7 +33,7 @@ class CONTENT_EXPORT LevelDBWrapperImpl : public mojom::LevelDBWrapper { public: // |no_bindings_callback| will be called when this object has no more - // bindings. + // bindings and all pending modifications have been processed. LevelDBWrapperImpl(leveldb::mojom::LevelDBDatabase* database, const std::string& prefix, size_t max_size, @@ -55,8 +55,6 @@ friend class LevelDBWrapperImplTest; using ValueMap = std::map<std::vector<uint8_t>, std::vector<uint8_t>>; - using ChangedValueMap = - std::map<std::vector<uint8_t>, base::Optional<std::vector<uint8_t>>>; // Used to rate limit commits. class RateLimiter { @@ -83,11 +81,10 @@ struct CommitBatch { bool clear_all_first; - ChangedValueMap changed_values; + std::set<std::vector<uint8_t>> changed_keys; CommitBatch(); ~CommitBatch(); - size_t GetDataSize() const; }; // LevelDBWrapperImpl: @@ -103,8 +100,9 @@ const DeleteAllCallback& callback) override; void Get(const std::vector<uint8_t>& key, const GetCallback& callback) override; - void GetAll(const std::string& source, - const GetAllCallback& callback) override; + void GetAll( + mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo complete_callback, + const GetAllCallback& callback) override; void OnConnectionError(); void LoadMap(const base::Closure& completion_callback);
diff --git a/content/browser/leveldb_wrapper_impl_unittest.cc b/content/browser/leveldb_wrapper_impl_unittest.cc index 3fd3dd8..a40c58e7 100644 --- a/content/browser/leveldb_wrapper_impl_unittest.cc +++ b/content/browser/leveldb_wrapper_impl_unittest.cc
@@ -7,7 +7,9 @@ #include "base/run_loop.h" #include "components/leveldb/public/cpp/util.h" #include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/mock_leveldb_database.h" #include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/strong_associated_binding.h" #include "testing/gtest/include/gtest/gtest.h" using leveldb::StdStringToUint8Vector; @@ -21,146 +23,32 @@ const char* kTestSource = "source"; const size_t kTestSizeLimit = 512; -leveldb::mojom::KeyValuePtr CreateKeyValue(std::vector<uint8_t> key, - std::vector<uint8_t> value) { - leveldb::mojom::KeyValuePtr result = leveldb::mojom::KeyValue::New(); - result->key = std::move(key); - result->value = std::move(value); - return result; -} - -base::StringPiece AsStringPiece(const std::vector<uint8_t>& data) { - return base::StringPiece(reinterpret_cast<const char*>(data.data()), - data.size()); -} - -bool StartsWith(const std::vector<uint8_t>& key, - const std::vector<uint8_t>& prefix) { - return base::StartsWith(AsStringPiece(key), AsStringPiece(prefix), - base::CompareCase::SENSITIVE); -} - -std::vector<uint8_t> successor(std::vector<uint8_t> data) { - for (unsigned i = data.size(); i > 0; --i) { - if (data[i - 1] < 255) { - data[i - 1]++; - return data; - } - } - NOTREACHED(); - return data; -} - -class MockLevelDBDatabase : public leveldb::mojom::LevelDBDatabase { +class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback { public: - explicit MockLevelDBDatabase( - std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data) - : mock_data_(*mock_data) {} - - void Put(const std::vector<uint8_t>& key, - const std::vector<uint8_t>& value, - const PutCallback& callback) override { - FAIL(); - } - - void Delete(const std::vector<uint8_t>& key, - const DeleteCallback& callback) override { - FAIL(); - } - - void DeletePrefixed(const std::vector<uint8_t>& key_prefix, - const DeletePrefixedCallback& callback) override { - FAIL(); - } - - void Write(std::vector<leveldb::mojom::BatchedOperationPtr> operations, - const WriteCallback& callback) override { - for (const auto& op : operations) { - switch (op->type) { - case leveldb::mojom::BatchOperationType::PUT_KEY: - mock_data_[op->key] = *op->value; - break; - case leveldb::mojom::BatchOperationType::DELETE_KEY: - mock_data_.erase(op->key); - break; - case leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY: - mock_data_.erase(mock_data_.lower_bound(op->key), - mock_data_.lower_bound(successor(op->key))); - break; - } - } - callback.Run(leveldb::mojom::DatabaseError::OK); - } - - void Get(const std::vector<uint8_t>& key, - const GetCallback& callback) override { - FAIL(); - } - - void GetPrefixed(const std::vector<uint8_t>& key_prefix, - const GetPrefixedCallback& callback) override { - std::vector<leveldb::mojom::KeyValuePtr> data; - for (const auto& row : mock_data_) { - if (StartsWith(row.first, key_prefix)) { - data.push_back(CreateKeyValue(row.first, row.second)); - } - } - callback.Run(leveldb::mojom::DatabaseError::OK, std::move(data)); - } - - void GetSnapshot(const GetSnapshotCallback& callback) override { FAIL(); } - - void ReleaseSnapshot(const base::UnguessableToken& snapshot) override { - FAIL(); - } - - void GetFromSnapshot(const base::UnguessableToken& snapshot, - const std::vector<uint8_t>& key, - const GetCallback& callback) override { - FAIL(); - } - - void NewIterator(const NewIteratorCallback& callback) override { FAIL(); } - - void NewIteratorFromSnapshot( - const base::UnguessableToken& snapshot, - const NewIteratorFromSnapshotCallback& callback) override { - FAIL(); - } - - void ReleaseIterator(const base::UnguessableToken& iterator) override { - FAIL(); - } - - void IteratorSeekToFirst( - const base::UnguessableToken& iterator, - const IteratorSeekToFirstCallback& callback) override { - FAIL(); - } - - void IteratorSeekToLast(const base::UnguessableToken& iterator, - const IteratorSeekToLastCallback& callback) override { - FAIL(); - } - - void IteratorSeek(const base::UnguessableToken& iterator, - const std::vector<uint8_t>& target, - const IteratorSeekToLastCallback& callback) override { - FAIL(); - } - - void IteratorNext(const base::UnguessableToken& iterator, - const IteratorNextCallback& callback) override { - FAIL(); - } - - void IteratorPrev(const base::UnguessableToken& iterator, - const IteratorPrevCallback& callback) override { - FAIL(); + static mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo CreateAndBind( + mojo::AssociatedGroup* associated_group, + bool* result, + const base::Closure& callback) { + mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo ptr_info; + mojom::LevelDBWrapperGetAllCallbackAssociatedRequest request; + associated_group->CreateAssociatedInterface( + mojo::AssociatedGroup::WILL_PASS_PTR, &ptr_info, &request); + mojo::MakeStrongAssociatedBinding( + base::WrapUnique(new GetAllCallback(result, callback)), + std::move(request)); + return ptr_info; } private: - std::map<std::vector<uint8_t>, std::vector<uint8_t>>& mock_data_; + GetAllCallback(bool* result, const base::Closure& callback) + : m_result(result), m_callback(callback) {} + void Complete(bool success) override { + *m_result = success; + m_callback.Run(); + } + + bool* m_result; + base::Closure m_callback; }; void NoOp() {} @@ -211,7 +99,7 @@ level_db_wrapper_.Bind(mojo::MakeRequest(&level_db_wrapper_ptr_)); mojom::LevelDBObserverAssociatedPtrInfo ptr_info; - observer_binding_.Bind(&ptr_info, level_db_wrapper_ptr_.associated_group()); + observer_binding_.Bind(&ptr_info, associated_group()); level_db_wrapper_ptr_->AddObserver(std::move(ptr_info)); } @@ -235,6 +123,9 @@ } mojom::LevelDBWrapper* wrapper() { return level_db_wrapper_ptr_.get(); } + mojo::AssociatedGroup* associated_group() { + return level_db_wrapper_ptr_.associated_group(); + } bool GetSync(const std::vector<uint8_t>& key, std::vector<uint8_t>* result) { base::RunLoop run_loop; @@ -309,7 +200,6 @@ void AllDeleted(const std::string& source) override { observations_.push_back({Observation::kDeleteAll, "", "", "", source}); } - void GetAllComplete(const std::string& source) override {} TestBrowserThreadBundle thread_bundle_; std::map<std::vector<uint8_t>, std::vector<uint8_t>> mock_data_; @@ -353,9 +243,17 @@ TEST_F(LevelDBWrapperImplTest, GetAll) { leveldb::mojom::DatabaseError status; std::vector<mojom::KeyValuePtr> data; - EXPECT_TRUE(wrapper()->GetAll(kTestSource, &status, &data)); + base::RunLoop run_loop; + bool result = false; + EXPECT_TRUE(wrapper()->GetAll( + GetAllCallback::CreateAndBind(associated_group(), &result, + run_loop.QuitClosure()), + &status, &data)); EXPECT_EQ(leveldb::mojom::DatabaseError::OK, status); EXPECT_EQ(2u, data.size()); + EXPECT_FALSE(result); + run_loop.Run(); + EXPECT_TRUE(result); } TEST_F(LevelDBWrapperImplTest, CommitPutToDB) {
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc index a4ef557..5494a0c 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
@@ -101,6 +101,22 @@ DCHECK(!end.last_event_time.is_null()); \ DCHECK_GE(end.last_event_time, start.first_event_time); +// Touch/wheel to scroll latency using Rappor. +#define RAPPOR_TOUCH_WHEEL_TO_SCROLL_LATENCY(delegate, name, start, end) \ + CONFIRM_VALID_TIMING(start, end) \ + rappor::RapporService* rappor_service = \ + GetContentClient()->browser()->GetRapporService(); \ + if (rappor_service && delegate) { \ + std::unique_ptr<rappor::Sample> sample = \ + rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE); \ + delegate->AddDomainInfoToRapporSample(sample.get()); \ + sample->SetUInt64Field( \ + "Latency", \ + (end.last_event_time - start.first_event_time).InMicroseconds(), \ + rappor::NO_NOISE); \ + rappor_service->RecordSample(name, std::move(sample)); \ + } + // Touch/wheel to scroll latency that is mostly under 1 second. #define UMA_HISTOGRAM_TOUCH_WHEEL_TO_SCROLL_LATENCY(name, start, end) \ CONFIRM_VALID_TIMING(start, end) \ @@ -248,6 +264,7 @@ const ui::LatencyInfo& latency, const std::string event_type_name) { DCHECK(!latency.coalesced()); + DCHECK(event_type_name == "Touch" || event_type_name == "Wheel"); if (latency.coalesced()) return; @@ -266,6 +283,12 @@ ".TimeToScrollUpdateSwapBegin2", original_component, gpu_swap_begin_component); + RAPPOR_TOUCH_WHEEL_TO_SCROLL_LATENCY( + render_widget_host_delegate, + "Event.Latency.ScrollBegin." + event_type_name + + ".TimeToScrollUpdateSwapBegin2", + original_component, gpu_swap_begin_component); + // TODO(lanwei): Will remove them when M56 is stable, see // https://crbug.com/669618. UMA_HISTOGRAM_TOUCH_WHEEL_TO_SCROLL_LATENCY( @@ -284,21 +307,10 @@ ".TimeToScrollUpdateSwapBegin2", original_component, gpu_swap_begin_component); - rappor::RapporService* rappor_service = - GetContentClient()->browser()->GetRapporService(); - if (rappor_service && render_widget_host_delegate) { - std::unique_ptr<rappor::Sample> sample = - rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE); - render_widget_host_delegate->AddDomainInfoToRapporSample(sample.get()); - sample->SetUInt64Field("Latency", - (gpu_swap_begin_component.last_event_time - - original_component.first_event_time) - .InMicroseconds(), - rappor::NO_NOISE); - rappor_service->RecordSample( - "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2", - std::move(sample)); - } + RAPPOR_TOUCH_WHEEL_TO_SCROLL_LATENCY( + render_widget_host_delegate, + "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2", + original_component, gpu_swap_begin_component); } } else { // No original component found.
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc index a511ed7..908b65d 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -91,6 +91,49 @@ ResetHistograms(); } + ::testing::AssertionResult RapporSampleAssert(const char* rappor_name, + int count) { + rappor::TestSample::Shadow* sample_obj = + test_browser_client_.getTestRapporService()->GetRecordedSampleForMetric( + rappor_name); + if (count) { + if (!sample_obj) + return ::testing::AssertionFailure() + << rappor_name << " rappor sample should not be null"; + + const auto& domain_it = sample_obj->string_fields.find("Domain"); + if (domain_it == sample_obj->string_fields.end()) + return ::testing::AssertionFailure() + << rappor_name << " rappor sample should contain the string " + "attribute \"Domain\""; + const auto& domain = domain_it->second; + if (domain != "bar.com") + return ::testing::AssertionFailure() + << rappor_name << " rappor expected bar.com domain but had " + << domain << " domain"; + + const auto& latency_it = sample_obj->uint64_fields.find("Latency"); + if (latency_it == sample_obj->uint64_fields.end()) + return ::testing::AssertionFailure() + << rappor_name << " rappor sample should contain the uint64 " + "attribute \"Latency\""; + const auto& latency_noise = latency_it->second.second; + if (latency_noise != rappor::NO_NOISE) + return ::testing::AssertionFailure() + << rappor_name + << " rappor expected rappor::NO_NOISE latency but had " + << latency_noise << " latency"; + + return ::testing::AssertionSuccess(); + } else { + if (!sample_obj) + return ::testing::AssertionSuccess(); + else + return ::testing::AssertionFailure() << rappor_name + << " rappor sample should be null"; + } + } + ::testing::AssertionResult HistogramSizeEq(const char* histogram_name, int size) { uint64_t histogram_size = @@ -135,6 +178,8 @@ }; TEST_F(RenderWidgetHostLatencyTrackerTest, TestWheelToFirstScrollHistograms) { + const GURL url("http://www.foo.bar.com/subpage/1"); + contents()->NavigateAndCommit(url); for (bool rendering_on_main : {false, true}) { for (bool is_running_navigation_hint_task : {false, true}) { ResetHistograms(); @@ -160,6 +205,24 @@ INPUT_EVENT_ACK_STATE_NOT_CONSUMED); tracker()->OnFrameSwapped(wheel_latency, is_running_navigation_hint_task); + + // Rappor metrics. + EXPECT_TRUE( + RapporSampleAssert("Event.Latency.ScrollUpdate.Touch." + "TimeToScrollUpdateSwapBegin2", + 0)); + EXPECT_TRUE( + RapporSampleAssert("Event.Latency.ScrollBegin.Touch." + "TimeToScrollUpdateSwapBegin2", + 0)); + EXPECT_TRUE( + RapporSampleAssert("Event.Latency.ScrollBegin.Wheel." + "TimeToScrollUpdateSwapBegin2", + 2)); + EXPECT_EQ( + 2, test_browser_client_.getTestRapporService()->GetReportsCount()); + + // UMA histograms. EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.WheelUI", 1)); EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.WheelAcked", 1)); @@ -289,6 +352,8 @@ } TEST_F(RenderWidgetHostLatencyTrackerTest, TestTouchToFirstScrollHistograms) { + const GURL url("http://www.foo.bar.com/subpage/1"); + contents()->NavigateAndCommit(url); for (bool rendering_on_main : {false, true}) { for (bool is_running_navigation_hint_task : {false, true}) { ResetHistograms(); @@ -338,6 +403,23 @@ is_running_navigation_hint_task); } + // Rappor metrics. + EXPECT_TRUE( + RapporSampleAssert("Event.Latency.ScrollUpdate.Touch." + "TimeToScrollUpdateSwapBegin2", + 0)); + EXPECT_TRUE( + RapporSampleAssert("Event.Latency.ScrollBegin.Touch." + "TimeToScrollUpdateSwapBegin2", + 2)); + EXPECT_TRUE( + RapporSampleAssert("Event.Latency.ScrollBegin.Wheel." + "TimeToScrollUpdateSwapBegin2", + 0)); + EXPECT_EQ(2, + test_browser_client_.getTestRapporService()->GetReportsCount()); + + // UMA histograms. EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.TouchUI", 1)); EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.TouchAcked", 1)); EXPECT_TRUE(HistogramSizeEq( @@ -472,24 +554,23 @@ is_running_navigation_hint_task); } - // Rappor metrics - rappor::TestSample::Shadow* sample_obj = - test_browser_client_.getTestRapporService() - ->GetRecordedSampleForMetric( - "Event.Latency.ScrollUpdate.Touch." - "TimeToScrollUpdateSwapBegin2"); - EXPECT_NE(nullptr, sample_obj); - const auto& domain_it = sample_obj->string_fields.find("Domain"); - EXPECT_NE(domain_it, sample_obj->string_fields.end()); - EXPECT_EQ("bar.com", domain_it->second); - const auto& latency_it = sample_obj->uint64_fields.find("Latency"); - EXPECT_NE(latency_it, sample_obj->uint64_fields.end()); - EXPECT_EQ(rappor::NO_NOISE, latency_it->second.second); - + // Rappor metrics. + EXPECT_TRUE( + RapporSampleAssert("Event.Latency.ScrollUpdate.Touch." + "TimeToScrollUpdateSwapBegin2", + 2)); + EXPECT_TRUE( + RapporSampleAssert("Event.Latency.ScrollBegin.Touch." + "TimeToScrollUpdateSwapBegin2", + 0)); + EXPECT_TRUE( + RapporSampleAssert("Event.Latency.ScrollBegin.Wheel." + "TimeToScrollUpdateSwapBegin2", + 0)); EXPECT_EQ(2, test_browser_client_.getTestRapporService()->GetReportsCount()); - // UMA histograms + // UMA histograms. EXPECT_TRUE(HistogramSizeEq( "Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin2", 0)); EXPECT_TRUE(HistogramSizeEq(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 415e9df..3ac8a1a7 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -166,7 +166,6 @@ #include "device/battery/battery_monitor_impl.h" #include "device/gamepad/gamepad_monitor.h" #include "device/power_monitor/power_monitor_message_broadcaster.h" -#include "device/time_zone_monitor/time_zone_monitor.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/gpu_switches.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" @@ -1247,16 +1246,6 @@ registry.get(), base::Bind(&CreateMemoryCoordinatorHandle, GetID())); } - // BrowserMainLoop, which owns TimeZoneMonitor, is alive for the lifetime of - // Mojo communication (see BrowserMainLoop::ShutdownThreadsAndCleanUp(), - // which shuts down Mojo). Hence, passing that TimeZoneMonitor instance as - // a raw pointer here is safe. - AddUIThreadInterface( - registry.get(), - base::Bind(&device::TimeZoneMonitor::Bind, - base::Unretained( - BrowserMainLoop::GetInstance()->time_zone_monitor()))); - AddUIThreadInterface( registry.get(), base::Bind(&device::PowerMonitorMessageBroadcaster::Create));
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc index cc1d268..17cc03d 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -1518,8 +1518,9 @@ } void RenderWidgetHostViewAndroid::SendBeginFrame(cc::BeginFrameArgs args) { - TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::SendBeginFrame", - "frame_time_us", args.frame_time.ToInternalValue()); + TRACE_EVENT2("cc", "RenderWidgetHostViewAndroid::SendBeginFrame", + "frame_number", args.sequence_number, "frame_time_us", + args.frame_time.ToInternalValue()); // Synchronous compositor does not use deadline-based scheduling. // TODO(brianderson): Replace this hardcoded deadline after Android
diff --git a/content/browser/resource_context_impl.cc b/content/browser/resource_context_impl.cc index 62b4396..93e2890d 100644 --- a/content/browser/resource_context_impl.cc +++ b/content/browser/resource_context_impl.cc
@@ -87,7 +87,7 @@ new UserDataAdapter<StreamContext>( StreamContext::GetFor(browser_context))); - resource_context->DetachUserDataThread(); + resource_context->DetachFromSequence(); } } // namespace content
diff --git a/content/browser/ssl/ssl_manager.cc b/content/browser/ssl/ssl_manager.cc index c9cf39e..df2e757 100644 --- a/content/browser/ssl/ssl_manager.cc +++ b/content/browser/ssl/ssl_manager.cc
@@ -105,9 +105,11 @@ return; } - SSLManager* manager = - static_cast<NavigationControllerImpl*>(&web_contents->GetController()) - ->ssl_manager(); + NavigationControllerImpl* controller = + static_cast<NavigationControllerImpl*>(&web_contents->GetController()); + controller->SetPendingNavigationSSLError(true); + + SSLManager* manager = controller->ssl_manager(); manager->OnCertError(std::move(handler)); }
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn index 29f7f094a..945f5da 100644 --- a/content/child/BUILD.gn +++ b/content/child/BUILD.gn
@@ -177,6 +177,8 @@ "sync_load_response.h", "thread_safe_sender.cc", "thread_safe_sender.h", + "url_loader_client_impl.cc", + "url_loader_client_impl.h", "url_response_body_consumer.cc", "url_response_body_consumer.h", "v8_value_converter_impl.cc", @@ -212,6 +214,7 @@ public_deps = [ "//gpu/ipc/client:client", + "//services/service_manager/public/interfaces:interfaces_blink", ] deps = [
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc index a87fcd54..4f3c2aa 100644 --- a/content/child/blink_platform_impl.cc +++ b/content/child/blink_platform_impl.cc
@@ -48,7 +48,10 @@ #include "content/child/web_url_request_util.h" #include "content/child/worker_thread_registry.h" #include "content/public/common/content_client.h" +#include "content/public/common/service_manager_connection.h" #include "net/base/net_errors.h" +#include "services/service_manager/public/cpp/connector.h" +#include "services/service_manager/public/interfaces/connector.mojom.h" #include "third_party/WebKit/public/platform/WebData.h" #include "third_party/WebKit/public/platform/WebFloatPoint.h" #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" @@ -71,11 +74,6 @@ namespace content { -namespace { - - -} // namespace - static int ToMessageID(WebLocalizedString::Name name) { switch (name) { case WebLocalizedString::AXAMPMFieldText: @@ -786,6 +784,19 @@ return false; } +void BlinkPlatformImpl::bindServiceConnector( + mojo::ScopedMessagePipeHandle remote_handle) { + if (!ChildThreadImpl::current()) + return; + + service_manager::mojom::ConnectorRequest chromium_request; + chromium_request.Bind(std::move(remote_handle)); + ChildThreadImpl::current() + ->GetServiceManagerConnection() + ->GetConnector() + ->BindRequest(std::move(chromium_request)); +} + size_t BlinkPlatformImpl::actualMemoryUsageMB() { return GetMemoryUsageKB() >> 10; }
diff --git a/content/child/blink_platform_impl.h b/content/child/blink_platform_impl.h index 0deda78..a4553fb 100644 --- a/content/child/blink_platform_impl.h +++ b/content/child/blink_platform_impl.h
@@ -76,6 +76,9 @@ size_t actualMemoryUsageMB() override; size_t numberOfProcessors() override; + void bindServiceConnector( + mojo::ScopedMessagePipeHandle remote_handle) override; + size_t maxDecodedImageBytes() override; uint32_t getUniqueIdForProcess() override; blink::WebString userAgent() override;
diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc index 4cfd5f4..3cbc9ff 100644 --- a/content/child/resource_dispatcher.cc +++ b/content/child/resource_dispatcher.cc
@@ -27,7 +27,7 @@ #include "content/child/shared_memory_received_data_factory.h" #include "content/child/site_isolation_stats_gatherer.h" #include "content/child/sync_load_response.h" -#include "content/child/url_response_body_consumer.h" +#include "content/child/url_loader_client_impl.h" #include "content/common/inter_process_time_ticks_converter.h" #include "content/common/navigation_params.h" #include "content/common/resource_messages.h" @@ -39,9 +39,7 @@ #include "content/public/common/content_features.h" #include "content/public/common/resource_response.h" #include "content/public/common/resource_type.h" -#include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/associated_group.h" -#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" #include "net/base/net_errors.h" #include "net/base/request_priority.h" #include "net/http/http_response_headers.h" @@ -76,82 +74,6 @@ return next_request_id++; } -class URLLoaderClientImpl final : public mojom::URLLoaderClient { - public: - URLLoaderClientImpl(int request_id, - ResourceDispatcher* resource_dispatcher, - scoped_refptr<base::SingleThreadTaskRunner> task_runner) - : binding_(this), - request_id_(request_id), - resource_dispatcher_(resource_dispatcher), - task_runner_(std::move(task_runner)) {} - ~URLLoaderClientImpl() override { - if (body_consumer_) - body_consumer_->Cancel(); - } - - void OnReceiveResponse( - const ResourceResponseHead& response_head, - mojom::DownloadedTempFilePtr downloaded_file) override { - has_received_response_ = true; - if (body_consumer_) - body_consumer_->Start(); - downloaded_file_ = std::move(downloaded_file); - resource_dispatcher_->OnMessageReceived( - ResourceMsg_ReceivedResponse(request_id_, response_head)); - } - - void OnReceiveRedirect(const net::RedirectInfo& redirect_info, - const ResourceResponseHead& response_head) override { - DCHECK(!has_received_response_); - DCHECK(!body_consumer_); - resource_dispatcher_->OnMessageReceived(ResourceMsg_ReceivedRedirect( - request_id_, redirect_info, response_head)); - } - - void OnDataDownloaded(int64_t data_len, int64_t encoded_data_len) override { - resource_dispatcher_->OnMessageReceived( - ResourceMsg_DataDownloaded(request_id_, data_len, encoded_data_len)); - } - - void OnTransferSizeUpdated(int32_t transfer_size_diff) override { - resource_dispatcher_->OnTransferSizeUpdated(request_id_, - transfer_size_diff); - } - - void OnStartLoadingResponseBody( - mojo::ScopedDataPipeConsumerHandle body) override { - DCHECK(!body_consumer_); - body_consumer_ = new URLResponseBodyConsumer( - request_id_, resource_dispatcher_, std::move(body), task_runner_); - if (has_received_response_) - body_consumer_->Start(); - } - - void OnComplete(const ResourceRequestCompletionStatus& status) override { - if (!body_consumer_) { - resource_dispatcher_->OnMessageReceived( - ResourceMsg_RequestComplete(request_id_, status)); - return; - } - body_consumer_->OnComplete(status); - } - - void Bind(mojom::URLLoaderClientAssociatedPtrInfo* client_ptr_info, - mojo::AssociatedGroup* associated_group) { - binding_.Bind(client_ptr_info, associated_group); - } - - private: - mojo::AssociatedBinding<mojom::URLLoaderClient> binding_; - scoped_refptr<URLResponseBodyConsumer> body_consumer_; - mojom::DownloadedTempFilePtr downloaded_file_; - const int request_id_; - bool has_received_response_ = false; - ResourceDispatcher* const resource_dispatcher_; - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; -}; - void CheckSchemeForReferrerPolicy(const ResourceRequest& request) { if ((request.referrer_policy == blink::WebReferrerPolicyDefault || request.referrer_policy == @@ -548,7 +470,7 @@ } // Cancel the request if it didn't complete, and clean it up so the bridge // will receive no more messages. - if (info.completion_time.is_null()) + if (info.completion_time.is_null() && !info.url_loader) message_sender_->Send(new ResourceHostMsg_CancelRequest(request_id)); RemovePendingRequest(request_id); }
diff --git a/content/child/resource_dispatcher.h b/content/child/resource_dispatcher.h index 48a7f57..39557b2 100644 --- a/content/child/resource_dispatcher.h +++ b/content/child/resource_dispatcher.h
@@ -50,6 +50,7 @@ class SharedMemoryReceivedDataFactory; struct SiteIsolationResponseMetaData; struct SyncLoadResponse; +class URLLoaderClientImpl; namespace mojom { class URLLoaderFactory; @@ -192,7 +193,7 @@ // For mojo loading. mojom::URLLoaderAssociatedPtr url_loader; - std::unique_ptr<mojom::URLLoaderClient> url_loader_client; + std::unique_ptr<URLLoaderClientImpl> url_loader_client; }; using PendingRequestMap = std::map<int, std::unique_ptr<PendingRequestInfo>>;
diff --git a/content/child/resource_dispatcher_unittest.cc b/content/child/resource_dispatcher_unittest.cc index 3e4bb15..758376de 100644 --- a/content/child/resource_dispatcher_unittest.cc +++ b/content/child/resource_dispatcher_unittest.cc
@@ -21,6 +21,7 @@ #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "content/child/request_extra_data.h" +#include "content/child/test_request_peer.h" #include "content/common/appcache_interfaces.h" #include "content/common/resource_messages.h" #include "content/common/resource_request.h" @@ -51,107 +52,6 @@ static const char kTestRedirectHeaders[] = "HTTP/1.1 302 Found\nLocation:http://www.google.com/\n\n"; -// Listens for request response data and stores it so that it can be compared -// to the reference data. -class TestRequestPeer : public RequestPeer { - public: - struct Context; - TestRequestPeer(ResourceDispatcher* dispatcher, Context* context) - : dispatcher_(dispatcher), context_(context) {} - - void OnUploadProgress(uint64_t position, uint64_t size) override {} - - bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, - const ResourceResponseInfo& info) override { - EXPECT_FALSE(context_->cancelled); - ++context_->seen_redirects; - if (context_->defer_on_redirect) - dispatcher_->SetDefersLoading(context_->request_id, true); - return context_->follow_redirects; - } - - void OnReceivedResponse(const ResourceResponseInfo& info) override { - EXPECT_FALSE(context_->cancelled); - EXPECT_FALSE(context_->received_response); - context_->received_response = true; - if (context_->cancel_on_receive_response) { - dispatcher_->Cancel(context_->request_id); - context_->cancelled = true; - } - } - - void OnDownloadedData(int len, int encoded_data_length) override { - EXPECT_FALSE(context_->cancelled); - context_->total_downloaded_data_length += len; - context_->total_encoded_data_length += encoded_data_length; - } - - void OnReceivedData(std::unique_ptr<ReceivedData> data) override { - if (context_->cancelled) - return; - EXPECT_TRUE(context_->received_response); - EXPECT_FALSE(context_->complete); - context_->data.append(data->payload(), data->length()); - - if (context_->cancel_on_receive_data) { - dispatcher_->Cancel(context_->request_id); - context_->cancelled = true; - } - } - - void OnTransferSizeUpdated(int transfer_size_diff) override { - if (context_->cancelled) - return; - context_->total_encoded_data_length += transfer_size_diff; - } - - void OnCompletedRequest(int error_code, - bool was_ignored_by_handler, - bool stale_copy_in_cache, - const base::TimeTicks& completion_time, - int64_t total_transfer_size, - int64_t encoded_body_size) override { - if (context_->cancelled) - return; - EXPECT_TRUE(context_->received_response); - EXPECT_FALSE(context_->complete); - context_->complete = true; - } - - struct Context { - // True if should follow redirects, false if should cancel them. - bool follow_redirects = true; - // True if the request should be deferred on redirects. - bool defer_on_redirect = false; - - // Number of total redirects seen. - int seen_redirects = 0; - - bool cancel_on_receive_response = false; - bool cancel_on_receive_data = false; - bool received_response = false; - - // Data received. If downloading to file, remains empty. - std::string data; - - // Total encoded data length, regardless of whether downloading to a file or - // not. - int total_encoded_data_length = 0; - // Total length when downloading to a file. - int total_downloaded_data_length = 0; - - bool complete = false; - bool cancelled = false; - int request_id = -1; - }; - - private: - ResourceDispatcher* dispatcher_; - Context* context_; - - DISALLOW_COPY_AND_ASSIGN(TestRequestPeer); -}; - // Sets up the message sender override for the unit test. class ResourceDispatcherTest : public testing::Test, public IPC::Sender { public:
diff --git a/content/child/test_request_peer.cc b/content/child/test_request_peer.cc new file mode 100644 index 0000000..25200d53 --- /dev/null +++ b/content/child/test_request_peer.cc
@@ -0,0 +1,89 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/child/test_request_peer.h" + +#include "content/child/resource_dispatcher.h" +#include "net/url_request/redirect_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +TestRequestPeer::TestRequestPeer(ResourceDispatcher* dispatcher, + Context* context) + : dispatcher_(dispatcher), context_(context) {} + +TestRequestPeer::~TestRequestPeer() = default; + +void TestRequestPeer::OnUploadProgress(uint64_t position, uint64_t size) { + EXPECT_FALSE(context_->complete); +} + +bool TestRequestPeer::OnReceivedRedirect(const net::RedirectInfo& redirect_info, + const ResourceResponseInfo& info) { + EXPECT_FALSE(context_->cancelled); + EXPECT_FALSE(context_->complete); + ++context_->seen_redirects; + if (context_->defer_on_redirect) + dispatcher_->SetDefersLoading(context_->request_id, true); + return context_->follow_redirects; +} + +void TestRequestPeer::OnReceivedResponse(const ResourceResponseInfo& info) { + EXPECT_FALSE(context_->cancelled); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + context_->received_response = true; + if (context_->cancel_on_receive_response) { + dispatcher_->Cancel(context_->request_id); + context_->cancelled = true; + } +} + +void TestRequestPeer::OnDownloadedData(int len, int encoded_data_length) { + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->cancelled); + EXPECT_FALSE(context_->complete); + context_->total_downloaded_data_length += len; + context_->total_encoded_data_length += encoded_data_length; +} + +void TestRequestPeer::OnReceivedData(std::unique_ptr<ReceivedData> data) { + if (context_->cancelled) + return; + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + context_->data.append(data->payload(), data->length()); + + if (context_->cancel_on_receive_data) { + dispatcher_->Cancel(context_->request_id); + context_->cancelled = true; + } +} + +void TestRequestPeer::OnTransferSizeUpdated(int transfer_size_diff) { + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + if (context_->cancelled) + return; + context_->total_encoded_data_length += transfer_size_diff; +} + +void TestRequestPeer::OnCompletedRequest(int error_code, + bool was_ignored_by_handler, + bool stale_copy_in_cache, + const base::TimeTicks& completion_time, + int64_t total_transfer_size, + int64_t encoded_body_size) { + if (context_->cancelled) + return; + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + context_->complete = true; +} + +TestRequestPeer::Context::Context() = default; +TestRequestPeer::Context::~Context() = default; + +} // namespace content
diff --git a/content/child/test_request_peer.h b/content/child/test_request_peer.h new file mode 100644 index 0000000..81162ac --- /dev/null +++ b/content/child/test_request_peer.h
@@ -0,0 +1,85 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_CHILD_TEST_REQUEST_PEER_H_ +#define CONTENT_CHILD_TEST_REQUEST_PEER_H_ + +#include <stdint.h> +#include <memory> +#include <string> +#include "base/time/time.h" +#include "content/public/child/request_peer.h" + +namespace net { +struct RedirectInfo; +} // namespace net + +namespace content { + +class ReceivedData; +class ResourceDispatcher; +struct ResourceResponseInfo; + +// Listens for request response data and stores it so that it can be compared +// to the reference data. +class TestRequestPeer : public RequestPeer { + public: + struct Context; + TestRequestPeer(ResourceDispatcher* dispatcher, Context* context); + ~TestRequestPeer() override; + + void OnUploadProgress(uint64_t position, uint64_t size) override; + bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, + const ResourceResponseInfo& info) override; + void OnReceivedResponse(const ResourceResponseInfo& info) override; + void OnDownloadedData(int len, int encoded_data_length) override; + void OnReceivedData(std::unique_ptr<ReceivedData> data) override; + void OnTransferSizeUpdated(int transfer_size_diff) override; + void OnCompletedRequest(int error_code, + bool was_ignored_by_handler, + bool stale_copy_in_cache, + const base::TimeTicks& completion_time, + int64_t total_transfer_size, + int64_t encoded_body_size) override; + + struct Context final { + Context(); + ~Context(); + + // True if should follow redirects, false if should cancel them. + bool follow_redirects = true; + // True if the request should be deferred on redirects. + bool defer_on_redirect = false; + + // Number of total redirects seen. + int seen_redirects = 0; + + bool cancel_on_receive_response = false; + bool cancel_on_receive_data = false; + bool received_response = false; + + // Data received. If downloading to file, remains empty. + std::string data; + + // Total encoded data length, regardless of whether downloading to a file or + // not. + int total_encoded_data_length = 0; + // Total length when downloading to a file. + int total_downloaded_data_length = 0; + + bool complete = false; + bool cancelled = false; + int request_id = -1; + }; + + private: + ResourceDispatcher* dispatcher_; + Context* context_; + + DISALLOW_COPY_AND_ASSIGN(TestRequestPeer); +}; + +} // namespace content + +#endif // CONTENT_CHILD_TEST_REQUEST_PEER_H_
diff --git a/content/child/url_loader_client_impl.cc b/content/child/url_loader_client_impl.cc new file mode 100644 index 0000000..b9305043 --- /dev/null +++ b/content/child/url_loader_client_impl.cc
@@ -0,0 +1,88 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/child/url_loader_client_impl.h" + +#include "base/callback.h" +#include "base/single_thread_task_runner.h" +#include "content/child/resource_dispatcher.h" +#include "content/child/url_response_body_consumer.h" +#include "content/common/resource_messages.h" +#include "mojo/public/cpp/bindings/associated_group.h" +#include "net/url_request/redirect_info.h" + +namespace content { + +URLLoaderClientImpl::URLLoaderClientImpl( + int request_id, + ResourceDispatcher* resource_dispatcher, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : binding_(this), + request_id_(request_id), + resource_dispatcher_(resource_dispatcher), + task_runner_(std::move(task_runner)) {} + +URLLoaderClientImpl::~URLLoaderClientImpl() { + if (body_consumer_) + body_consumer_->Cancel(); +} + +void URLLoaderClientImpl::Bind( + mojom::URLLoaderClientAssociatedPtrInfo* client_ptr_info, + mojo::AssociatedGroup* associated_group) { + binding_.Bind(client_ptr_info, associated_group); +} + +void URLLoaderClientImpl::OnReceiveResponse( + const ResourceResponseHead& response_head, + mojom::DownloadedTempFilePtr downloaded_file) { + has_received_response_ = true; + if (body_consumer_) + body_consumer_->Start(); + downloaded_file_ = std::move(downloaded_file); + Dispatch(ResourceMsg_ReceivedResponse(request_id_, response_head)); +} + +void URLLoaderClientImpl::OnReceiveRedirect( + const net::RedirectInfo& redirect_info, + const ResourceResponseHead& response_head) { + DCHECK(!has_received_response_); + DCHECK(!body_consumer_); + Dispatch( + ResourceMsg_ReceivedRedirect(request_id_, redirect_info, response_head)); +} + +void URLLoaderClientImpl::OnDataDownloaded(int64_t data_len, + int64_t encoded_data_len) { + Dispatch(ResourceMsg_DataDownloaded(request_id_, data_len, encoded_data_len)); +} + +void URLLoaderClientImpl::OnTransferSizeUpdated(int32_t transfer_size_diff) { + resource_dispatcher_->OnTransferSizeUpdated(request_id_, + transfer_size_diff); +} + +void URLLoaderClientImpl::OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) { + DCHECK(!body_consumer_); + body_consumer_ = new URLResponseBodyConsumer( + request_id_, resource_dispatcher_, std::move(body), task_runner_); + if (has_received_response_) + body_consumer_->Start(); +} + +void URLLoaderClientImpl::OnComplete( + const ResourceRequestCompletionStatus& status) { + if (!body_consumer_) { + Dispatch(ResourceMsg_RequestComplete(request_id_, status)); + return; + } + body_consumer_->OnComplete(status); +} + +void URLLoaderClientImpl::Dispatch(const IPC::Message& message) { + resource_dispatcher_->OnMessageReceived(message); +} + +} // namespace content
diff --git a/content/child/url_loader_client_impl.h b/content/child/url_loader_client_impl.h new file mode 100644 index 0000000..3710c07 --- /dev/null +++ b/content/child/url_loader_client_impl.h
@@ -0,0 +1,70 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_CHILD_URL_LOADER_CLIENT_IMPL_H_ +#define CONTENT_CHILD_URL_LOADER_CLIENT_IMPL_H_ + +#include <stdint.h> +#include "base/callback_forward.h" +#include "base/memory/ref_counted.h" +#include "content/common/content_export.h" +#include "content/common/url_loader.mojom.h" +#include "ipc/ipc_message.h" +#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/system/data_pipe.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace mojo { +class AssociatedGroup; +} // namespace mojo + +namespace net { +struct RedirectInfo; +} // namespace net + +namespace content { +class ResourceDispatcher; +class URLResponseBodyConsumer; +struct ResourceRequestCompletionStatus; +struct ResourceResponseHead; + +class CONTENT_EXPORT URLLoaderClientImpl final : public mojom::URLLoaderClient { + public: + URLLoaderClientImpl(int request_id, + ResourceDispatcher* resource_dispatcher, + scoped_refptr<base::SingleThreadTaskRunner> task_runner); + ~URLLoaderClientImpl() override; + + void Bind(mojom::URLLoaderClientAssociatedPtrInfo* client_ptr_info, + mojo::AssociatedGroup* associated_group); + + // mojom::URLLoaderClient implementation + void OnReceiveResponse(const ResourceResponseHead& response_head, + mojom::DownloadedTempFilePtr downloaded_file) override; + void OnReceiveRedirect(const net::RedirectInfo& redirect_info, + const ResourceResponseHead& response_head) override; + void OnDataDownloaded(int64_t data_len, int64_t encoded_data_len) override; + void OnTransferSizeUpdated(int32_t transfer_size_diff) override; + void OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) override; + void OnComplete(const ResourceRequestCompletionStatus& status) override; + +private: + void Dispatch(const IPC::Message& message); + + mojo::AssociatedBinding<mojom::URLLoaderClient> binding_; + scoped_refptr<URLResponseBodyConsumer> body_consumer_; + mojom::DownloadedTempFilePtr downloaded_file_; + const int request_id_; + bool has_received_response_ = false; + ResourceDispatcher* const resource_dispatcher_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; +}; + +} // namespace content + +#endif // CONTENT_CHILD_URL_LOADER_CLIENT_IMPL_H_
diff --git a/content/child/url_loader_client_impl_unittest.cc b/content/child/url_loader_client_impl_unittest.cc new file mode 100644 index 0000000..cec32a7 --- /dev/null +++ b/content/child/url_loader_client_impl_unittest.cc
@@ -0,0 +1,354 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/child/url_loader_client_impl.h" + +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "content/child/resource_dispatcher.h" +#include "content/child/test_request_peer.h" +#include "content/common/url_loader_factory.mojom.h" +#include "ipc/ipc_sender.h" +#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "net/url_request/redirect_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +class URLLoaderClientImplTest : public ::testing::Test, + IPC::Sender, + mojom::URLLoaderFactory { + protected: + URLLoaderClientImplTest() + : dispatcher_(new ResourceDispatcher(this, message_loop_.task_runner())), + mojo_binding_(this) { + url_loader_factory_proxy_ = mojo_binding_.CreateInterfacePtrAndBind(); + + request_id_ = dispatcher_->StartAsync( + base::MakeUnique<ResourceRequest>(), 0, nullptr, url::Origin(), + base::MakeUnique<TestRequestPeer>(dispatcher_.get(), + &request_peer_context_), + blink::WebURLRequest::LoadingIPCType::Mojo, + url_loader_factory_proxy_.get(), + url_loader_factory_proxy_.associated_group()); + request_peer_context_.request_id = request_id_; + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(url_loader_client_); + } + + void TearDown() override { + url_loader_client_ = nullptr; + url_loader_factory_proxy_ = nullptr; + } + + bool Send(IPC::Message* message) override { + ADD_FAILURE() << "IPC::Sender::Send should not be called."; + return false; + } + + void CreateLoaderAndStart( + mojom::URLLoaderAssociatedRequest request, + int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info) override { + url_loader_client_.Bind(std::move(client_ptr_info)); + } + + void SyncLoad(int32_t routing_id, + int32_t request_id, + const ResourceRequest& request, + const SyncLoadCallback& callback) override { + NOTREACHED(); + } + + static MojoCreateDataPipeOptions DataPipeOptions() { + MojoCreateDataPipeOptions options; + options.struct_size = sizeof(MojoCreateDataPipeOptions); + options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; + options.element_num_bytes = 1; + options.capacity_num_bytes = 4096; + return options; + } + + base::MessageLoop message_loop_; + std::unique_ptr<ResourceDispatcher> dispatcher_; + TestRequestPeer::Context request_peer_context_; + int request_id_ = 0; + mojom::URLLoaderClientAssociatedPtr url_loader_client_; + mojom::URLLoaderFactoryPtr url_loader_factory_proxy_; + mojo::Binding<mojom::URLLoaderFactory> mojo_binding_; +}; + +TEST_F(URLLoaderClientImplTest, OnReceiveResponse) { + ResourceResponseHead response_head; + + url_loader_client_->OnReceiveResponse(response_head, nullptr); + + EXPECT_FALSE(request_peer_context_.received_response); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); +} + +TEST_F(URLLoaderClientImplTest, ResponseBody) { + ResourceResponseHead response_head; + + url_loader_client_->OnReceiveResponse(response_head, nullptr); + + EXPECT_FALSE(request_peer_context_.received_response); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + + mojo::DataPipe data_pipe(DataPipeOptions()); + url_loader_client_->OnStartLoadingResponseBody( + std::move(data_pipe.consumer_handle)); + uint32_t size = 5; + MojoResult result = + mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size, + MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("hello", request_peer_context_.data); +} + +// OnStartLoadingResponseBody can be called before OnReceiveResponse. Because +// of the lack of the ordering guarantee between the message channel and the +// data pipe, bytes can arrive before OnReceiveResponse. URLLoaderClientImpl +// should restore the order. +TEST_F(URLLoaderClientImplTest, ResponseBodyShouldComeAfterResponse) { + ResourceResponseHead response_head; + + mojo::DataPipe data_pipe(DataPipeOptions()); + url_loader_client_->OnStartLoadingResponseBody( + std::move(data_pipe.consumer_handle)); + uint32_t size = 5; + MojoResult result = + mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size, + MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("", request_peer_context_.data); + + url_loader_client_->OnReceiveResponse(response_head, nullptr); + + EXPECT_FALSE(request_peer_context_.received_response); + EXPECT_EQ("", request_peer_context_.data); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_EQ("hello", request_peer_context_.data); +} + +TEST_F(URLLoaderClientImplTest, OnReceiveRedirect) { + ResourceResponseHead response_head; + net::RedirectInfo redirect_info; + + url_loader_client_->OnReceiveRedirect(redirect_info, response_head); + + EXPECT_EQ(0, request_peer_context_.seen_redirects); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, request_peer_context_.seen_redirects); +} + +TEST_F(URLLoaderClientImplTest, OnDataDownloaded) { + ResourceResponseHead response_head; + + url_loader_client_->OnReceiveResponse(response_head, nullptr); + url_loader_client_->OnDataDownloaded(8, 13); + url_loader_client_->OnDataDownloaded(2, 1); + + EXPECT_FALSE(request_peer_context_.received_response); + EXPECT_EQ(0, request_peer_context_.total_downloaded_data_length); + EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_EQ(10, request_peer_context_.total_downloaded_data_length); + EXPECT_EQ(14, request_peer_context_.total_encoded_data_length); +} + +TEST_F(URLLoaderClientImplTest, OnTransferSizeUpdated) { + ResourceResponseHead response_head; + + url_loader_client_->OnReceiveResponse(response_head, nullptr); + url_loader_client_->OnTransferSizeUpdated(4); + url_loader_client_->OnTransferSizeUpdated(4); + + EXPECT_FALSE(request_peer_context_.received_response); + EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_EQ(8, request_peer_context_.total_encoded_data_length); +} + +TEST_F(URLLoaderClientImplTest, OnCompleteWithoutResponseBody) { + ResourceResponseHead response_head; + ResourceRequestCompletionStatus completion_status; + + url_loader_client_->OnReceiveResponse(response_head, nullptr); + url_loader_client_->OnComplete(completion_status); + + EXPECT_FALSE(request_peer_context_.received_response); + EXPECT_FALSE(request_peer_context_.complete); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_TRUE(request_peer_context_.complete); +} + +TEST_F(URLLoaderClientImplTest, OnCompleteWithResponseBody) { + ResourceResponseHead response_head; + ResourceRequestCompletionStatus completion_status; + + url_loader_client_->OnReceiveResponse(response_head, nullptr); + mojo::DataPipe data_pipe(DataPipeOptions()); + url_loader_client_->OnStartLoadingResponseBody( + std::move(data_pipe.consumer_handle)); + uint32_t size = 5; + MojoResult result = + mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size, + MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + data_pipe.producer_handle.reset(); + + EXPECT_FALSE(request_peer_context_.received_response); + EXPECT_EQ("", request_peer_context_.data); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_EQ("hello", request_peer_context_.data); + + url_loader_client_->OnComplete(completion_status); + + EXPECT_FALSE(request_peer_context_.complete); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_EQ("hello", request_peer_context_.data); + EXPECT_TRUE(request_peer_context_.complete); +} + +// Due to the lack of ordering guarantee, it is possible that the response body +// bytes arrives after the completion message. URLLoaderClientImpl should +// restore the order. +TEST_F(URLLoaderClientImplTest, OnCompleteShouldBeTheLastMessage) { + ResourceResponseHead response_head; + ResourceRequestCompletionStatus completion_status; + + url_loader_client_->OnReceiveResponse(response_head, nullptr); + mojo::DataPipe data_pipe(DataPipeOptions()); + url_loader_client_->OnStartLoadingResponseBody( + std::move(data_pipe.consumer_handle)); + url_loader_client_->OnComplete(completion_status); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_FALSE(request_peer_context_.complete); + + uint32_t size = 5; + MojoResult result = + mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size, + MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("hello", request_peer_context_.data); + EXPECT_FALSE(request_peer_context_.complete); + + data_pipe.producer_handle.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("hello", request_peer_context_.data); + EXPECT_TRUE(request_peer_context_.complete); +} + +TEST_F(URLLoaderClientImplTest, CancelOnReceiveResponseWithoutResponseBody) { + request_peer_context_.cancel_on_receive_response = true; + + ResourceResponseHead response_head; + ResourceRequestCompletionStatus completion_status; + + url_loader_client_->OnReceiveResponse(response_head, nullptr); + mojo::DataPipe data_pipe(DataPipeOptions()); + url_loader_client_->OnStartLoadingResponseBody( + std::move(data_pipe.consumer_handle)); + url_loader_client_->OnComplete(completion_status); + + EXPECT_FALSE(request_peer_context_.received_response); + EXPECT_FALSE(request_peer_context_.complete); + EXPECT_FALSE(request_peer_context_.cancelled); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_FALSE(request_peer_context_.complete); + EXPECT_TRUE(request_peer_context_.cancelled); +} + +TEST_F(URLLoaderClientImplTest, CancelOnReceiveResponseWithResponseBody) { + request_peer_context_.cancel_on_receive_response = true; + + ResourceResponseHead response_head; + ResourceRequestCompletionStatus completion_status; + + mojo::DataPipe data_pipe(DataPipeOptions()); + uint32_t size = 5; + MojoResult result = + mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size, + MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + + url_loader_client_->OnStartLoadingResponseBody( + std::move(data_pipe.consumer_handle)); + base::RunLoop().RunUntilIdle(); + url_loader_client_->OnReceiveResponse(response_head, nullptr); + url_loader_client_->OnComplete(completion_status); + + EXPECT_FALSE(request_peer_context_.received_response); + EXPECT_FALSE(request_peer_context_.complete); + EXPECT_FALSE(request_peer_context_.cancelled); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_FALSE(request_peer_context_.complete); + EXPECT_TRUE(request_peer_context_.cancelled); +} + +TEST_F(URLLoaderClientImplTest, CancelOnReceiveData) { + request_peer_context_.cancel_on_receive_data = true; + + ResourceResponseHead response_head; + ResourceRequestCompletionStatus completion_status; + + mojo::DataPipe data_pipe(DataPipeOptions()); + uint32_t size = 5; + MojoResult result = + mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size, + MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + + url_loader_client_->OnStartLoadingResponseBody( + std::move(data_pipe.consumer_handle)); + base::RunLoop().RunUntilIdle(); + url_loader_client_->OnReceiveResponse(response_head, nullptr); + url_loader_client_->OnComplete(completion_status); + + EXPECT_FALSE(request_peer_context_.received_response); + EXPECT_EQ("", request_peer_context_.data); + EXPECT_FALSE(request_peer_context_.complete); + EXPECT_FALSE(request_peer_context_.cancelled); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(request_peer_context_.received_response); + EXPECT_EQ("hello", request_peer_context_.data); + EXPECT_FALSE(request_peer_context_.complete); + EXPECT_TRUE(request_peer_context_.cancelled); +} + +} // namespace content
diff --git a/content/common/leveldb_wrapper.mojom b/content/common/leveldb_wrapper.mojom index 6295be0b..e099618 100644 --- a/content/common/leveldb_wrapper.mojom +++ b/content/common/leveldb_wrapper.mojom
@@ -15,11 +15,6 @@ string source); KeyDeleted(array<uint8> key, array<uint8> old_value, string source); AllDeleted(string source); - - // Since the GetAll call is synchronous, observers need this asynchronously - // delivered notification to avoid applying changes to the returned array - // that it already contains. - GetAllComplete(string source); }; struct KeyValue { @@ -27,6 +22,16 @@ array<uint8> value; }; +// Since the GetAll call is synchronous, LevelDBWrapper users need this +// asynchronously delivered notification to avoid applying changes to the +// returned array that it already contains. This is not sent over the +// normal LevelDBObserver interface as there can be many observers and +// only the connection that made the GetAll call needs to be notified of +// its completion. +interface LevelDBWrapperGetAllCallback { + Complete(bool success); +}; + // A wrapper around leveldb that supports giving notifications when values // change. interface LevelDBWrapper { @@ -48,6 +53,6 @@ // Only used with small databases. Returns all key/value pairs. [Sync] - GetAll(string source) + GetAll(associated LevelDBWrapperGetAllCallback complete_callback) => (leveldb.mojom.DatabaseError status, array<KeyValue> data); };
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java index a6f43af..57b206c 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -94,6 +94,7 @@ waitForEventLogs("selectionchange"); clearEventLogs(); + waitAndVerifyUpdateSelection(0, 0, 0, -1, -1); resetAllStates(); } @@ -776,33 +777,6 @@ assertTextsAroundCursor("blablargblarg", null, ""); } - // crbug.com/606059 - @MediumTest - @Feature({"TextInput"}) - @RetryOnFailure - public void testPasteLongText() throws Exception { - final int textLength = 25000; - final String text = new String(new char[textLength]).replace("\0", "a"); - setClip(text); - assertClipboardContents(getActivity(), text); - - focusElement("textarea"); - waitAndVerifyUpdateSelection(0, 0, 0, -1, -1); - - // In order to reproduce the bug, we need some text after the pasting text. - commitText("hello", 1); - waitAndVerifyUpdateSelection(1, 5, 5, -1, -1); - - setSelection(0, 0); - waitAndVerifyUpdateSelection(2, 0, 0, -1, -1); - - // It will crash after the 3rd paste if ImeThread is not enabled. - for (int i = 0; i < 10; i++) { - paste(); - waitAndVerifyUpdateSelection(3 + i, textLength * (i + 1), textLength * (i + 1), -1, -1); - } - } - @SmallTest @Feature({"TextInput"}) @DisabledTest(message = "crbug.com/673588")
diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json index eacc854..da87521 100644 --- a/content/public/app/mojo/content_renderer_manifest.json +++ b/content/public/app/mojo/content_renderer_manifest.json
@@ -19,6 +19,7 @@ }, "requires": { "content_browser": [ "renderer" ], + "device": [ "device:time_zone_monitor" ], "ui": [ "discardable_memory", "gpu_client"
diff --git a/content/public/common/browser_side_navigation_policy.cc b/content/public/common/browser_side_navigation_policy.cc index a94eff1..698ce3f 100644 --- a/content/public/common/browser_side_navigation_policy.cc +++ b/content/public/common/browser_side_navigation_policy.cc
@@ -5,13 +5,16 @@ #include "content/public/common/browser_side_navigation_policy.h" #include "base/command_line.h" +#include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" namespace content { bool IsBrowserSideNavigationEnabled() { - return base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation); + return + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBrowserSideNavigation) || + base::FeatureList::IsEnabled(features::kBrowserSideNavigation); } } // namespace content
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index 9e01f6f..e3b3a25 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -19,6 +19,10 @@ const base::Feature kBrotliEncoding{"brotli-encoding", base::FEATURE_ENABLED_BY_DEFAULT}; +// Enables browser side navigation (aka PlzNavigate). http://crbug.com/368813 +const base::Feature kBrowserSideNavigation{"browser-side-navigation", + base::FEATURE_DISABLED_BY_DEFAULT}; + // If Canvas2D Image Chromium is allowed, this feature controls whether it is // enabled. const base::Feature kCanvas2DImageChromium{"Canvas2DImageChromium",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h index 0ea5008..0aed60d 100644 --- a/content/public/common/content_features.h +++ b/content/public/common/content_features.h
@@ -18,6 +18,7 @@ // alongside the definition of their values in the .cc file. CONTENT_EXPORT extern const base::Feature kAsmJsToWebAssembly; CONTENT_EXPORT extern const base::Feature kBrotliEncoding; +CONTENT_EXPORT extern const base::Feature kBrowserSideNavigation; CONTENT_EXPORT extern const base::Feature kCanvas2DImageChromium; CONTENT_EXPORT extern const base::Feature kCompositeOpaqueFixedPosition; CONTENT_EXPORT extern const base::Feature kCompositeOpaqueScrollers;
diff --git a/content/renderer/dom_storage/local_storage_cached_area.cc b/content/renderer/dom_storage/local_storage_cached_area.cc index e2e4f3b5..b09dcba 100644 --- a/content/renderer/dom_storage/local_storage_cached_area.cc +++ b/content/renderer/dom_storage/local_storage_cached_area.cc
@@ -15,9 +15,12 @@ #include "content/common/storage_partition_service.mojom.h" #include "content/renderer/dom_storage/local_storage_area.h" #include "content/renderer/dom_storage/local_storage_cached_areas.h" +#include "mojo/public/cpp/bindings/strong_associated_binding.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/web/WebStorageEventDispatcher.h" +namespace content { + namespace { base::string16 Uint8VectorToString16(const std::vector<uint8_t>& input) { @@ -30,9 +33,29 @@ return std::vector<uint8_t>(data, data + input.size() * sizeof(base::char16)); } -} // namespace +class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback { + public: + static mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo CreateAndBind( + mojo::AssociatedGroup* associated_group, + const base::Callback<void(bool)>& callback) { + mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo ptr_info; + mojom::LevelDBWrapperGetAllCallbackAssociatedRequest request; + associated_group->CreateAssociatedInterface( + mojo::AssociatedGroup::WILL_PASS_PTR, &ptr_info, &request); + mojo::MakeStrongAssociatedBinding( + base::WrapUnique(new GetAllCallback(callback)), std::move(request)); + return ptr_info; + } -namespace content { + private: + explicit GetAllCallback(const base::Callback<void(bool)>& callback) + : m_callback(callback) {} + void Complete(bool success) override { m_callback.Run(success); } + + base::Callback<void(bool)> m_callback; +}; + +} // namespace // These methods are used to pack and unpack the page_url/storage_area_id into // source strings to/from the browser. @@ -218,18 +241,6 @@ base::NullableString16(), origin_.GetURL(), page_url, originating_area); } -void LocalStorageCachedArea::GetAllComplete(const std::string& source) { - // Since the GetAll method is synchronous, we need this asynchronously - // delivered notification to avoid applying changes to the returned array - // that we already have. - if (source == get_all_request_id_) { - DCHECK(ignore_all_mutations_); - DCHECK(!get_all_request_id_.empty()); - ignore_all_mutations_ = false; - get_all_request_id_.clear(); - } -} - void LocalStorageCachedArea::KeyAddedOrChanged( const std::vector<uint8_t>& key, const std::vector<uint8_t>& new_value, @@ -272,10 +283,13 @@ base::TimeTicks before = base::TimeTicks::Now(); ignore_all_mutations_ = true; - get_all_request_id_ = base::Uint64ToString(base::RandUint64()); leveldb::mojom::DatabaseError status = leveldb::mojom::DatabaseError::OK; std::vector<content::mojom::KeyValuePtr> data; - leveldb_->GetAll(get_all_request_id_, &status, &data); + leveldb_->GetAll(GetAllCallback::CreateAndBind( + leveldb_.associated_group(), + base::Bind(&LocalStorageCachedArea::OnGetAllComplete, + weak_factory_.GetWeakPtr())), + &status, &data); DOMStorageValuesMap values; for (size_t i = 0; i < data.size(); ++i) { @@ -336,6 +350,15 @@ ignore_all_mutations_ = false; } +void LocalStorageCachedArea::OnGetAllComplete(bool success) { + // Since the GetAll method is synchronous, we need this asynchronously + // delivered notification to avoid applying changes to the returned array + // that we already have. + DCHECK(success); + DCHECK(ignore_all_mutations_); + ignore_all_mutations_ = false; +} + void LocalStorageCachedArea::Reset() { map_ = NULL; ignore_key_mutations_.clear();
diff --git a/content/renderer/dom_storage/local_storage_cached_area.h b/content/renderer/dom_storage/local_storage_cached_area.h index 11915a9b..e2902af 100644 --- a/content/renderer/dom_storage/local_storage_cached_area.h +++ b/content/renderer/dom_storage/local_storage_cached_area.h
@@ -76,7 +76,6 @@ const std::vector<uint8_t>& old_value, const std::string& source) override; void AllDeleted(const std::string& source) override; - void GetAllComplete(const std::string& source) override; // Common helper for KeyAdded() and KeyChanged() void KeyAddedOrChanged(const std::vector<uint8_t>& key, @@ -91,6 +90,7 @@ void OnSetItemComplete(const base::string16& key, bool success); void OnRemoveItemComplete(const base::string16& key, bool success); void OnClearComplete(bool success); + void OnGetAllComplete(bool success); // Resets the object back to its newly constructed state. void Reset(); @@ -99,7 +99,6 @@ scoped_refptr<DOMStorageMap> map_; std::map<base::string16, int> ignore_key_mutations_; bool ignore_all_mutations_ = false; - std::string get_all_request_id_; mojom::LevelDBWrapperPtr leveldb_; mojo::AssociatedBinding<mojom::LevelDBObserver> binding_; LocalStorageCachedAreas* cached_areas_;
diff --git a/content/renderer/gpu/compositor_forwarding_message_filter_unittest.cc b/content/renderer/gpu/compositor_forwarding_message_filter_unittest.cc index 97f67a36..d990a60 100644 --- a/content/renderer/gpu/compositor_forwarding_message_filter_unittest.cc +++ b/content/renderer/gpu/compositor_forwarding_message_filter_unittest.cc
@@ -45,7 +45,7 @@ int route_id = 0; ViewMsg_BeginFrame msg( - route_id, cc::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE)); + route_id, cc::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1)); CompositorForwardingMessageFilter::Handler plus_handler = base::Bind(&CompositorForwardingMessageFilterTestHandler::OnPlusMethod,
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index cf00e8f..2916c78 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -602,18 +602,7 @@ const base::TimeTicks& browser_navigation_start, const base::TimeTicks& renderer_navigation_start) { DCHECK(!browser_navigation_start.is_null()); - base::TimeTicks navigation_start = - std::min(browser_navigation_start, renderer_navigation_start); - base::TimeDelta difference = - renderer_navigation_start - browser_navigation_start; - if (difference > base::TimeDelta()) { - UMA_HISTOGRAM_TIMES("Navigation.Start.RendererBrowserDifference.Positive", - difference); - } else { - UMA_HISTOGRAM_TIMES("Navigation.Start.RendererBrowserDifference.Negative", - -difference); - } - return navigation_start; + return std::min(browser_navigation_start, renderer_navigation_start); } // PlzNavigate
diff --git a/content/renderer/service_worker/embedded_worker_dispatcher.cc b/content/renderer/service_worker/embedded_worker_dispatcher.cc index bf046f2..57e0085e10 100644 --- a/content/renderer/service_worker/embedded_worker_dispatcher.cc +++ b/content/renderer/service_worker/embedded_worker_dispatcher.cc
@@ -58,9 +58,9 @@ void EmbeddedWorkerDispatcher::WorkerContextDestroyed( int embedded_worker_id) { - UnregisterWorker(embedded_worker_id); RenderThreadImpl::current()->thread_safe_sender()->Send( new EmbeddedWorkerHostMsg_WorkerStopped(embedded_worker_id)); + UnregisterWorker(embedded_worker_id); } void EmbeddedWorkerDispatcher::OnStartWorker(
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc index 38014c8..aa699bc 100644 --- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc +++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -28,12 +28,18 @@ void EmbeddedWorkerInstanceClientImpl::StopWorkerCompleted() { DCHECK(embedded_worker_id_); DCHECK(stop_callback_); - dispatcher_->UnregisterWorker(embedded_worker_id_.value()); - embedded_worker_id_.reset(); - stop_callback_.Run(); TRACE_EVENT0("ServiceWorker", "EmbeddedWorkerInstanceClientImpl::StopWorkerCompleted"); + // TODO(falken): The signals to the browser should be in the order: + // (1) WorkerStopped (via stop_callback_) + // (2) ProviderDestroyed (via UnregisterWorker destroying + // WebEmbeddedWorkerImpl) + // But this ordering is currently not guaranteed since the Mojo pipes are + // different. https://crbug.com/676526 + stop_callback_.Run(); stop_callback_.Reset(); + dispatcher_->UnregisterWorker(embedded_worker_id_.value()); + embedded_worker_id_.reset(); wrapper_ = nullptr; }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index d2e8d356..bf3ff46e 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -151,6 +151,8 @@ "mock_keyboard.h", "mock_keyboard_driver_win.cc", "mock_keyboard_driver_win.h", + "mock_leveldb_database.cc", + "mock_leveldb_database.h", "mock_permission_manager.cc", "mock_permission_manager.h", "mock_render_process.cc", @@ -222,6 +224,7 @@ "//cc/ipc", "//cc/surfaces", "//components/display_compositor", + "//components/leveldb/public/interfaces", "//content/app:both_for_content_tests", "//content/browser:for_content_tests", "//content/browser/speech/proto", @@ -994,6 +997,7 @@ "../browser/byte_stream_unittest.cc", "../browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc", "../browser/cache_storage/cache_storage_cache_unittest.cc", + "../browser/cache_storage/cache_storage_index_unittest.cc", "../browser/cache_storage/cache_storage_manager_unittest.cc", "../browser/cache_storage/cache_storage_operation_unittest.cc", "../browser/cache_storage/cache_storage_scheduler_unittest.cc", @@ -1016,6 +1020,7 @@ "../browser/dom_storage/dom_storage_area_unittest.cc", "../browser/dom_storage/dom_storage_context_impl_unittest.cc", "../browser/dom_storage/dom_storage_database_unittest.cc", + "../browser/dom_storage/local_storage_context_mojo_unittest.cc", "../browser/dom_storage/session_storage_database_unittest.cc", "../browser/download/base_file_unittest.cc", "../browser/download/base_file_win_unittest.cc", @@ -1276,6 +1281,9 @@ "../child/shared_memory_data_consumer_handle_unittest.cc", "../child/shared_memory_received_data_factory_unittest.cc", "../child/site_isolation_stats_gatherer_unittest.cc", + "../child/test_request_peer.cc", + "../child/test_request_peer.h", + "../child/url_loader_client_impl_unittest.cc", "../child/url_response_body_consumer_unittest.cc", "../child/v8_value_converter_impl_unittest.cc", "../child/web_data_consumer_handle_impl_unittest.cc", @@ -1388,7 +1396,6 @@ "//cc/surfaces", "//components/display_compositor", "//components/leveldb/public/cpp", - "//components/leveldb/public/interfaces", "//components/payments:payment_app", "//components/rappor:test_support", "//content:resources",
diff --git a/content/test/DEPS b/content/test/DEPS index 3591ebda..34a4d4c 100644 --- a/content/test/DEPS +++ b/content/test/DEPS
@@ -1,6 +1,7 @@ include_rules = [ # Allow inclusion of specific components that we depend on. # See comment in content/DEPS for which components are allowed. + "+components/leveldb", "+components/scheduler/renderer", "+components/scheduler/test",
diff --git a/content/test/data/dom_storage/sanity_check.js b/content/test/data/dom_storage/sanity_check.js index c41e493..ae547bb 100644 --- a/content/test/data/dom_storage/sanity_check.js +++ b/content/test/data/dom_storage/sanity_check.js
@@ -12,7 +12,7 @@ sanityCheck(window.localStorage); debug('Checking window.sessionStorage'); sanityCheck(window.sessionStorage); - done(); + window.setTimeout(done, 0); } catch(e) { fail(e); }
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py index 4f1b59a..757260a 100644 --- a/content/test/gpu/gpu_tests/pixel_expectations.py +++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -24,10 +24,6 @@ self.Fail('Pixel_ScissorTestWithPreserveDrawingBuffer', ['android'], bug=521588) - # TODO(xlai): Check / generate reference images. - self.Fail('Pixel_OffscreenCanvas2DResizeOnWorker', bug=662498) - self.Fail('Pixel_OffscreenCanvasWebglResizeOnWorker', bug=662498) - # TODO(ccameron) fix these on Mac Retina self.Fail('Pixel_CSS3DBlueBox', ['mac'], bug=533690)
diff --git a/content/test/mock_leveldb_database.cc b/content/test/mock_leveldb_database.cc new file mode 100644 index 0000000..60a5db7 --- /dev/null +++ b/content/test/mock_leveldb_database.cc
@@ -0,0 +1,177 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/test/mock_leveldb_database.h" + +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" + +namespace content { + +namespace { + +leveldb::mojom::KeyValuePtr CreateKeyValue(std::vector<uint8_t> key, + std::vector<uint8_t> value) { + leveldb::mojom::KeyValuePtr result = leveldb::mojom::KeyValue::New(); + result->key = std::move(key); + result->value = std::move(value); + return result; +} + +base::StringPiece AsStringPiece(const std::vector<uint8_t>& data) { + return base::StringPiece(reinterpret_cast<const char*>(data.data()), + data.size()); +} + +bool StartsWith(const std::vector<uint8_t>& key, + const std::vector<uint8_t>& prefix) { + return base::StartsWith(AsStringPiece(key), AsStringPiece(prefix), + base::CompareCase::SENSITIVE); +} + +std::vector<uint8_t> successor(std::vector<uint8_t> data) { + for (unsigned i = data.size(); i > 0; --i) { + if (data[i - 1] < 255) { + data[i - 1]++; + return data; + } + } + NOTREACHED(); + return data; +} + +} // namespace + +MockLevelDBDatabase::MockLevelDBDatabase( + std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data) + : mock_data_(*mock_data) {} + +void MockLevelDBDatabase::Put(const std::vector<uint8_t>& key, + const std::vector<uint8_t>& value, + const PutCallback& callback) { + mock_data_[key] = value; + callback.Run(leveldb::mojom::DatabaseError::OK); +} + +void MockLevelDBDatabase::Delete(const std::vector<uint8_t>& key, + const DeleteCallback& callback) { + mock_data_.erase(key); + callback.Run(leveldb::mojom::DatabaseError::OK); +} + +void MockLevelDBDatabase::DeletePrefixed( + const std::vector<uint8_t>& key_prefix, + const DeletePrefixedCallback& callback) { + mock_data_.erase(mock_data_.lower_bound(key_prefix), + mock_data_.lower_bound(successor(key_prefix))); + callback.Run(leveldb::mojom::DatabaseError::OK); +} + +void MockLevelDBDatabase::Write( + std::vector<leveldb::mojom::BatchedOperationPtr> operations, + const WriteCallback& callback) { + for (const auto& op : operations) { + switch (op->type) { + case leveldb::mojom::BatchOperationType::PUT_KEY: + mock_data_[op->key] = *op->value; + break; + case leveldb::mojom::BatchOperationType::DELETE_KEY: + mock_data_.erase(op->key); + break; + case leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY: + mock_data_.erase(mock_data_.lower_bound(op->key), + mock_data_.lower_bound(successor(op->key))); + break; + } + } + callback.Run(leveldb::mojom::DatabaseError::OK); +} + +void MockLevelDBDatabase::Get(const std::vector<uint8_t>& key, + const GetCallback& callback) { + if (mock_data_.find(key) != mock_data_.end()) { + callback.Run(leveldb::mojom::DatabaseError::OK, mock_data_[key]); + } else { + callback.Run(leveldb::mojom::DatabaseError::NOT_FOUND, + std::vector<uint8_t>()); + } +} + +void MockLevelDBDatabase::GetPrefixed( + const std::vector<uint8_t>& key_prefix, + const GetPrefixedCallback& callback) { + std::vector<leveldb::mojom::KeyValuePtr> data; + for (const auto& row : mock_data_) { + if (StartsWith(row.first, key_prefix)) { + data.push_back(CreateKeyValue(row.first, row.second)); + } + } + callback.Run(leveldb::mojom::DatabaseError::OK, std::move(data)); +} + +void MockLevelDBDatabase::GetSnapshot( + const GetSnapshotCallback& callback) { + NOTREACHED(); +} + +void MockLevelDBDatabase::ReleaseSnapshot( + const base::UnguessableToken& snapshot) { + NOTREACHED(); +} + +void MockLevelDBDatabase::GetFromSnapshot( + const base::UnguessableToken& snapshot, + const std::vector<uint8_t>& key, + const GetCallback& callback) { + NOTREACHED(); +} + +void MockLevelDBDatabase::NewIterator( + const NewIteratorCallback& callback) { + NOTREACHED(); +} + +void MockLevelDBDatabase::NewIteratorFromSnapshot( + const base::UnguessableToken& snapshot, + const NewIteratorFromSnapshotCallback& callback) { + NOTREACHED(); +} + +void MockLevelDBDatabase::ReleaseIterator( + const base::UnguessableToken& iterator) { + NOTREACHED(); +} + +void MockLevelDBDatabase::IteratorSeekToFirst( + const base::UnguessableToken& iterator, + const IteratorSeekToFirstCallback& callback) { + NOTREACHED(); +} + +void MockLevelDBDatabase::IteratorSeekToLast( + const base::UnguessableToken& iterator, + const IteratorSeekToLastCallback& callback) { + NOTREACHED(); +} + +void MockLevelDBDatabase::IteratorSeek( + const base::UnguessableToken& iterator, + const std::vector<uint8_t>& target, + const IteratorSeekToLastCallback& callback) { + NOTREACHED(); +} + +void MockLevelDBDatabase::IteratorNext( + const base::UnguessableToken& iterator, + const IteratorNextCallback& callback) { + NOTREACHED(); +} + +void MockLevelDBDatabase::IteratorPrev( + const base::UnguessableToken& iterator, + const IteratorPrevCallback& callback) { + NOTREACHED(); +} + +} // namespace content
diff --git a/content/test/mock_leveldb_database.h b/content/test/mock_leveldb_database.h new file mode 100644 index 0000000..29a2abd --- /dev/null +++ b/content/test/mock_leveldb_database.h
@@ -0,0 +1,60 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/leveldb/public/interfaces/leveldb.mojom.h" + +namespace content { + +// Simple implementation of the leveldb::mojom::LevelDBDatabase interface that +// is backed by a std::map. + +class MockLevelDBDatabase : public leveldb::mojom::LevelDBDatabase { + public: + // |mock_data| must not be null and must outlive this MockLevelDBDatabase + // instance. + explicit MockLevelDBDatabase( + std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data); + + // LevelDBDatabase: + void Put(const std::vector<uint8_t>& key, + const std::vector<uint8_t>& value, + const PutCallback& callback) override; + void Delete(const std::vector<uint8_t>& key, + const DeleteCallback& callback) override; + void DeletePrefixed(const std::vector<uint8_t>& key_prefix, + const DeletePrefixedCallback& callback) override; + void Write(std::vector<leveldb::mojom::BatchedOperationPtr> operations, + const WriteCallback& callback) override; + void Get(const std::vector<uint8_t>& key, + const GetCallback& callback) override; + void GetPrefixed(const std::vector<uint8_t>& key_prefix, + const GetPrefixedCallback& callback) override; + void GetSnapshot(const GetSnapshotCallback& callback) override; + void ReleaseSnapshot(const base::UnguessableToken& snapshot) override; + void GetFromSnapshot(const base::UnguessableToken& snapshot, + const std::vector<uint8_t>& key, + const GetCallback& callback) override; + void NewIterator(const NewIteratorCallback& callback) override; + void NewIteratorFromSnapshot( + const base::UnguessableToken& snapshot, + const NewIteratorFromSnapshotCallback& callback) override; + void ReleaseIterator(const base::UnguessableToken& iterator) override; + void IteratorSeekToFirst( + const base::UnguessableToken& iterator, + const IteratorSeekToFirstCallback& callback) override; + void IteratorSeekToLast(const base::UnguessableToken& iterator, + const IteratorSeekToLastCallback& callback) override; + void IteratorSeek(const base::UnguessableToken& iterator, + const std::vector<uint8_t>& target, + const IteratorSeekToLastCallback& callback) override; + void IteratorNext(const base::UnguessableToken& iterator, + const IteratorNextCallback& callback) override; + void IteratorPrev(const base::UnguessableToken& iterator, + const IteratorPrevCallback& callback) override; + + private: + std::map<std::vector<uint8_t>, std::vector<uint8_t>>& mock_data_; +}; + +} // namespace content
diff --git a/extensions/renderer/resources/media_router_bindings.js b/extensions/renderer/resources/media_router_bindings.js index 40ebfab8..0373e036 100644 --- a/extensions/renderer/resources/media_router_bindings.js +++ b/extensions/renderer/resources/media_router_bindings.js
@@ -5,22 +5,16 @@ var mediaRouter; define('media_router_bindings', [ - 'mojo/public/js/bindings', - 'mojo/public/js/core', 'content/public/renderer/frame_interfaces', 'chrome/browser/media/router/mojo/media_router.mojom', 'extensions/common/mojo/keep_alive.mojom', 'mojo/common/time.mojom', - 'mojo/public/js/connection', - 'mojo/public/js/router', -], function(bindings, - core, - frameInterfaces, + 'mojo/public/js/bindings', +], function(frameInterfaces, mediaRouterMojom, keepAliveMojom, timeMojom, - connector, - routerModule) { + bindings) { 'use strict'; /** @@ -187,14 +181,14 @@ /** * Creates a new MediaRouter. * Converts a route struct to its Mojo form. - * @param {!MediaRouterService} service + * @param {!mediaRouterMojom.MediaRouterPtr} service * @constructor */ function MediaRouter(service) { /** * The Mojo service proxy. Allows extension code to call methods that reside * in the browser. - * @type {!MediaRouterService} + * @type {!mediaRouterMojom.MediaRouterPtr} */ this.service_ = service; @@ -287,10 +281,10 @@ */ MediaRouter.prototype.setKeepAlive = function(keepAlive) { if (keepAlive === false && this.keepAlive_) { - this.keepAlive_.close(); + this.keepAlive_.ptr.reset(); this.keepAlive_ = null; } else if (keepAlive === true && !this.keepAlive_) { - this.keepAlive_ = new routerModule.Router( + this.keepAlive_ = new keepAliveMojom.KeepAlivePtr( frameInterfaces.getInterface(keepAliveMojom.KeepAlive.name)); } }; @@ -498,8 +492,6 @@ * @constructor */ function MediaRouteProvider(mediaRouter) { - mediaRouterMojom.MediaRouteProvider.stubClass.call(this); - /** * Object containing JS callbacks into Provider Manager code. * @type {!MediaRouterHandlers} @@ -512,8 +504,6 @@ */ this.mediaRouter_ = mediaRouter; } - MediaRouteProvider.prototype = Object.create( - mediaRouterMojom.MediaRouteProvider.stubClass.prototype); /* * Sets the callback handler used to invoke methods in the provider manager. @@ -813,9 +803,8 @@ }); }; - mediaRouter = new MediaRouter(connector.bindHandleToProxy( - frameInterfaces.getInterface(mediaRouterMojom.MediaRouter.name), - mediaRouterMojom.MediaRouter)); + mediaRouter = new MediaRouter(new mediaRouterMojom.MediaRouterPtr( + frameInterfaces.getInterface(mediaRouterMojom.MediaRouter.name))); return mediaRouter; });
diff --git a/extensions/renderer/resources/mime_handler_private_custom_bindings.js b/extensions/renderer/resources/mime_handler_private_custom_bindings.js index 4a515394..1fa03663 100644 --- a/extensions/renderer/resources/mime_handler_private_custom_bindings.js +++ b/extensions/renderer/resources/mime_handler_private_custom_bindings.js
@@ -15,13 +15,11 @@ var servicePromise = Promise.all([ requireAsync('content/public/renderer/frame_interfaces'), requireAsync('extensions/common/api/mime_handler.mojom'), - requireAsync('mojo/public/js/router'), ]).then(function(modules) { var frameInterfaces = modules[0]; var mojom = modules[1]; - var routerModule = modules[2]; - return new mojom.MimeHandlerService.proxyClass(new routerModule.Router( - frameInterfaces.getInterface(mojom.MimeHandlerService.name))); + return new mojom.MimeHandlerServicePtr( + frameInterfaces.getInterface(mojom.MimeHandlerService.name)); }); // Stores a promise to the GetStreamInfo() result to avoid making additional
diff --git a/extensions/test/data/api_test_base_unittest.js b/extensions/test/data/api_test_base_unittest.js index 6aa8b78..094a623 100644 --- a/extensions/test/data/api_test_base_unittest.js +++ b/extensions/test/data/api_test_base_unittest.js
@@ -36,14 +36,14 @@ }, function testMojoModulesAreAvailable() { Promise.all([ - requireAsync('mojo/public/js/connection'), + requireAsync('mojo/public/js/bindings'), requireAsync('mojo/public/js/core'), requireAsync('content/public/renderer/frame_interfaces'), ]).then(test.callback(function(modules) { - var connection = modules[0]; + var bindings = modules[0]; var core = modules[1]; var frameInterfaces = modules[2]; - test.assertTrue(!!connection.Connection); + test.assertTrue(!!bindings.Binding); test.assertTrue(!!core.createMessagePipe); test.assertTrue(!!frameInterfaces.getInterface); }));
diff --git a/gpu/config/gpu_control_list_jsons.h b/gpu/config/gpu_control_list_jsons.h index 671aa9e..71d574c 100644 --- a/gpu/config/gpu_control_list_jsons.h +++ b/gpu/config/gpu_control_list_jsons.h
@@ -10,7 +10,6 @@ namespace gpu { GPU_EXPORT extern const char kGpuDriverBugListJson[]; -GPU_EXPORT extern const char kGpuSwitchingListJson[]; GPU_EXPORT extern const char kSoftwareRenderingListJson[]; } // namespace gpu
diff --git a/headless/lib/embedder_mojo_browsertest.cc b/headless/lib/embedder_mojo_browsertest.cc index 3fb4ed1..0ae117b33 100644 --- a/headless/lib/embedder_mojo_browsertest.cc +++ b/headless/lib/embedder_mojo_browsertest.cc
@@ -152,16 +152,12 @@ "// fires after the requested modules have been loaded. \n" "define([ \n" " 'headless/lib/embedder_test.mojom', \n" - " 'mojo/public/js/core', \n" - " 'mojo/public/js/router', \n" " 'content/public/renderer/frame_interfaces', \n" - " ], function(embedderMojom, mojoCore, routerModule, \n" - " frameInterfaces) { \n" + " ], function(embedderMojom, frameInterfaces) { \n" " var testEmbedderService = \n" - " new embedderMojom.TestEmbedderService.proxyClass( \n" - " new routerModule.Router( \n" - " frameInterfaces.getInterface( \n" - " embedderMojom.TestEmbedderService.name))); \n" + " new embedderMojom.TestEmbedderServicePtr( \n" + " frameInterfaces.getInterface( \n" + " embedderMojom.TestEmbedderService.name)); \n" " \n" " // Send a message to the embedder! \n" " testEmbedderService.returnTestResult('hello world'); \n"
diff --git a/headless/test/data/page_one.html b/headless/test/data/page_one.html index 469b95ac..a29176e 100644 --- a/headless/test/data/page_one.html +++ b/headless/test/data/page_one.html
@@ -6,16 +6,10 @@ // fires after the requested modules have been loaded. define([ 'headless/lib/embedder_test.mojom', - 'mojo/public/js/core', - 'mojo/public/js/router', 'content/public/renderer/frame_interfaces', - ], function(embedderMojom, mojoCore, routerModule, - frameInterfaces) { - var testEmbedderService = - new embedderMojom.TestEmbedderService.proxyClass( - new routerModule.Router( - frameInterfaces.getInterface( - embedderMojom.TestEmbedderService.name))); + ], function(embedderMojom, frameInterfaces) { + var testEmbedderService = new embedderMojom.TestEmbedderServicePtr( + frameInterfaces.getInterface(embedderMojom.TestEmbedderService.name)); // Send a message to the embedder! testEmbedderService.returnTestResult('page one');
diff --git a/headless/test/data/page_two.html b/headless/test/data/page_two.html index 7cf22d4e..47edec7 100644 --- a/headless/test/data/page_two.html +++ b/headless/test/data/page_two.html
@@ -6,16 +6,10 @@ // fires after the requested modules have been loaded. define([ 'headless/lib/embedder_test.mojom', - 'mojo/public/js/core', - 'mojo/public/js/router', 'content/public/renderer/frame_interfaces', - ], function(embedderMojom, mojoCore, routerModule, - frameInterfaces) { - var testEmbedderService = - new embedderMojom.TestEmbedderService.proxyClass( - new routerModule.Router( - frameInterfaces.getInterface( - embedderMojom.TestEmbedderService.name))); + ], function(embedderMojom, frameInterfaces) { + var testEmbedderService = new embedderMojom.TestEmbedderServicePtr( + frameInterfaces.getInterface(embedderMojom.TestEmbedderService.name)); // Send a message to the embedder! testEmbedderService.returnTestResult('page two');
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index 5ec36c1..5a494ce 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg
@@ -54,7 +54,7 @@ builders { name: "linux_chromium_compile_dbg_ng" } builders { name: "linux_chromium_headless_rel" - experiment_percentage: 10 + experiment_percentage: 100 } builders { name: "linux_chromium_rel_ng" } }
diff --git a/ios/OWNERS b/ios/OWNERS index d1bdac3..0fbae9fe 100644 --- a/ios/OWNERS +++ b/ios/OWNERS
@@ -1,5 +1,5 @@ -droger@chromium.org eugenebut@chromium.org +marq@chromium.org noyau@chromium.org rohitrao@chromium.org sdefresne@chromium.org @@ -7,4 +7,4 @@ # per-file rules: # These are for the common case of adding or renaming files. If you're doing # structural changes, please get a review from a reviewer in this file. -per-file *.gyp=* +per-file BUILD.gn=*
diff --git a/ios/chrome/browser/autofill/autofill_agent.mm b/ios/chrome/browser/autofill/autofill_agent.mm index 1b95bf5a..b716c4a 100644 --- a/ios/chrome/browser/autofill/autofill_agent.mm +++ b/ios/chrome/browser/autofill/autofill_agent.mm
@@ -693,7 +693,6 @@ fieldName:(const std::string&)fieldName type:(const std::string&)type value:(const std::string&)value - keyCode:(int)keyCode inputMissing:(BOOL)inputMissing { if (!browserState_->GetPrefs()->GetBoolean(autofill::prefs::kAutofillEnabled)) return;
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm index 833c7581..cec559d 100644 --- a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm +++ b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
@@ -484,7 +484,6 @@ fieldName:(const std::string&)fieldName type:(const std::string&)type value:(const std::string&)value - keyCode:(int)keyCode inputMissing:(BOOL)inputMissing { web::URLVerificationTrustLevel trustLevel; const GURL pageURL(webState->GetCurrentURL(&trustLevel));
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm index 68250b9e..c44ad27 100644 --- a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm +++ b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
@@ -285,8 +285,6 @@ fieldName:"field" type:"type" value:"value" - keyCode:web::WebStateObserver:: - kInvalidFormKeyCode inputMissing:false]; EXPECT_TRUE(GetSuggestionView(input_accessory_view_)); @@ -303,8 +301,6 @@ fieldName:"field" type:"blur" // blur! value:"value" - keyCode:web::WebStateObserver:: - kInvalidFormKeyCode inputMissing:false]; EXPECT_FALSE(GetSuggestionView(input_accessory_view_)); } @@ -320,8 +316,6 @@ fieldName:"field" type:"type" value:"value" - keyCode:web::WebStateObserver:: - kInvalidFormKeyCode inputMissing:false]; // The suggestions accessory view should be empty. @@ -348,8 +342,6 @@ fieldName:"field" type:"type" value:"value" - keyCode:web::WebStateObserver:: - kInvalidFormKeyCode inputMissing:false]; // The providers should each be asked if they have suggestions for the @@ -397,8 +389,6 @@ fieldName:"field" type:"type" value:"value" - keyCode:web::WebStateObserver:: - kInvalidFormKeyCode inputMissing:false]; // Since the first provider has suggestions available, it and only it @@ -436,8 +426,6 @@ fieldName:"field" type:"type" value:"value" - keyCode:web::WebStateObserver:: - kInvalidFormKeyCode inputMissing:false]; // Selecting a suggestion should notify the delegate.
diff --git a/ios/chrome/browser/crash_loop_detection_util.h b/ios/chrome/browser/crash_loop_detection_util.h index 0d8118b..ec8692f 100644 --- a/ios/chrome/browser/crash_loop_detection_util.h +++ b/ios/chrome/browser/crash_loop_detection_util.h
@@ -25,6 +25,9 @@ // elapsed, or some deliberate user action has been taken). void ResetFailedStartupAttemptCount(); +// Resets the hidden state of failed startup attempt count for testing. +void ResetFailedStartupAttemptCountForTests(); + } // namespace crash_util #endif // IOS_CHROME_BROWSER_CRASH_LOOP_DETECTION_UTIL_H_
diff --git a/ios/chrome/browser/crash_loop_detection_util.mm b/ios/chrome/browser/crash_loop_detection_util.mm index 2eb1913..0de92d6c 100644 --- a/ios/chrome/browser/crash_loop_detection_util.mm +++ b/ios/chrome/browser/crash_loop_detection_util.mm
@@ -11,13 +11,13 @@ #endif namespace { +static int startup_attempt_count = -1; NSString* const kAppStartupFailureCountKey = @"AppStartupFailureCount"; } namespace crash_util { int GetFailedStartupAttemptCount() { - static int startup_attempt_count = -1; if (startup_attempt_count == -1) { NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; startup_attempt_count = [defaults integerForKey:kAppStartupFailureCountKey]; @@ -42,4 +42,9 @@ } } +void ResetFailedStartupAttemptCountForTests() { + ResetFailedStartupAttemptCount(); + startup_attempt_count = -1; +} + } // namespace crash_util
diff --git a/ios/chrome/browser/crash_loop_detection_util_unittest.mm b/ios/chrome/browser/crash_loop_detection_util_unittest.mm index c36670a0..ea620e8 100644 --- a/ios/chrome/browser/crash_loop_detection_util_unittest.mm +++ b/ios/chrome/browser/crash_loop_detection_util_unittest.mm
@@ -15,6 +15,8 @@ typedef PlatformTest CrashLoopDetectionUtilTest; TEST_F(CrashLoopDetectionUtilTest, FullCycle) { + crash_util::ResetFailedStartupAttemptCountForTests(); + // Simulate one prior crash. NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; [defaults setInteger:1 forKey:kAppStartupAttemptCountKey];
diff --git a/ios/chrome/browser/dom_distiller/distiller_viewer.cc b/ios/chrome/browser/dom_distiller/distiller_viewer.cc index a0bd94ab..46a26ab 100644 --- a/ios/chrome/browser/dom_distiller/distiller_viewer.cc +++ b/ios/chrome/browser/dom_distiller/distiller_viewer.cc
@@ -39,13 +39,14 @@ void DistillerViewer::OnArticleReady( const dom_distiller::DistilledArticleProto* article_proto) { DomDistillerRequestViewBase::OnArticleReady(article_proto); - if (article_proto->pages_size() > 0) { + bool is_empty = article_proto->pages_size() == 0 || + article_proto->pages(0).html().empty(); + if (!is_empty) { std::vector<ImageInfo> images; for (int i = 0; i < article_proto->pages(0).image_size(); i++) { auto image = article_proto->pages(0).image(i); images.push_back(ImageInfo{GURL(image.url()), image.data()}); } - const std::string html = viewer::GetUnsafeArticleTemplateHtml( url_.spec(), distilled_page_prefs_->GetTheme(), distilled_page_prefs_->GetFontFamily());
diff --git a/ios/chrome/browser/metrics/first_user_action_recorder.cc b/ios/chrome/browser/metrics/first_user_action_recorder.cc index d93836cd5..aac5e0c 100644 --- a/ios/chrome/browser/metrics/first_user_action_recorder.cc +++ b/ios/chrome/browser/metrics/first_user_action_recorder.cc
@@ -91,8 +91,6 @@ background_duration_(background_duration), action_callback_(base::Bind(&FirstUserActionRecorder::OnUserAction, base::Unretained(this))) { - base::SetRecordActionTaskRunner( - web::WebThread::GetTaskRunnerForThread(web::WebThread::UI)); base::AddActionCallback(action_callback_); }
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm index 4c942ada..c02088a 100644 --- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm +++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm
@@ -69,9 +69,7 @@ class MobileSessionShutdownMetricsProviderTest : public testing::TestWithParam<int> { public: - MobileSessionShutdownMetricsProviderTest() - : task_runner_(new base::TestSimpleTaskRunner) { - base::SetRecordActionTaskRunner(task_runner_); + MobileSessionShutdownMetricsProviderTest() { metrics::MetricsService::RegisterPrefs(local_state_.registry()); } @@ -82,7 +80,6 @@ std::unique_ptr<metrics::MetricsService> metrics_service_; std::unique_ptr<MobileSessionShutdownMetricsProviderForTesting> metrics_provider_; - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; private: DISALLOW_COPY_AND_ASSIGN(MobileSessionShutdownMetricsProviderTest);
diff --git a/ios/chrome/browser/passwords/password_generation_agent_unittest.mm b/ios/chrome/browser/passwords/password_generation_agent_unittest.mm index d96792be..e1c8020c 100644 --- a/ios/chrome/browser/passwords/password_generation_agent_unittest.mm +++ b/ios/chrome/browser/passwords/password_generation_agent_unittest.mm
@@ -275,8 +275,6 @@ fieldName:base::SysNSStringToUTF8(field_name) type:base::SysNSStringToUTF8(type) value:"" - keyCode:web::WebStateObserver:: - kInvalidFormKeyCode inputMissing:false]; }
diff --git a/ios/chrome/browser/reading_list/BUILD.gn b/ios/chrome/browser/reading_list/BUILD.gn index d68daec..500a8c3 100644 --- a/ios/chrome/browser/reading_list/BUILD.gn +++ b/ios/chrome/browser/reading_list/BUILD.gn
@@ -11,8 +11,6 @@ "reading_list_download_service.h", "reading_list_download_service_factory.cc", "reading_list_download_service_factory.h", - "reading_list_entry_loading_util.h", - "reading_list_entry_loading_util.mm", "reading_list_model_factory.cc", "reading_list_model_factory.h", "reading_list_web_state_observer.h", @@ -44,7 +42,6 @@ testonly = true sources = [ "offline_url_utils_unittest.cc", - "reading_list_entry_loading_util_unittest.mm", "url_downloader_unittest.mm", ] deps = [
diff --git a/ios/chrome/browser/reading_list/offline_url_utils.cc b/ios/chrome/browser/reading_list/offline_url_utils.cc index b3d3037..e2ceb6c 100644 --- a/ios/chrome/browser/reading_list/offline_url_utils.cc +++ b/ios/chrome/browser/reading_list/offline_url_utils.cc
@@ -45,14 +45,12 @@ } GURL FileURLForDistilledURL(const GURL& distilled_url, - const base::FilePath& profile_path, + const base::FilePath& offline_path, GURL* resources_root_url) { if (!distilled_url.is_valid()) { return GURL(); } DCHECK(distilled_url.SchemeIs(kChromeUIScheme)); - base::FilePath offline_path = OfflineRootDirectoryPath(profile_path); - GURL file_url(base::StringPrintf("%s%s", url::kFileScheme, url::kStandardSchemeSeparator) + offline_path.value() + distilled_url.path());
diff --git a/ios/chrome/browser/reading_list/offline_url_utils.h b/ios/chrome/browser/reading_list/offline_url_utils.h index bee8586..479b75c 100644 --- a/ios/chrome/browser/reading_list/offline_url_utils.h +++ b/ios/chrome/browser/reading_list/offline_url_utils.h
@@ -22,9 +22,9 @@ // The file URL pointing to the local file to load to display |distilled_url|. // If |resources_root_url| is not nullptr, it is set to a file URL to the // directory conatining all the resources needed by |distilled_url|. -// |profile_path| is the path to the profile directory. +// |offline_path| is the root path to the directory containing offline files. GURL FileURLForDistilledURL(const GURL& distilled_url, - const base::FilePath& profile_path, + const base::FilePath& offline_path, GURL* resources_root_url); // Returns whether the URL points to a chrome offline URL.
diff --git a/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc b/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc index 7beea10..d711ca5 100644 --- a/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc +++ b/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc
@@ -48,20 +48,20 @@ // Checks the resource root for chrome://offline/MD5/page.html is // file://profile_path/Offline/MD5 TEST(OfflineURLUtilsTest, FileURLForDistilledURLTest) { - base::FilePath profile_path("/profile_path"); + base::FilePath offline_path("/profile_path/Offline"); GURL file_url = - reading_list::FileURLForDistilledURL(GURL(), profile_path, nullptr); + reading_list::FileURLForDistilledURL(GURL(), offline_path, nullptr); EXPECT_FALSE(file_url.is_valid()); GURL distilled_url("chrome://offline/MD5/page.html"); - file_url = reading_list::FileURLForDistilledURL(distilled_url, profile_path, + file_url = reading_list::FileURLForDistilledURL(distilled_url, offline_path, nullptr); EXPECT_TRUE(file_url.is_valid()); EXPECT_TRUE(file_url.SchemeIsFile()); EXPECT_EQ("/profile_path/Offline/MD5/page.html", file_url.path()); GURL resource_url; - file_url = reading_list::FileURLForDistilledURL(distilled_url, profile_path, + file_url = reading_list::FileURLForDistilledURL(distilled_url, offline_path, &resource_url); EXPECT_TRUE(resource_url.is_valid()); EXPECT_TRUE(resource_url.SchemeIsFile());
diff --git a/ios/chrome/browser/reading_list/reading_list_download_service.cc b/ios/chrome/browser/reading_list/reading_list_download_service.cc index 24cdc66d..35047e8 100644 --- a/ios/chrome/browser/reading_list/reading_list_download_service.cc +++ b/ios/chrome/browser/reading_list/reading_list_download_service.cc
@@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" +#include "components/reading_list/ios/offline_url_utils.h" #include "components/reading_list/ios/reading_list_entry.h" #include "components/reading_list/ios/reading_list_model.h" #include "ios/web/public/web_thread.h" @@ -42,6 +43,7 @@ PrefService* prefs, base::FilePath chrome_profile_path) : reading_list_model_(reading_list_model), + chrome_profile_path_(chrome_profile_path), had_connection_(!net::NetworkChangeNotifier::IsOffline()), weak_ptr_factory_(this) { DCHECK(reading_list_model); @@ -62,6 +64,10 @@ reading_list_model_->AddObserver(this); } +base::FilePath ReadingListDownloadService::OfflineRoot() const { + return reading_list::OfflineRootDirectoryPath(chrome_profile_path_); +} + void ReadingListDownloadService::Shutdown() { reading_list_model_->RemoveObserver(this); }
diff --git a/ios/chrome/browser/reading_list/reading_list_download_service.h b/ios/chrome/browser/reading_list/reading_list_download_service.h index 62cd7b40..8f70e28 100644 --- a/ios/chrome/browser/reading_list/reading_list_download_service.h +++ b/ios/chrome/browser/reading_list/reading_list_download_service.h
@@ -41,6 +41,9 @@ // Initializes the reading list download service. void Initialize(); + // The root folder containing all the offline files. + virtual base::FilePath OfflineRoot() const; + // KeyedService implementation. void Shutdown() override; @@ -83,6 +86,7 @@ net::NetworkChangeNotifier::ConnectionType type) override; ReadingListModel* reading_list_model_; + base::FilePath chrome_profile_path_; std::unique_ptr<URLDownloader> url_downloader_; std::vector<GURL> url_to_download_cellular_; std::vector<GURL> url_to_download_wifi_;
diff --git a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.h b/ios/chrome/browser/reading_list/reading_list_entry_loading_util.h deleted file mode 100644 index 0218cda..0000000 --- a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.h +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef IOS_CHROME_BROWSER_READING_LIST_READING_LIST_ENTRY_LOADING_UTIL_H_ -#define IOS_CHROME_BROWSER_READING_LIST_READING_LIST_ENTRY_LOADING_UTIL_H_ - -class ReadingListEntry; -class ReadingListModel; - -namespace web { -class WebState; -}; - -namespace reading_list { - -// Loads the URL of the |entry| into the |web_state|. If the entry is -// successfully loaded, marks the entry as read. -void LoadReadingListEntry(ReadingListEntry const& entry, - ReadingListModel* model, - web::WebState* web_state); - -// Loads the distilled URL of the |entry| into the |web_state|, and marks -// |entry| as read. |entry->DistilledState()| must be |PROCESSED|. -void LoadReadingListDistilled(ReadingListEntry const& entry, - ReadingListModel* model, - web::WebState* web_state); -}; - -#endif // IOS_CHROME_BROWSER_READING_LIST_READING_LIST_ENTRY_LOADING_UTIL_H_
diff --git a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm b/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm deleted file mode 100644 index 3b34fc6..0000000 --- a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2016 The Chromium 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 "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h" - -#include "components/reading_list/ios/reading_list_entry.h" -#include "components/reading_list/ios/reading_list_model.h" -#include "ios/chrome/browser/reading_list/offline_url_utils.h" -#include "ios/chrome/browser/reading_list/reading_list_web_state_observer.h" -#import "ios/web/public/navigation_manager.h" -#import "ios/web/public/web_state/web_state.h" -#include "net/base/network_change_notifier.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -namespace reading_list { - -void LoadReadingListEntry(ReadingListEntry const& entry, - ReadingListModel* model, - web::WebState* web_state) { - // TODO(crbug.com/625617): Evaluate whether NetworkChangeNotifier - // correctly detects when users are offline. - bool open_distilled_entry = - net::NetworkChangeNotifier::IsOffline() && - entry.DistilledState() == ReadingListEntry::PROCESSED; - if (open_distilled_entry) { - return LoadReadingListDistilled(entry, model, web_state); - } - - DCHECK(entry.URL().is_valid()); - web::NavigationManager::WebLoadParams params(entry.URL()); - params.transition_type = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK; - web_state->GetNavigationManager()->LoadURLWithParams(params); - ReadingListWebStateObserver* web_state_observer = - ReadingListWebStateObserver::FromWebState(web_state, model); - web_state_observer->StartCheckingProgress(entry.URL()); -} - -void LoadReadingListDistilled(ReadingListEntry const& entry, - ReadingListModel* model, - web::WebState* web_state) { - DCHECK(entry.DistilledState() == ReadingListEntry::PROCESSED); - GURL url = - reading_list::DistilledURLForPath(entry.DistilledPath(), entry.URL()); - DCHECK(url.is_valid()); - web::NavigationManager::WebLoadParams params(url); - params.transition_type = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK; - web_state->GetNavigationManager()->LoadURLWithParams(params); - model->SetReadStatus(entry.URL(), true); -} - -} // namespace reading_list
diff --git a/ios/chrome/browser/reading_list/reading_list_entry_loading_util_unittest.mm b/ios/chrome/browser/reading_list/reading_list_entry_loading_util_unittest.mm deleted file mode 100644 index 36e19e1e..0000000 --- a/ios/chrome/browser/reading_list/reading_list_entry_loading_util_unittest.mm +++ /dev/null
@@ -1,141 +0,0 @@ -// Copyright 2016 The Chromium 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 "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h" - -#include "base/memory/ptr_util.h" -#include "components/reading_list/ios/reading_list_model_impl.h" -#include "ios/chrome/browser/reading_list/offline_url_utils.h" -#import "ios/web/public/navigation_item.h" -#import "ios/web/public/navigation_manager.h" -#include "ios/web/public/test/web_test_with_web_state.h" -#import "ios/web/public/web_state/web_state.h" -#include "net/base/network_change_notifier.h" -#include "testing/gtest/include/gtest/gtest.h" - -// A mock NetworkChangeNotifier that will report the network state passed in the -// constructor. -class MockNetworkChangeNotifier : public net::NetworkChangeNotifier { - public: - MockNetworkChangeNotifier(ConnectionType connection) - : NetworkChangeNotifier() { - connection_ = connection; - } - - ~MockNetworkChangeNotifier() override {} - - ConnectionType GetCurrentConnectionType() const override { - return connection_; - }; - - private: - ConnectionType connection_; -}; - -// Test fixture to test loading of Reading list entries. -typedef web::WebTestWithWebState ReadingListEntryLoadingUtilTest; - -// Tests that loading a not distilled entry with network will load online -// version. -TEST_F(ReadingListEntryLoadingUtilTest, TestLoadEntryOnlineWODistilled) { - MockNetworkChangeNotifier network_enabler( - net::NetworkChangeNotifier::CONNECTION_WIFI); - GURL url("http://foo.bar"); - auto reading_list_model = - base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr); - reading_list_model->AddEntry(url, "title"); - const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url); - reading_list::LoadReadingListEntry(*entry, reading_list_model.get(), - web_state()); - web::NavigationManager* navigation_manager = - web_state()->GetNavigationManager(); - EXPECT_EQ(navigation_manager->GetPendingItem()->GetURL(), url); - // Entry is only marked read when successfully loaded. - EXPECT_FALSE(entry->IsRead()); -} - -// Tests that loading a distilled entry with network will load online version. -TEST_F(ReadingListEntryLoadingUtilTest, TestLoadEntryOnlineWithistilled) { - MockNetworkChangeNotifier network_enabler( - net::NetworkChangeNotifier::CONNECTION_WIFI); - GURL url("http://foo.bar"); - std::string distilled_path = "distilled/page.html"; - auto reading_list_model = - base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr); - reading_list_model->AddEntry(url, "title"); - reading_list_model->SetEntryDistilledPath(url, - base::FilePath(distilled_path)); - const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url); - reading_list::LoadReadingListEntry(*entry, reading_list_model.get(), - web_state()); - web::NavigationManager* navigation_manager = - web_state()->GetNavigationManager(); - EXPECT_EQ(navigation_manager->GetPendingItem()->GetURL(), url); - // Entry is only marked read when successfully loaded. - EXPECT_FALSE(entry->IsRead()); -} - -// Tests that loading a not distilled entry without network will load online -// version. -TEST_F(ReadingListEntryLoadingUtilTest, TestLoadEntryOfflineWODistilled) { - MockNetworkChangeNotifier network_disabler( - net::NetworkChangeNotifier::CONNECTION_NONE); - GURL url("http://foo.bar"); - auto reading_list_model = - base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr); - reading_list_model->AddEntry(url, "title"); - const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url); - reading_list::LoadReadingListEntry(*entry, reading_list_model.get(), - web_state()); - web::NavigationManager* navigation_manager = - web_state()->GetNavigationManager(); - EXPECT_EQ(navigation_manager->GetPendingItem()->GetURL(), url); - // Entry is only marked read when successfully loaded. - EXPECT_FALSE(entry->IsRead()); -} - -// Tests that loading a distilled entry without network will load offline -// version. -TEST_F(ReadingListEntryLoadingUtilTest, TestLoadEntryOfflineWithDistilled) { - MockNetworkChangeNotifier network_disabler( - net::NetworkChangeNotifier::CONNECTION_NONE); - GURL url("http://foo.bar"); - std::string distilled_path = "distilled/page.html"; - auto reading_list_model = - base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr); - reading_list_model->AddEntry(url, "title"); - reading_list_model->SetEntryDistilledPath(url, - base::FilePath(distilled_path)); - const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url); - reading_list::LoadReadingListEntry(*entry, reading_list_model.get(), - web_state()); - web::NavigationManager* navigation_manager = - web_state()->GetNavigationManager(); - EXPECT_NE(navigation_manager->GetPendingItem()->GetURL(), url); - EXPECT_EQ( - navigation_manager->GetPendingItem()->GetURL(), - reading_list::DistilledURLForPath(entry->DistilledPath(), entry->URL())); - EXPECT_TRUE(entry->IsRead()); -} - -// Tests that loading a distilled version of an entry. -TEST_F(ReadingListEntryLoadingUtilTest, TestLoadReadingListDistilled) { - GURL url("http://foo.bar"); - std::string distilled_path = "distilled/page.html"; - auto reading_list_model = - base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr); - reading_list_model->AddEntry(url, "title"); - reading_list_model->SetEntryDistilledPath(url, - base::FilePath(distilled_path)); - const ReadingListEntry* entry = reading_list_model->GetEntryByURL(url); - reading_list::LoadReadingListDistilled(*entry, reading_list_model.get(), - web_state()); - web::NavigationManager* navigation_manager = - web_state()->GetNavigationManager(); - EXPECT_NE(navigation_manager->GetPendingItem()->GetURL(), url); - EXPECT_EQ( - navigation_manager->GetPendingItem()->GetURL(), - reading_list::DistilledURLForPath(entry->DistilledPath(), entry->URL())); - EXPECT_TRUE(entry->IsRead()); -}
diff --git a/ios/chrome/browser/reading_list/reading_list_web_state_observer.h b/ios/chrome/browser/reading_list/reading_list_web_state_observer.h index 47fcfb6f..1c16afe 100644 --- a/ios/chrome/browser/reading_list/reading_list_web_state_observer.h +++ b/ios/chrome/browser/reading_list/reading_list_web_state_observer.h
@@ -12,6 +12,10 @@ class ReadingListModel; +namespace web { +class NavigationItem; +} + // Observes the loading of pages coming from the reading list, determines // whether loading an offline version of the page is needed, and actually // trigger the loading of the offline page (if possible). @@ -23,34 +27,44 @@ ~ReadingListWebStateObserver() override; - // Starts checking that the current navigation is loading quickly enough [1]. - // If not, starts to load a distilled version of the page (if there is any). - // If that same WebStateObserver was already checking that a page was loading - // quickly enough, stops checking the loading of that page. - // [1] A page loading quickly enough is a page that has loaded 15% within - // 1 second. - void StartCheckingProgress(const GURL& pending_url); - private: ReadingListWebStateObserver(web::WebState* web_state, ReadingListModel* reading_list_model); - // Looks at the loading percentage. If less than 15%, attemps to load the - // offline version of that page. + // Looks at the loading percentage. If less than 25% * time, attemps to load + // the offline version of that page. + // |time| is the number of seconds since |StartCheckingProgress| was called. void VerifyIfReadingListEntryStartedLoading(); friend class ReadingListWebStateObserverUserDataWrapper; - // Stop checking the loading of the |pending_url_|. + // Stops checking the loading of the |pending_url_|. // The WebState will still be observed, but no action will be done on events. void StopCheckingProgress(); + // Loads the offline version of the URL in place of the current page. + void LoadOfflineReadingListEntry(web::NavigationItem* item); + + // Returns if the current page with |url| has an offline version that can be + // displayed if the normal loading fails. + bool IsUrlAvailableOffline(const GURL& url) const; + + // Checks if |item| should be observed or not. + // A non-null item should be observed if it is not already loading an offline + // URL. + bool ShouldObserveItem(web::NavigationItem* item) const; + // WebContentsObserver implementation. - void DidStopLoading() override; void PageLoaded( web::PageLoadCompletionStatus load_completion_status) override; void WebStateDestroyed() override; + // Starts checking that the current navigation is loading quickly enough [1]. + // If not, starts to load a distilled version of the page (if there is any). + // [1] A page loading quickly enough is a page that has loaded 25% within + // 1 second, 50% within 2 seconds and 75% within 3 seconds. + void DidStartLoading() override; + ReadingListModel* reading_list_model_; std::unique_ptr<base::Timer> timer_; GURL pending_url_;
diff --git a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm index b4aa9c09..cc079b7 100644 --- a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm +++ b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
@@ -9,7 +9,8 @@ #include "base/memory/ptr_util.h" #include "components/reading_list/ios/reading_list_model.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" -#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h" +#include "ios/chrome/browser/chrome_url_constants.h" +#include "ios/chrome/browser/reading_list/offline_url_utils.h" #include "ios/chrome/browser/reading_list/reading_list_model_factory.h" #include "ios/web/public/navigation_item.h" #include "ios/web/public/navigation_manager.h" @@ -83,27 +84,50 @@ DCHECK(reading_list_model_); } -void ReadingListWebStateObserver::DidStopLoading() { - StopCheckingProgress(); -} - -void ReadingListWebStateObserver::PageLoaded( - web::PageLoadCompletionStatus load_completion_status) { - if (load_completion_status == web::PageLoadCompletionStatus::SUCCESS && - pending_url_.is_valid()) { - reading_list_model_->SetReadStatus(pending_url_, true); +bool ReadingListWebStateObserver::ShouldObserveItem( + web::NavigationItem* item) const { + if (!item) { + return false; } - StopCheckingProgress(); + GURL loading_url = item->GetURL(); + return !loading_url.SchemeIs(kChromeUIScheme) || + loading_url.host() != kChromeUIOfflineHost; } -void ReadingListWebStateObserver::WebStateDestroyed() { - StopCheckingProgress(); - web_state()->RemoveUserData(kObserverKey); +bool ReadingListWebStateObserver::IsUrlAvailableOffline(const GURL& url) const { + const ReadingListEntry* entry = reading_list_model_->GetEntryByURL(url); + return entry && entry->DistilledState() == ReadingListEntry::PROCESSED; } -void ReadingListWebStateObserver::StartCheckingProgress( - const GURL& pending_url) { - pending_url_ = pending_url; +void ReadingListWebStateObserver::DidStartLoading() { + if (!reading_list_model_->loaded() || !web_state() || + web_state()->IsShowingWebInterstitial()) { + StopCheckingProgress(); + return; + } + + web::NavigationManager* manager = web_state()->GetNavigationManager(); + web::NavigationItem* item = manager->GetPendingItem(); + + // Manager->GetPendingItem() returns null on reload. + // TODO(crbug.com/676129): Remove this workaround once GetPendingItem() + // returns the correct value on reload. + if (!item) { + item = manager->GetLastCommittedItem(); + } + + if (!ShouldObserveItem(item)) { + StopCheckingProgress(); + return; + } + + pending_url_ = item->GetVirtualURL(); + if (!IsUrlAvailableOffline(pending_url_)) { + // No need to launch the timer as there is no offline version to show. + // Track |pending_url_| to mark the entry as read in case of a successful + // load. + return; + } try_number_ = 0; timer_.reset(new base::Timer(false, true)); const base::TimeDelta kDelayUntilLoadingProgressIsChecked = @@ -115,29 +139,82 @@ base::Unretained(this))); } +void ReadingListWebStateObserver::PageLoaded( + web::PageLoadCompletionStatus load_completion_status) { + web::NavigationItem* item = + web_state()->GetNavigationManager()->GetLastCommittedItem(); + if (!item || !pending_url_.is_valid()) { + StopCheckingProgress(); + return; + } + + if (load_completion_status == web::PageLoadCompletionStatus::SUCCESS) { + reading_list_model_->SetReadStatus(pending_url_, true); + } else { + LoadOfflineReadingListEntry(item); + } + StopCheckingProgress(); +} + +void ReadingListWebStateObserver::WebStateDestroyed() { + StopCheckingProgress(); + web_state()->RemoveUserData(kObserverKey); +} + void ReadingListWebStateObserver::StopCheckingProgress() { - pending_url_ = GURL(); + pending_url_ = GURL::EmptyGURL(); timer_.reset(); } void ReadingListWebStateObserver::VerifyIfReadingListEntryStartedLoading() { if (!pending_url_.is_valid()) { + StopCheckingProgress(); return; } - const ReadingListEntry* entry = - reading_list_model_->GetEntryByURL(pending_url_); - if (!entry || entry->DistilledState() != ReadingListEntry::PROCESSED) { + web::NavigationManager* manager = web_state()->GetNavigationManager(); + web::NavigationItem* item = manager->GetPendingItem(); + + // Manager->GetPendingItem() returns null on reload. + // TODO(crbug.com/676129): Remove this workaround once GetPendingItem() + // returns the correct value on reload. + if (!item) { + item = manager->GetLastCommittedItem(); + } + if (!item || !pending_url_.is_valid() || + !IsUrlAvailableOffline(pending_url_)) { + StopCheckingProgress(); return; } try_number_++; double progress = web_state()->GetLoadingProgress(); const double kMinimumExpectedProgressPerStep = 0.25; if (progress < try_number_ * kMinimumExpectedProgressPerStep) { - reading_list::LoadReadingListDistilled(*entry, reading_list_model_, - web_state()); + LoadOfflineReadingListEntry(item); + StopCheckingProgress(); + return; } if (try_number_ >= 3) { // Loading reached 75%, let the page finish normal loading. + // Do not call |StopCheckingProgress()| as |pending_url_| is still + // needed to mark the entry read on success loading or to display + // offline version on error. timer_->Stop(); } } + +void ReadingListWebStateObserver::LoadOfflineReadingListEntry( + web::NavigationItem* item) { + DCHECK(item); + if (!pending_url_.is_valid() || !IsUrlAvailableOffline(pending_url_)) { + return; + } + const ReadingListEntry* entry = + reading_list_model_->GetEntryByURL(pending_url_); + DCHECK(entry->DistilledState() == ReadingListEntry::PROCESSED); + GURL url = + reading_list::DistilledURLForPath(entry->DistilledPath(), entry->URL()); + item->SetURL(url); + item->SetVirtualURL(pending_url_); + web_state()->GetNavigationManager()->Reload(false); + reading_list_model_->SetReadStatus(entry->URL(), true); +}
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn index be04f826..234647ad 100644 --- a/ios/chrome/browser/tabs/BUILD.gn +++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -46,6 +46,8 @@ "//components/metrics_services_manager", "//components/navigation_metrics", "//components/prefs", + "//components/reading_list/core", + "//components/reading_list/ios", "//components/search_engines", "//components/sessions", "//components/signin/core/browser", @@ -71,6 +73,7 @@ "//ios/chrome/browser/net", "//ios/chrome/browser/passwords", "//ios/chrome/browser/passwords:passwords_internal", + "//ios/chrome/browser/reading_list", "//ios/chrome/browser/search_engines", "//ios/chrome/browser/sessions", "//ios/chrome/browser/sessions:sessions_internal",
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm index 6c7d0c2..a0a17d9 100644 --- a/ios/chrome/browser/tabs/tab.mm +++ b/ios/chrome/browser/tabs/tab.mm
@@ -41,6 +41,8 @@ #include "components/navigation_metrics/navigation_metrics.h" #include "components/navigation_metrics/origins_seen_service.h" #include "components/prefs/pref_service.h" +#include "components/reading_list/core/reading_list_switches.h" +#include "components/reading_list/ios/reading_list_model.h" #include "components/search_engines/template_url_service.h" #include "components/sessions/core/session_types.h" #include "components/sessions/ios/ios_serialized_navigation_builder.h" @@ -74,6 +76,8 @@ #import "ios/chrome/browser/passwords/password_controller.h" #import "ios/chrome/browser/passwords/passwords_ui_delegate_impl.h" #include "ios/chrome/browser/pref_names.h" +#include "ios/chrome/browser/reading_list/reading_list_model_factory.h" +#include "ios/chrome/browser/reading_list/reading_list_web_state_observer.h" #include "ios/chrome/browser/search_engines/template_url_service_factory.h" #include "ios/chrome/browser/sessions/ios_chrome_session_tab_helper.h" #include "ios/chrome/browser/signin/account_consistency_service_factory.h" @@ -579,6 +583,12 @@ InfoBarManagerImpl::CreateForWebState(self.webState); IOSSecurityStateTabHelper::CreateForWebState(self.webState); + if (reading_list::switches::IsReadingListEnabled()) { + ReadingListModel* model = + ReadingListModelFactory::GetForBrowserState(browserState_); + ReadingListWebStateObserver::FromWebState(self.webState, model); + } + tabInfoBarObserver_.reset(new TabInfoBarObserver(self)); tabInfoBarObserver_->SetShouldObserveInfoBarManager(true);
diff --git a/ios/chrome/browser/ui/actions/settings_actions.h b/ios/chrome/browser/ui/actions/settings_actions.h index 2800571..15d9d9a 100644 --- a/ios/chrome/browser/ui/actions/settings_actions.h +++ b/ios/chrome/browser/ui/actions/settings_actions.h
@@ -12,6 +12,8 @@ #import <Foundation/Foundation.h> // Target/Action methods relating to the Settings UI. +// (Actions should only be used to communicate into or between the View +// Controller layer). @protocol SettingsActions @optional // Show the Settings UI over whatever UI is currently active.
diff --git a/ios/chrome/browser/ui/actions/tab_grid_actions.h b/ios/chrome/browser/ui/actions/tab_grid_actions.h index 94ddd7e..72539f7 100644 --- a/ios/chrome/browser/ui/actions/tab_grid_actions.h +++ b/ios/chrome/browser/ui/actions/tab_grid_actions.h
@@ -12,6 +12,8 @@ #import <Foundation/Foundation.h> // Target/Action methods relating to the tab grid. +// (Actions should only be used to communicate into or between the View +// Controller layer). @protocol TabGridActions @optional // Dismisses whatever UI is currently active and shows the tab grid.
diff --git a/ios/chrome/browser/ui/actions/tools_menu_actions.h b/ios/chrome/browser/ui/actions/tools_menu_actions.h index c903ad0..3d6d02b 100644 --- a/ios/chrome/browser/ui/actions/tools_menu_actions.h +++ b/ios/chrome/browser/ui/actions/tools_menu_actions.h
@@ -12,6 +12,8 @@ #import <Foundation/Foundation.h> // Target/Action methods relating to the Tools menu UI. +// (Actions should only be used to communicate into or between the View +// Controller layer). @protocol ToolsMenuActions @optional
diff --git a/ios/chrome/browser/ui/collection_view/collection_view_model_unittest.mm b/ios/chrome/browser/ui/collection_view/collection_view_model_unittest.mm index 922bdd37..09d7543e 100644 --- a/ios/chrome/browser/ui/collection_view/collection_view_model_unittest.mm +++ b/ios/chrome/browser/ui/collection_view/collection_view_model_unittest.mm
@@ -302,9 +302,9 @@ [[CollectionViewModel alloc] init]); [model addSectionWithIdentifier:SectionIdentifierCheese]; + logging::SetLogAssertHandler(&LogSink); bool out_of_bounds_exception_thrown = false; @try { - logging::SetLogAssertHandler(&LogSink); [model indexInItemTypeForIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; } @catch (NSException* exception) { @@ -313,6 +313,7 @@ } } EXPECT_TRUE(out_of_bounds_exception_thrown); + logging::SetLogAssertHandler(nullptr); } TEST(CollectionViewModelTest, RemoveItems) {
diff --git a/ios/chrome/browser/ui/commands/BUILD.gn b/ios/chrome/browser/ui/commands/BUILD.gn index ce2d052..e3a31ca 100644 --- a/ios/chrome/browser/ui/commands/BUILD.gn +++ b/ios/chrome/browser/ui/commands/BUILD.gn
@@ -46,3 +46,15 @@ "//url", ] } + +# Clean Skeleton targets +source_set("commands_clean_skeleton") { + sources = [ + "settings_commands.h", + "tab_commands.h", + "tab_grid_commands.h", + "toolbar_commands.h", + ] + + configs += [ "//build/config/compiler:enable_arc" ] +}
diff --git a/ios/chrome/browser/ui/commands/settings_commands.h b/ios/chrome/browser/ui/commands/settings_commands.h new file mode 100644 index 0000000..cb0d2bf --- /dev/null +++ b/ios/chrome/browser/ui/commands/settings_commands.h
@@ -0,0 +1,21 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_SETTINGS_COMMANDS_H_ +#define IOS_CHROME_BROWSER_UI_COMMANDS_SETTINGS_COMMANDS_H_ + +// ====== New Architecture ===== +// = This code is only used in the new iOS Chrome architecture. = +// ============================================================================ + +// Command protocol for commands relating to the Settings UI. +// (Commands are for communicating into or within the coordinator layer). +@protocol SettingsCommands +// Display the settings UI. +- (void)showSettings; +// Dismiss the settings UI. +- (void)closeSettings; +@end + +#endif // IOS_CHROME_BROWSER_UI_COMMANDS_SETTINGS_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/commands/tab_commands.h b/ios/chrome/browser/ui/commands/tab_commands.h new file mode 100644 index 0000000..d8086b47 --- /dev/null +++ b/ios/chrome/browser/ui/commands/tab_commands.h
@@ -0,0 +1,19 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_TAB_COMMANDS_H_ +#define IOS_CHROME_BROWSER_UI_COMMANDS_TAB_COMMANDS_H_ + +// ====== New Architecture ===== +// = This code is only used in the new iOS Chrome architecture. = +// ============================================================================ + +// Command protocol for commands relating to tabs. +// (Commands are for communicating into or within the coordinator layer). +@protocol TabCommands +// Display the tab corresponding to |indexPath|. The receiver determines how +// this correspondence relates to the tab model(s) it knows about. +- (void)showTabAtIndexPath:(NSIndexPath*)indexPath; +@end +#endif // IOS_CHROME_BROWSER_UI_COMMANDS_TAB_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/commands/tab_grid_commands.h b/ios/chrome/browser/ui/commands/tab_grid_commands.h new file mode 100644 index 0000000..2e8faad --- /dev/null +++ b/ios/chrome/browser/ui/commands/tab_grid_commands.h
@@ -0,0 +1,19 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_TAB_GRID_COMMANDS_H_ +#define IOS_CHROME_BROWSER_UI_COMMANDS_TAB_GRID_COMMANDS_H_ + +// ====== New Architecture ===== +// = This code is only used in the new iOS Chrome architecture. = +// ============================================================================ + +// Command protocol for commands relating to the tab grid UI. +// (Commands are for communicating into or within the coordinator layer). +@protocol TabGridCommands +// Display the tab grid. +- (void)showTabGrid; +@end + +#endif // IOS_CHROME_BROWSER_UI_COMMANDS_TAB_GRID_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/commands/toolbar_commands.h b/ios/chrome/browser/ui/commands/toolbar_commands.h new file mode 100644 index 0000000..675c738 --- /dev/null +++ b/ios/chrome/browser/ui/commands/toolbar_commands.h
@@ -0,0 +1,21 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_TOOLBAR_COMMANDS_H_ +#define IOS_CHROME_BROWSER_UI_COMMANDS_TOOLBAR_COMMANDS_H_ + +// ====== New Architecture ===== +// = This code is only used in the new iOS Chrome architecture. = +// ============================================================================ + +// Command protocol for commands relating to the toolbar UI. +// (Commands are for communicating into or within the coordinator layer). +@protocol ToolbarCommands +// Shows the tools menu. +- (void)showToolsMenu; +// Closes the tools menu. +- (void)closeToolsMenu; +@end + +#endif // IOS_CHROME_BROWSER_UI_COMMANDS_TOOLBAR_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/fancy_ui/BUILD.gn b/ios/chrome/browser/ui/fancy_ui/BUILD.gn index 7af8ee0e..2cab462 100644 --- a/ios/chrome/browser/ui/fancy_ui/BUILD.gn +++ b/ios/chrome/browser/ui/fancy_ui/BUILD.gn
@@ -33,4 +33,5 @@ "//base:i18n", "//testing/gtest", ] + configs += [ "//build/config/compiler:enable_arc" ] }
diff --git a/ios/chrome/browser/ui/fancy_ui/bidi_container_view_unittest.mm b/ios/chrome/browser/ui/fancy_ui/bidi_container_view_unittest.mm index ecfb47f..7af825e 100644 --- a/ios/chrome/browser/ui/fancy_ui/bidi_container_view_unittest.mm +++ b/ios/chrome/browser/ui/fancy_ui/bidi_container_view_unittest.mm
@@ -5,9 +5,12 @@ #import "ios/chrome/browser/ui/fancy_ui/bidi_container_view.h" #include "base/i18n/rtl.h" -#include "base/mac/scoped_nsobject.h" #include "testing/platform_test.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace { class BidiContainerViewTest : public PlatformTest { @@ -23,10 +26,9 @@ const char* locale, UIViewAutoresizing autoresizing) { base::i18n::SetICUDefaultLocale(base::i18n::GetCanonicalLocale(locale)); - base::scoped_nsobject<BidiContainerView> view( - [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]); - base::scoped_nsobject<UILabel> label( - [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]); + BidiContainerView* view = + [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]; [label setAutoresizingMask:autoresizing]; [view addSubview:label]; [view layoutSubviews]; @@ -36,10 +38,9 @@ TEST_F(BidiContainerViewTest, InitializeLeftToRight) { base::i18n::SetICUDefaultLocale( base::i18n::GetCanonicalLocale("en" /* English */)); - base::scoped_nsobject<BidiContainerView> view( - [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]); - base::scoped_nsobject<UILabel> label( - [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]); + BidiContainerView* view = + [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]; [view addSubview:label]; [view layoutSubviews]; CGRect labelFrame = [label frame]; @@ -52,10 +53,9 @@ TEST_F(BidiContainerViewTest, InitializeRightToLeft) { base::i18n::SetICUDefaultLocale( base::i18n::GetCanonicalLocale("he" /* Hebrew */)); - base::scoped_nsobject<BidiContainerView> view( - [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]); - base::scoped_nsobject<UILabel> label( - [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]); + BidiContainerView* view = + [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]; [view addSubview:label]; [view layoutSubviews]; CGRect labelFrame = [label frame]; @@ -69,13 +69,11 @@ TEST_F(BidiContainerViewTest, InitializeRightToLeftTwoViews) { base::i18n::SetICUDefaultLocale( base::i18n::GetCanonicalLocale("he" /* Hebrew */)); - base::scoped_nsobject<BidiContainerView> view( - [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]); - base::scoped_nsobject<UILabel> label1( - [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]); + BidiContainerView* view = + [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + UILabel* label1 = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 40, 50)]; [view addSubview:label1]; - base::scoped_nsobject<UILabel> label2( - [[UILabel alloc] initWithFrame:CGRectMake(60, 30, 30, 50)]); + UILabel* label2 = [[UILabel alloc] initWithFrame:CGRectMake(60, 30, 30, 50)]; [view addSubview:label2]; [view layoutSubviews]; EXPECT_EQ(100 - 20 - 40 /* view.width - label1.originX - label1.width */,
diff --git a/ios/chrome/browser/ui/icons/BUILD.gn b/ios/chrome/browser/ui/icons/BUILD.gn index 8114034..506c6b1d 100644 --- a/ios/chrome/browser/ui/icons/BUILD.gn +++ b/ios/chrome/browser/ui/icons/BUILD.gn
@@ -2,34 +2,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -bundle_data("assets") { - sources = [ - "resources/Icons.xcassets/ic_arrow_back.imageset/Contents.json", - "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back.png", - "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back_2x.png", - "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back_3x.png", - "resources/Icons.xcassets/ic_chevron_right.imageset/Contents.json", - "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right.png", - "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right_2x.png", - "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right_3x.png", - "resources/Icons.xcassets/ic_close.imageset/Contents.json", - "resources/Icons.xcassets/ic_close.imageset/ic_close.png", - "resources/Icons.xcassets/ic_close.imageset/ic_close_2x.png", - "resources/Icons.xcassets/ic_close.imageset/ic_close_3x.png", - "resources/Icons.xcassets/ic_info.imageset/Contents.json", - "resources/Icons.xcassets/ic_info.imageset/ic_info.png", - "resources/Icons.xcassets/ic_info.imageset/ic_info_2x.png", - "resources/Icons.xcassets/ic_info.imageset/ic_info_3x.png", - "resources/Icons.xcassets/ic_search.imageset/Contents.json", - "resources/Icons.xcassets/ic_search.imageset/ic_search.png", - "resources/Icons.xcassets/ic_search.imageset/ic_search_2x.png", - "resources/Icons.xcassets/ic_search.imageset/ic_search_3x.png", - ] - outputs = [ - "{{bundle_resources_dir}}/{{source_file_part}}", - ] -} - source_set("icons") { sources = [ "chrome_icon.h", @@ -59,4 +31,33 @@ "//third_party/ocmock", "//ui/base", ] + configs += [ "//build/config/compiler:enable_arc" ] +} + +bundle_data("assets") { + sources = [ + "resources/Icons.xcassets/ic_arrow_back.imageset/Contents.json", + "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back.png", + "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back_2x.png", + "resources/Icons.xcassets/ic_arrow_back.imageset/ic_arrow_back_3x.png", + "resources/Icons.xcassets/ic_chevron_right.imageset/Contents.json", + "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right.png", + "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right_2x.png", + "resources/Icons.xcassets/ic_chevron_right.imageset/ic_chevron_right_3x.png", + "resources/Icons.xcassets/ic_close.imageset/Contents.json", + "resources/Icons.xcassets/ic_close.imageset/ic_close.png", + "resources/Icons.xcassets/ic_close.imageset/ic_close_2x.png", + "resources/Icons.xcassets/ic_close.imageset/ic_close_3x.png", + "resources/Icons.xcassets/ic_info.imageset/Contents.json", + "resources/Icons.xcassets/ic_info.imageset/ic_info.png", + "resources/Icons.xcassets/ic_info.imageset/ic_info_2x.png", + "resources/Icons.xcassets/ic_info.imageset/ic_info_3x.png", + "resources/Icons.xcassets/ic_search.imageset/Contents.json", + "resources/Icons.xcassets/ic_search.imageset/ic_search.png", + "resources/Icons.xcassets/ic_search.imageset/ic_search_2x.png", + "resources/Icons.xcassets/ic_search.imageset/ic_search_3x.png", + ] + outputs = [ + "{{bundle_resources_dir}}/{{source_file_part}}", + ] }
diff --git a/ios/chrome/browser/ui/icons/chrome_icon_unittest.mm b/ios/chrome/browser/ui/icons/chrome_icon_unittest.mm index 84533010..1de818d 100644 --- a/ios/chrome/browser/ui/icons/chrome_icon_unittest.mm +++ b/ios/chrome/browser/ui/icons/chrome_icon_unittest.mm
@@ -9,6 +9,10 @@ #import "third_party/ocmock/ocmock_extensions.h" #include "ui/base/l10n/l10n_util_mac.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + @protocol MockTarget - (void)doSomething; @end
diff --git a/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm b/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm index 538e52e7..a03f7b7 100644 --- a/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm +++ b/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm
@@ -6,8 +6,9 @@ #include <map> +#include "base/bind.h" #include "base/metrics/field_trial.h" -#include "base/test/user_action_tester.h" +#include "base/metrics/user_metrics.h" #include "base/time/time.h" #include "base/values.h" #include "components/metrics/metrics_pref_names.h" @@ -28,12 +29,17 @@ public: NotificationPromoWhatsNewTest() : promo_(&local_state_), + action_callback_( + base::Bind(&NotificationPromoWhatsNewTest::OnUserAction, + base::Unretained(this))), field_trial_list_(new base::FieldTrialList(NULL)) { ios::NotificationPromo::RegisterPrefs(local_state_.registry()); local_state_.registry()->RegisterInt64Pref(metrics::prefs::kInstallDate, 0); + base::AddActionCallback(action_callback_); } ~NotificationPromoWhatsNewTest() override { + base::RemoveActionCallback(action_callback_); variations::testing::ClearAllVariationParams(); } @@ -98,9 +104,19 @@ EXPECT_EQ(icon, promo_.icon()); } + void OnUserAction(const std::string& user_action) { + user_action_count_map_[user_action]++; + } + + int GetUserActionCount(const std::string& user_action) { + return user_action_count_map_[user_action]; + } + protected: TestingPrefServiceSimple local_state_; NotificationPromoWhatsNew promo_; + base::ActionCallback action_callback_; + std::map<std::string, int> user_action_count_map_; private: std::unique_ptr<base::FieldTrialList> field_trial_list_; @@ -203,20 +219,16 @@ "IDS_IOS_APP_RATING_PROMO_STRING", "0", "chrome_command", "", "ratethisapp", "RateThisAppPromo", "logo", "0", "0"); - base::UserActionTester user_action_tester; // Assert that promo is appropriately set up to be viewed. ASSERT_TRUE(promo_.CanShow()); promo_.HandleViewed(); - EXPECT_EQ(1, user_action_tester.GetActionCount( - "WhatsNewPromoViewed_RateThisAppPromo")); + EXPECT_EQ(1, GetUserActionCount("WhatsNewPromoViewed_RateThisAppPromo")); // Verify that the promo closed user action count is 0 before |HandleClosed()| // is called. - EXPECT_EQ(0, user_action_tester.GetActionCount( - "WhatsNewPromoClosed_RateThisAppPromo")); + EXPECT_EQ(0, GetUserActionCount("WhatsNewPromoClosed_RateThisAppPromo")); promo_.HandleClosed(); - EXPECT_EQ(1, user_action_tester.GetActionCount( - "WhatsNewPromoClosed_RateThisAppPromo")); + EXPECT_EQ(1, GetUserActionCount("WhatsNewPromoClosed_RateThisAppPromo")); } } // namespace
diff --git a/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm b/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm index 90454c1..7d1945ebd9 100644 --- a/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm +++ b/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm
@@ -10,18 +10,23 @@ #include "components/reading_list/ios/reading_list_model.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" #include "ios/chrome/browser/reading_list/offline_url_utils.h" -#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h" +#include "ios/chrome/browser/reading_list/reading_list_download_service.h" +#include "ios/chrome/browser/reading_list/reading_list_download_service_factory.h" #include "ios/chrome/browser/reading_list/reading_list_model_factory.h" #import "ios/chrome/browser/ui/static_content/static_html_view_controller.h" #include "ios/web/public/browser_state.h" -#include "net/base/network_change_notifier.h" +#import "ios/web/public/navigation_item.h" +#import "ios/web/public/navigation_manager.h" +#import "ios/web/public/web_state/web_state.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif -#pragma mark - -#pragma mark Public +@interface OfflinePageNativeContent () +// Restores the last committed item to its initial state. +- (void)restoreOnlineURL; +@end @implementation OfflinePageNativeContent { // The virtual URL that will be displayed to the user. @@ -42,14 +47,18 @@ DCHECK(browserState); DCHECK(URL.is_valid()); - if (reading_list::switches::IsReadingListEnabled()) { - _model = ReadingListModelFactory::GetForBrowserState( - ios::ChromeBrowserState::FromBrowserState(browserState)); - } + DCHECK(reading_list::switches::IsReadingListEnabled()); + _model = ReadingListModelFactory::GetForBrowserState( + ios::ChromeBrowserState::FromBrowserState(browserState)); + base::FilePath offline_root = + ReadingListDownloadServiceFactory::GetForBrowserState( + ios::ChromeBrowserState::FromBrowserState(browserState)) + ->OfflineRoot(); + _webState = webState; GURL resourcesRoot; - GURL fileURL = reading_list::FileURLForDistilledURL( - URL, browserState->GetStatePath(), &resourcesRoot); + GURL fileURL = + reading_list::FileURLForDistilledURL(URL, offline_root, &resourcesRoot); StaticHtmlViewController* HTMLViewController = [[StaticHtmlViewController alloc] initWithFileURL:fileURL @@ -62,21 +71,29 @@ URL:URL]; } +- (void)willBeDismissed { + [self restoreOnlineURL]; +} + +- (void)close { + [self restoreOnlineURL]; + [super close]; +} + - (GURL)virtualURL { return _virtualURL; } - (void)reload { - if (!_model || net::NetworkChangeNotifier::IsOffline()) { - [super reload]; - return; - } - const ReadingListEntry* entry = _model->GetEntryByURL([self virtualURL]); - if (entry) { - reading_list::LoadReadingListEntry(*entry, _model, _webState); - } else { - [super reload]; - } + [self restoreOnlineURL]; + _webState->GetNavigationManager()->Reload(false); +} + +- (void)restoreOnlineURL { + web::NavigationItem* item = + _webState->GetNavigationManager()->GetLastCommittedItem(); + item->SetURL([self virtualURL]); + item->SetVirtualURL([self virtualURL]); } @end
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm b/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm index f3273a7..7000f2a 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm +++ b/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm
@@ -7,7 +7,6 @@ #include "base/logging.h" #include "components/reading_list/ios/reading_list_entry.h" #include "components/reading_list/ios/reading_list_model.h" -#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h" #include "ios/web/public/web_state/web_state.h" #include "url/gurl.h" @@ -56,18 +55,20 @@ if (!webState || _readingListModel->unread_size() == 0) { return; } - int64_t updatetime = 0; - const ReadingListEntry* first_entry = nullptr; + int64_t updateTime = 0; + const ReadingListEntry* firstEntry = nullptr; for (const auto& url : _readingListModel->Keys()) { const ReadingListEntry* entry = _readingListModel->GetEntryByURL(url); - if (!entry->IsRead() && entry->UpdateTime() > updatetime) { - updatetime = entry->UpdateTime(); - first_entry = entry; + if (!entry->IsRead() && entry->UpdateTime() > updateTime) { + updateTime = entry->UpdateTime(); + firstEntry = entry; } } - DCHECK_GT(updatetime, 0); - DCHECK_NE(first_entry, nullptr); - reading_list::LoadReadingListEntry(*first_entry, _readingListModel, webState); + DCHECK_GT(updateTime, 0); + DCHECK(firstEntry); + web::NavigationManager::WebLoadParams params(firstEntry->URL()); + params.transition_type = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK; + webState->GetNavigationManager()->LoadURLWithParams(params); } @end
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm index 7ee1123..e4d4b79 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm +++ b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
@@ -19,7 +19,6 @@ #include "components/url_formatter/url_formatter.h" #include "ios/chrome/browser/reading_list/offline_url_utils.h" #include "ios/chrome/browser/reading_list/reading_list_download_service.h" -#include "ios/chrome/browser/reading_list/reading_list_entry_loading_util.h" #import "ios/chrome/browser/tabs/tab.h" #import "ios/chrome/browser/tabs/tab_model.h" #import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h" @@ -423,8 +422,9 @@ Tab* currentTab = _tabModel.currentTab; DCHECK(currentTab); - reading_list::LoadReadingListEntry(*entry, self.readingListModel, - currentTab.webState); + web::NavigationManager::WebLoadParams params(entry->URL()); + params.transition_type = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK; + [currentTab webState]->GetNavigationManager()->LoadURLWithParams(params); [self dismiss]; }
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn index e358659..7fdc973 100644 --- a/ios/chrome/browser/ui/settings/BUILD.gn +++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -196,10 +196,13 @@ sources = [ "passphrase_collection_view_controller_test.h", "passphrase_collection_view_controller_test.mm", + "personal_data_manager_data_changed_observer.cc", + "personal_data_manager_data_changed_observer.h", ] deps = [ ":settings", "//base", + "//components/autofill/core/browser", "//components/browser_sync", "//components/browser_sync:test_support", "//components/keyed_service/core", @@ -379,5 +382,6 @@ ":settings", "//ios/chrome/browser:browser_clean_skeleton", "//ios/chrome/browser/ui/actions", + "//ios/chrome/browser/ui/commands:commands_clean_skeleton", ] }
diff --git a/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm index 9cfbadc3..41ae1ea 100644 --- a/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm +++ b/ios/chrome/browser/ui/settings/autofill_collection_view_controller_unittest.mm
@@ -14,6 +14,7 @@ #include "ios/chrome/browser/autofill/personal_data_manager_factory.h" #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" #import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h" +#include "ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h" #include "ios/web/public/test/test_web_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" @@ -26,8 +27,7 @@ class AutofillCollectionViewControllerTest : public CollectionViewControllerTest { protected: - void SetUp() override { - CollectionViewControllerTest::SetUp(); + AutofillCollectionViewControllerTest() { TestChromeBrowserState::Builder test_cbs_builder; chrome_browser_state_ = test_cbs_builder.Build(); // Profile import requires a PersonalDataManager which itself needs the @@ -44,14 +44,17 @@ void AddProfile(const std::string& origin, const std::string& name, const std::string& address) { - autofill::PersonalDataManager* personalDataManager = + autofill::PersonalDataManager* personal_data_manager = autofill::PersonalDataManagerFactory::GetForBrowserState( chrome_browser_state_.get()); - autofill::AutofillProfile autofillProfile(base::GenerateGUID(), origin); - autofillProfile.SetRawInfo(autofill::NAME_FULL, base::ASCIIToUTF16(name)); - autofillProfile.SetRawInfo(autofill::ADDRESS_HOME_LINE1, - base::ASCIIToUTF16(address)); - personalDataManager->SaveImportedProfile(autofillProfile); + PersonalDataManagerDataChangedObserver observer(personal_data_manager); + + autofill::AutofillProfile autofill_profile(base::GenerateGUID(), origin); + autofill_profile.SetRawInfo(autofill::NAME_FULL, base::ASCIIToUTF16(name)); + autofill_profile.SetRawInfo(autofill::ADDRESS_HOME_LINE1, + base::ASCIIToUTF16(address)); + personal_data_manager->SaveImportedProfile(autofill_profile); + observer.Wait(); // Wait for completion of the asynchronous operation. } web::TestWebThreadBundle thread_bundle_; @@ -83,16 +86,20 @@ // Adding a single credit card results in a credit card section. TEST_F(AutofillCollectionViewControllerTest, TestOneCreditCard) { - autofill::PersonalDataManager* personalDataManager = + autofill::PersonalDataManager* personal_data_manager = autofill::PersonalDataManagerFactory::GetForBrowserState( chrome_browser_state_.get()); - autofill::CreditCard creditCard(base::GenerateGUID(), - "https://www.example.com/"); - creditCard.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL, - base::ASCIIToUTF16("Alan Smithee")); - creditCard.SetRawInfo(autofill::CREDIT_CARD_NUMBER, - base::ASCIIToUTF16("378282246310005")); - personalDataManager->SaveImportedCreditCard(creditCard); + PersonalDataManagerDataChangedObserver observer(personal_data_manager); + + autofill::CreditCard credit_card(base::GenerateGUID(), + "https://www.example.com/"); + credit_card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL, + base::ASCIIToUTF16("Alan Smithee")); + credit_card.SetRawInfo(autofill::CREDIT_CARD_NUMBER, + base::ASCIIToUTF16("378282246310005")); + personal_data_manager->SaveImportedCreditCard(credit_card); + observer.Wait(); // Wait for completion of the asynchronous operation. + CreateController(); // Expect two sections (header and credit card section). EXPECT_EQ(2, NumberOfSections()); @@ -117,6 +124,7 @@ base::mac::ObjCCastStrict<AutofillCollectionViewController>(controller()); // Put the collectionView in 'edit' mode. [view_controller editButtonPressed]; + // This is a bit of a shortcut, since actually clicking on the 'delete' // button would be tough. void (^delete_item_with_wait)(int, int) = ^(int i, int j) { @@ -129,7 +137,16 @@ }); }; + autofill::PersonalDataManager* personal_data_manager = + autofill::PersonalDataManagerFactory::GetForBrowserState( + chrome_browser_state_.get()); + PersonalDataManagerDataChangedObserver observer(personal_data_manager); + + // This call cause a modification of the PersonalDataManager, so wait until + // the asynchronous task complete in addition to waiting for the UI update. delete_item_with_wait(1, 0); + observer.Wait(); // Wait for completion of the asynchronous operation. + // Exit 'edit' mode. [view_controller editButtonPressed];
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller_unittest.mm index 4141ad4..fedc642 100644 --- a/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller_unittest.mm +++ b/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller_unittest.mm
@@ -17,6 +17,7 @@ #include "ios/chrome/browser/autofill/personal_data_manager_factory.h" #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" #import "ios/chrome/browser/ui/collection_view/collection_view_model.h" +#include "ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h" #include "ios/web/public/test/test_web_thread_bundle.h" #include "testing/platform_test.h" @@ -55,6 +56,7 @@ personal_data_manager_ = autofill::PersonalDataManagerFactory::GetForBrowserState( chrome_browser_state_.get()); + PersonalDataManagerDataChangedObserver observer(personal_data_manager_); std::string guid = base::GenerateGUID(); @@ -67,6 +69,7 @@ base::UTF8ToUTF16(kTestAddressLine1)); personal_data_manager_->SaveImportedProfile(autofill_profile); + observer.Wait(); // Wait for the completion of the asynchronous operation. autofill_profile_edit_controller_.reset( [[AutofillProfileEditCollectionViewController
diff --git a/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.cc b/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.cc new file mode 100644 index 0000000..05253e236 --- /dev/null +++ b/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.cc
@@ -0,0 +1,33 @@ +// Copyright 2016 The Chromium 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 "ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h" + +#include "components/autofill/core/browser/personal_data_manager.h" + +PersonalDataManagerDataChangedObserver::PersonalDataManagerDataChangedObserver( + autofill::PersonalDataManager* personal_data_manager) + : personal_data_manager_(personal_data_manager) { + personal_data_manager_->AddObserver(this); +} + +PersonalDataManagerDataChangedObserver:: + ~PersonalDataManagerDataChangedObserver() { + personal_data_manager_->RemoveObserver(this); +} + +void PersonalDataManagerDataChangedObserver::Wait() { + // If a test is blocked in that method, it means that OnPersonalDataChanged() + // was never called which indicates a bug in either the expectation of the + // test (no asynchronous operation is executed on the PersonalDataManager + // passed in the constructor) or in the utilisation of the + // PersonalDataManagerDataChangedObserver (there is a race-condition between + // sending the asynchronous operation and the creation of the observer, it can + // be fixed by creating the observer before sending the operation). + run_loop_.Run(); +} + +void PersonalDataManagerDataChangedObserver::OnPersonalDataChanged() { + run_loop_.QuitWhenIdle(); +}
diff --git a/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h b/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h new file mode 100644 index 0000000..5b4ae40 --- /dev/null +++ b/ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h
@@ -0,0 +1,45 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PERSONAL_DATA_MANAGER_DATA_CHANGED_OBSERVER_H_ +#define IOS_CHROME_BROWSER_UI_SETTINGS_PERSONAL_DATA_MANAGER_DATA_CHANGED_OBSERVER_H_ + +#include "base/macros.h" +#include "base/run_loop.h" +#include "components/autofill/core/browser/personal_data_manager_observer.h" + +namespace autofill { +class PersonalDataManager; +} + +// Helper class to allow waiting until the asynchronous operation on the +// autofill::PersonalDataManager completed. Need to be used like this: +// +// autofill::PersonalDataManager* personal_data_manager = ...; +// PersonalDataManagerDataChangedObserver observer(personal_data_manager); +// personal_data_manager->...(); // Starts some asynchronous operation. +// observer.Wait(); +// +class PersonalDataManagerDataChangedObserver + : public autofill::PersonalDataManagerObserver { + public: + PersonalDataManagerDataChangedObserver( + autofill::PersonalDataManager* personal_data_manager); + ~PersonalDataManagerDataChangedObserver() override; + + // Blocks until |OnPersonalDataChanged| is invoked at the end of the + // asynchronous modification on the PersonalDataManager. + void Wait(); + + // autofill::PersonalDataManagerObserver implementation. + void OnPersonalDataChanged() override; + + private: + autofill::PersonalDataManager* personal_data_manager_; + base::RunLoop run_loop_; + + DISALLOW_COPY_AND_ASSIGN(PersonalDataManagerDataChangedObserver); +}; + +#endif // IOS_CHROME_BROWSER_UI_SETTINGS_PERSONAL_DATA_MANAGER_DATA_CHANGED_OBSERVER_H_
diff --git a/ios/chrome/browser/ui/settings/privacy_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/privacy_collection_view_controller_unittest.mm index 445bcb6..b54ad4e 100644 --- a/ios/chrome/browser/ui/settings/privacy_collection_view_controller_unittest.mm +++ b/ios/chrome/browser/ui/settings/privacy_collection_view_controller_unittest.mm
@@ -24,6 +24,7 @@ #import "ios/chrome/browser/ui/settings/physical_web_collection_view_controller.h" #include "ios/chrome/grit/ios_chromium_strings.h" #include "ios/chrome/grit/ios_strings.h" +#include "ios/chrome/test/ios_chrome_scoped_testing_local_state.h" #include "ios/web/public/test/test_web_thread_bundle.h" #include "ios/web/public/web_capabilities.h" #include "testing/gtest/include/gtest/gtest.h" @@ -76,6 +77,7 @@ } web::TestWebThreadBundle thread_bundle_; + IOSChromeScopedTestingLocalState local_state_; std::unique_ptr<TestChromeBrowserState> chrome_browser_state_; base::scoped_nsobject<NSString> initialValueForSpdyProxyEnabled_; };
diff --git a/ios/chrome/browser/ui/settings/settings_coordinator.h b/ios/chrome/browser/ui/settings/settings_coordinator.h index e3a0089..6ff3a86 100644 --- a/ios/chrome/browser/ui/settings/settings_coordinator.h +++ b/ios/chrome/browser/ui/settings/settings_coordinator.h
@@ -13,18 +13,13 @@ #import "ios/chrome/browser/browser_coordinator.h" -// Action delegate protocol for coordinators to handle changes to the -// Settings UI. -@protocol SettingsActionDelegate<NSObject> -// Close the settings UI. -- (void)closeSettings; -@end +@protocol SettingsCommands; // A coordinator for the Settings UI, which is usually presented modally // on top of whatever other UI is currently active. @interface SettingsCoordinator : BrowserCoordinator // Action delegate for this coordinator. -@property(nonatomic, weak) id<SettingsActionDelegate> actionDelegate; +@property(nonatomic, weak) id<SettingsCommands> settingsCommandHandler; @end #endif // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/settings/settings_coordinator.mm b/ios/chrome/browser/ui/settings/settings_coordinator.mm index 87e5d2a..87dcd87 100644 --- a/ios/chrome/browser/ui/settings/settings_coordinator.mm +++ b/ios/chrome/browser/ui/settings/settings_coordinator.mm
@@ -9,6 +9,7 @@ #import "ios/chrome/browser/ui/settings/settings_coordinator.h" #import "ios/chrome/browser/browser_coordinator+internal.h" +#import "ios/chrome/browser/ui/commands/settings_commands.h" #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -20,7 +21,7 @@ @end @implementation SettingsCoordinator -@synthesize actionDelegate = _actionDelegate; +@synthesize settingsCommandHandler = _settingsCommandHandler; @synthesize viewController = _viewController; #pragma mark - BrowserCoordinator @@ -54,7 +55,7 @@ } - (void)closeSettings { - [self.actionDelegate closeSettings]; + [self.settingsCommandHandler closeSettings]; } @end
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm index 3d4a3fba..04b7f2b 100644 --- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm +++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
@@ -122,8 +122,7 @@ historySideSwipeProvider_.reset( [[HistorySideSwipeProvider alloc] initWithTabModel:model_]); - if (!browserState->IsOffTheRecord() && - reading_list::switches::IsReadingListEnabled()) { + if (reading_list::switches::IsReadingListEnabled()) { readingListSideSwipeProvider_.reset([[ReadingListSideSwipeProvider alloc] initWithReadingList:ReadingListModelFactory::GetForBrowserState( browserState)]);
diff --git a/ios/chrome/browser/ui/tab/tab_container_view_controller.mm b/ios/chrome/browser/ui/tab/tab_container_view_controller.mm index 7a54d19..3f7c173 100644 --- a/ios/chrome/browser/ui/tab/tab_container_view_controller.mm +++ b/ios/chrome/browser/ui/tab/tab_container_view_controller.mm
@@ -41,7 +41,7 @@ // Called after a new toolbar view controller is set, but before // |-didMoveToParentViewController:| is called on that view controller. -- (void)didAddContentViewController; +- (void)didAddToolbarViewController; // Methods to populate the constraint properties. - (void)updateContentConstraintsWithToolbar;
diff --git a/ios/chrome/browser/ui/tab_grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/BUILD.gn index e38eb26e..838c6d6b 100644 --- a/ios/chrome/browser/ui/tab_grid/BUILD.gn +++ b/ios/chrome/browser/ui/tab_grid/BUILD.gn
@@ -15,6 +15,7 @@ "//base", "//ios/chrome/browser:browser_clean_skeleton", "//ios/chrome/browser/browser_state", + "//ios/chrome/browser/ui/commands:commands_clean_skeleton", "//ios/chrome/browser/ui/settings:settings_clean_skeleton", "//ios/chrome/browser/ui/tab", "//ios/web", @@ -37,5 +38,6 @@ "//base", "//ios/chrome/browser/ui/actions", "//ios/chrome/browser/ui/animators", + "//ios/chrome/browser/ui/commands:commands_clean_skeleton", ] }
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm index 5a826f7c..fa34e39 100644 --- a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm +++ b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
@@ -13,6 +13,9 @@ #include "base/strings/sys_string_conversions.h" #import "ios/chrome/browser/browser_coordinator+internal.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" +#import "ios/chrome/browser/ui/commands/settings_commands.h" +#import "ios/chrome/browser/ui/commands/tab_commands.h" +#import "ios/chrome/browser/ui/commands/tab_grid_commands.h" #import "ios/chrome/browser/ui/settings/settings_coordinator.h" #import "ios/chrome/browser/ui/tab/tab_coordinator.h" #import "ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h" @@ -26,8 +29,9 @@ #endif @interface TabGridCoordinator ()<TabGridDataSource, - TabGridActionDelegate, - SettingsActionDelegate> + SettingsCommands, + TabCommands, + TabGridCommands> @property(nonatomic, strong) TabGridViewController* viewController; @property(nonatomic, weak) SettingsCoordinator* settingsCoordinator; @end @@ -44,7 +48,9 @@ - (void)start { self.viewController = [[TabGridViewController alloc] init]; self.viewController.dataSource = self; - self.viewController.actionDelegate = self; + self.viewController.settingsCommandHandler = self; + self.viewController.tabCommandHandler = self; + self.viewController.tabGridCommandHandler = self; // |rootViewController| is nullable, so this is by design a no-op if it hasn't // been set. This may be true in a unit test, or if this coordinator is being @@ -76,7 +82,7 @@ return urlText; } -#pragma mark - TabGridActionDelegate +#pragma mark - TabCommands - (void)showTabAtIndexPath:(NSIndexPath*)indexPath { DCHECK(_placeholderWebState); @@ -88,6 +94,8 @@ [tabCoordinator start]; } +#pragma mark - TabGridCommands + - (void)showTabGrid { // This object should only ever have at most one child. DCHECK_LE(self.children.count, 1UL); @@ -96,18 +104,16 @@ [self removeChildCoordinator:child]; } -#pragma mark - TabGridActionDelegate +#pragma mark - SettingsCommands - (void)showSettings { SettingsCoordinator* settingsCoordinator = [[SettingsCoordinator alloc] init]; - settingsCoordinator.actionDelegate = self; + settingsCoordinator.settingsCommandHandler = self; [self addOverlayCoordinator:settingsCoordinator]; self.settingsCoordinator = settingsCoordinator; [settingsCoordinator start]; } -#pragma mark - SettingsActionDelegate - - (void)closeSettings { [self.settingsCoordinator stop]; [self.settingsCoordinator.parentCoordinator
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h index a1043310..dddf35b 100644 --- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h +++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h
@@ -13,6 +13,10 @@ #import "ios/chrome/browser/ui/animators/zoom_transition_delegate.h" +@protocol SettingsCommands; +@protocol TabCommands; +@protocol TabGridCommands; + // The data source for tab grid UI. // Conceptually the tab grid represents a group of WebState objects (which // are ultimately the model-layer representation of a browser tab). The data @@ -27,20 +31,16 @@ @end -@protocol TabGridActionDelegate<NSObject> -- (void)showTabAtIndexPath:(NSIndexPath*)indexPath; -- (void)showTabGrid; -- (void)showSettings; -@end - // Controller for a scrolling view displaying square cells that represent // the user's open tabs. @interface TabGridViewController : UIViewController<ZoomTransitionDelegate> // Data source for the tabs to be displayed. @property(nonatomic, weak) id<TabGridDataSource> dataSource; -@property(nonatomic, weak) id<TabGridActionDelegate> actionDelegate; - +// Command handlers. +@property(nonatomic, weak) id<SettingsCommands> settingsCommandHandler; +@property(nonatomic, weak) id<TabCommands> tabCommandHandler; +@property(nonatomic, weak) id<TabGridCommands> tabGridCommandHandler; @end #endif // IOS_CHROME_BROWSER_UI_TAB_GRID_TAB_GRID_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm index 69684bc7..202dff0 100644 --- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm +++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -12,6 +12,9 @@ #import "ios/chrome/browser/ui/actions/settings_actions.h" #import "ios/chrome/browser/ui/actions/tab_grid_actions.h" #import "ios/chrome/browser/ui/tab_grid/tab_grid_tab_cell.h" +#import "ios/chrome/browser/ui/commands/settings_commands.h" +#import "ios/chrome/browser/ui/commands/tab_commands.h" +#import "ios/chrome/browser/ui/commands/tab_grid_commands.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -33,7 +36,9 @@ @implementation TabGridViewController @synthesize dataSource = _dataSource; -@synthesize actionDelegate = _actionDelegate; +@synthesize settingsCommandHandler = _settingsCommandHandler; +@synthesize tabGridCommandHandler = _tabGridCommandHandler; +@synthesize tabCommandHandler = _tabCommandHandler; @synthesize grid = _grid; - (void)viewDidLoad { @@ -122,7 +127,7 @@ - (void)collectionView:(UICollectionView*)collectionView didSelectItemAtIndexPath:(NSIndexPath*)indexPath { - [self.actionDelegate showTabAtIndexPath:indexPath]; + [self.tabCommandHandler showTabAtIndexPath:indexPath]; } #pragma mark - ZoomTransitionDelegate methods @@ -138,13 +143,13 @@ #pragma mark - SettingsActions - (void)showSettings:(id)sender { - [self.actionDelegate showSettings]; + [self.settingsCommandHandler showSettings]; } #pragma mark - TabGridActions - (void)showTabGrid:(id)sender { - [self.actionDelegate showTabGrid]; + [self.tabGridCommandHandler showTabGrid]; } @end
diff --git a/ios/chrome/browser/ui/toolbar/BUILD.gn b/ios/chrome/browser/ui/toolbar/BUILD.gn index 360b1db6..98e3916 100644 --- a/ios/chrome/browser/ui/toolbar/BUILD.gn +++ b/ios/chrome/browser/ui/toolbar/BUILD.gn
@@ -219,6 +219,7 @@ "//ios/chrome/browser:browser_clean_skeleton", "//ios/chrome/browser/ui/actions", "//ios/chrome/browser/ui/animators", + "//ios/chrome/browser/ui/commands:commands_clean_skeleton", "//ios/chrome/browser/ui/tools", "//ios/web", ]
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm index a172a84..b684843 100644 --- a/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm +++ b/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
@@ -10,6 +10,7 @@ #include "base/strings/sys_string_conversions.h" #import "ios/chrome/browser/browser_coordinator+internal.h" +#import "ios/chrome/browser/ui/commands/toolbar_commands.h" #import "ios/chrome/browser/ui/toolbar/toolbar_view_controller.h" #import "ios/chrome/browser/ui/tools/tools_coordinator.h" #include "ios/web/public/web_state/web_state.h" @@ -18,7 +19,7 @@ #error "This file requires ARC support." #endif -@interface ToolbarCoordinator ()<ToolbarActionDelegate> +@interface ToolbarCoordinator ()<ToolbarCommands> @property(nonatomic, weak) ToolsCoordinator* toolsMenuCoordinator; @property(nonatomic, strong) ToolbarViewController* viewController; @end @@ -29,7 +30,7 @@ - (void)start { self.viewController = [[ToolbarViewController alloc] init]; - self.viewController.actionDelegate = self; + self.viewController.toolbarCommandHandler = self; [self.rootViewController presentViewController:self.viewController animated:YES @@ -44,7 +45,7 @@ setCurrentPageText:base::SysUTF8ToNSString(pageURL.spec())]; } -#pragma mark - ToolbarActionDelegate +#pragma mark - ToolbarCommands - (void)showToolsMenu { ToolsCoordinator* toolsCoordinator = [[ToolsCoordinator alloc] init];
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_view_controller.h b/ios/chrome/browser/ui/toolbar/toolbar_view_controller.h index 4a181299..db348c7 100644 --- a/ios/chrome/browser/ui/toolbar/toolbar_view_controller.h +++ b/ios/chrome/browser/ui/toolbar/toolbar_view_controller.h
@@ -13,16 +13,7 @@ #import "ios/chrome/browser/ui/animators/zoom_transition_delegate.h" -// Action delegate for coordinators that handle the toolbar UI. -@protocol ToolbarActionDelegate<NSObject> - -// Shows the tools menu. -- (void)showToolsMenu; - -// Closes the tools menu. -- (void)closeToolsMenu; - -@end +@protocol ToolbarCommands; // View controller for a toolbar, which will show a horizontal row of // controls and/or labels. @@ -31,8 +22,8 @@ // height for it. @interface ToolbarViewController : UIViewController<ZoomTransitionDelegate> -// The action delegate for this view controller.:wq -@property(nonatomic, weak) id<ToolbarActionDelegate> actionDelegate; +// The action delegate for this view controller. +@property(nonatomic, weak) id<ToolbarCommands> toolbarCommandHandler; // Sets the text for a label appearing in the center of the toolbar. - (void)setCurrentPageText:(NSString*)text;
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/toolbar_view_controller.mm index 0c7cecc9..af7990e 100644 --- a/ios/chrome/browser/ui/toolbar/toolbar_view_controller.mm +++ b/ios/chrome/browser/ui/toolbar/toolbar_view_controller.mm
@@ -10,6 +10,7 @@ #import "ios/chrome/browser/ui/actions/tab_grid_actions.h" #import "ios/chrome/browser/ui/actions/tools_menu_actions.h" +#import "ios/chrome/browser/ui/commands/toolbar_commands.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -21,7 +22,7 @@ @end @implementation ToolbarViewController -@synthesize actionDelegate = _actionDelegate; +@synthesize toolbarCommandHandler = _toolbarCommandHandler; @synthesize omnibox = _omnibox; @synthesize toolsMenu = _toolsMenu; @@ -86,11 +87,11 @@ #pragma mark - ToolsMenuActions - (void)showToolsMenu:(id)sender { - [self.actionDelegate showToolsMenu]; + [self.toolbarCommandHandler showToolsMenu]; } - (void)closeToolsMenu:(id)sender { - [self.actionDelegate closeToolsMenu]; + [self.toolbarCommandHandler closeToolsMenu]; } @end
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn index 77b7009..8f457ab 100644 --- a/ios/chrome/test/BUILD.gn +++ b/ios/chrome/test/BUILD.gn
@@ -33,6 +33,7 @@ deps = [ "//base", + "//base/test:test_support", "//components/content_settings/core/common", "//components/network_time", "//components/prefs",
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h index 9c01844..c32d84b 100644 --- a/ios/chrome/test/earl_grey/chrome_matchers.h +++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -36,10 +36,6 @@ // Returns matcher for webview not containing |text|. id<GREYMatcher> webViewNotContainingText(std::string text); -// Matcher for WKWebView containing a blocked |image_id|. When blocked, the -// image element will be smaller than actual image. -id<GREYMatcher> webViewContainingBlockedImage(std::string image_id); - // Returns matcher for a StaticHtmlViewController containing |text|. id<GREYMatcher> staticHtmlViewContainingText(NSString* text);
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm index eea05e7b..02ddd256 100644 --- a/ios/chrome/test/earl_grey/chrome_matchers.mm +++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -126,11 +126,6 @@ return web::webViewNotContainingText(std::move(text), GetCurrentWebState()); } -id<GREYMatcher> webViewContainingBlockedImage(std::string image_id) { - return web::webViewContainingBlockedImage(std::move(image_id), - GetCurrentWebState()); -} - id<GREYMatcher> staticHtmlViewContainingText(NSString* text) { // The WKWebView in a static HTML view isn't part of a webState, but it // does have the StaticHtmlViewController as its navigation delegate.
diff --git a/ios/chrome/test/ios_chrome_unit_test_suite.h b/ios/chrome/test/ios_chrome_unit_test_suite.h index 86e8314d..faf545d 100644 --- a/ios/chrome/test/ios_chrome_unit_test_suite.h +++ b/ios/chrome/test/ios_chrome_unit_test_suite.h
@@ -7,6 +7,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/single_thread_task_runner.h" #include "ios/web/public/test/web_test_suite.h" // Test suite for unit tests. @@ -19,6 +20,8 @@ void Initialize() override; private: + scoped_refptr<base::SingleThreadTaskRunner> action_task_runner_; + DISALLOW_COPY_AND_ASSIGN(IOSChromeUnitTestSuite); };
diff --git a/ios/chrome/test/ios_chrome_unit_test_suite.mm b/ios/chrome/test/ios_chrome_unit_test_suite.mm index 43b6b2b2b..0797c7d 100644 --- a/ios/chrome/test/ios_chrome_unit_test_suite.mm +++ b/ios/chrome/test/ios_chrome_unit_test_suite.mm
@@ -5,7 +5,9 @@ #include "ios/chrome/test/ios_chrome_unit_test_suite.h" #include "base/macros.h" +#include "base/metrics/user_metrics.h" #include "base/path_service.h" +#include "base/test/test_simple_task_runner.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "ios/chrome/browser/browser_state/browser_state_keyed_service_factories.h" #include "ios/chrome/browser/chrome_paths.h" @@ -55,7 +57,8 @@ } // namespace IOSChromeUnitTestSuite::IOSChromeUnitTestSuite(int argc, char** argv) - : web::WebTestSuite(argc, argv) {} + : web::WebTestSuite(argc, argv), + action_task_runner_(new base::TestSimpleTaskRunner) {} IOSChromeUnitTestSuite::~IOSChromeUnitTestSuite() {} @@ -74,6 +77,10 @@ // test is run so that the dependencies are correctly resolved. EnsureBrowserStateKeyedServiceFactoriesBuilt(); + // Register a SingleThreadTaskRunner for base::RecordAction as overridding + // it in individual tests is unsafe (as there is no way to unregister). + base::SetRecordActionTaskRunner(action_task_runner_); + ios::RegisterPathProvider(); ui::RegisterPathProvider(); url::AddStandardScheme(kChromeUIScheme, url::SCHEME_WITHOUT_PORT);
diff --git a/ios/showcase/common/BUILD.gn b/ios/showcase/common/BUILD.gn index 3a469136..8a5dcd14 100644 --- a/ios/showcase/common/BUILD.gn +++ b/ios/showcase/common/BUILD.gn
@@ -5,7 +5,12 @@ source_set("common") { sources = [ "coordinator.h", + "protocol_alerter.h", + "protocol_alerter.mm", ] - libs = [ "Foundation.framework" ] + deps = [ + "//base", + ] + libs = [ "UIKit.framework" ] configs += [ "//build/config/compiler:enable_arc" ] }
diff --git a/ios/showcase/common/protocol_alerter.h b/ios/showcase/common/protocol_alerter.h new file mode 100644 index 0000000..02ad7645 --- /dev/null +++ b/ios/showcase/common/protocol_alerter.h
@@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_SHOWCASE_COMMON_PROTOCOL_ALERTER_H_ +#define IOS_SHOWCASE_COMMON_PROTOCOL_ALERTER_H_ + +#import <UIKit/UIKit.h> + +// A protocol alerter is a stub object for testing UI components. It can +// be initialized to conform to one or more protocols. When a protocol method +// is called on it, it will log the call to the console. If the alerter has +// has |baseViewController| set, it will display a UIAlert instead of logging. +@interface ProtocolAlerter : NSProxy + +// Create a new alerter that responds to all of the selectors in all of the +// protocols in |protocols|. +- (instancetype)initWithProtocols:(NSArray<Protocol*>*)protocols; + +// The view controller (if any)_ that will be used to present alerts. +@property(nonatomic, weak) UIViewController* baseViewController; + +@end + +#endif // IOS_SHOWCASE_COMMON_PROTOCOL_ALERTER_H_
diff --git a/ios/showcase/common/protocol_alerter.mm b/ios/showcase/common/protocol_alerter.mm new file mode 100644 index 0000000..888f957 --- /dev/null +++ b/ios/showcase/common/protocol_alerter.mm
@@ -0,0 +1,201 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/showcase/common/protocol_alerter.h" + +#import <objc/runtime.h> + +#import "base/logging.h" +#import "base/strings/sys_string_conversions.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { +// Opaque value to use as an associated object key. +char kAssociatedProtocolNameKey; +} + +@interface NSInvocation (Description) +// Returns a string description of the invocation consisting of the selector +// name interspersed with argument values. +- (NSString*)crsc_description; +@end + +@interface ProtocolAlerter () { + NSSet<Protocol*>* _protocols; +} +@end + +@implementation ProtocolAlerter + +@synthesize baseViewController = _baseViewController; + +- (instancetype)initWithProtocols:(NSArray<Protocol*>*)protocols { + if (!protocols) + return nil; + // NSProxy isn't a subclass of NSObject, and has no superclass, so + // there's no [super init] to call. + _protocols = [[NSSet<Protocol*> alloc] initWithArray:protocols]; + return self; +} + +#pragma mark - NSProxy + +- (NSMethodSignature*)methodSignatureForSelector:(SEL)sel { + for (Protocol* protocol in _protocols) { + for (NSNumber* required in @[ @(YES), @(NO) ]) { + struct objc_method_description method = + protocol_getMethodDescription(protocol, + sel, + required.boolValue, + YES /* an instance method */); + if (method.name != NULL) { + NSMethodSignature* signature = + [NSMethodSignature signatureWithObjCTypes:method.types]; + // Tag the method signature with the protocol name. + objc_setAssociatedObject(signature, + &kAssociatedProtocolNameKey, + NSStringFromProtocol(protocol), + OBJC_ASSOCIATION_COPY_NONATOMIC); + return signature; + } + } + } + return nil; +} + +- (void)forwardInvocation:(NSInvocation*)invocation { + // Instead of actually doing anything the protocol method would normally + // do, instead just generate a title and description and display an alert or + // log a message. + NSString* protocolName = objc_getAssociatedObject( + [self methodSignatureForSelector:invocation.selector], + &kAssociatedProtocolNameKey); + NSString* description = [invocation crsc_description]; + if (self.baseViewController) { + [self showAlertWithTitle:protocolName message:description]; + } else { + VLOG(0) << "Alerter -- protocol:" + << base::SysNSStringToUTF8(protocolName); + VLOG(0) << "Alerter -- invocation:" + << base::SysNSStringToUTF8(description); + } +} + +#pragma mark - NSObject + +- (BOOL)conformsToProtocol:(Protocol*)aProtocol { + for (Protocol* protocol in _protocols) { + // Handle protocols that conform to other protocols. + if (protocol_conformsToProtocol(protocol, aProtocol)) + return YES; + } + return NO; +} + +- (BOOL)respondsToSelector:(SEL)aSelector { + return [self methodSignatureForSelector:aSelector] != nil; +} + +#pragma mark - Private + +// Helper to show simple alert. +- (void)showAlertWithTitle:(NSString*)title message:(NSString*)message { + UIAlertController* alertController = + [UIAlertController alertControllerWithTitle:title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* action = + [UIAlertAction actionWithTitle:@"Done" + style:UIAlertActionStyleCancel + handler:nil]; + [alertController addAction:action]; + [self.baseViewController presentViewController:alertController + animated:YES + completion:nil]; +} + +@end + +@implementation NSInvocation (Description) + +- (NSString*)crsc_description { + NSInteger arguments = self.methodSignature.numberOfArguments; + NSString* selector = NSStringFromSelector(self.selector); + + // NSInvocation's first two arguments are |self| and |_cmd|; if they are the + // only ones, then the invocation has no actual arguments. + if (arguments == 2) + return selector; + + // Get the parts of the selector name by splitting on /:/, and dropping the + // last (empty) part. + NSArray* keywords = [[selector componentsSeparatedByString:@":"] + subarrayWithRange:NSMakeRange(0, arguments - 2)]; + NSMutableString* description = [[NSMutableString alloc] init]; + NSInteger argumentIndex = 2; + for (NSString* keyword in keywords) { + // Insert a space before each keyword after the first one. + if (description.length) + [description appendString:@" "]; + [description appendString:keyword]; + [description appendString:@":"]; + [description appendString:[self argumentDescriptionAtIndex:argumentIndex]]; + argumentIndex++; + } + + return description; +} + +// Return a string describing the argument value at |index|. +// (|index| is in NSInvocation's argument array). +- (NSString*)argumentDescriptionAtIndex:(NSInteger)index { + const char* type = [self.methodSignature getArgumentTypeAtIndex:index]; + + switch (*type) { + case '@': + return [self objectDescriptionAtIndex:index]; + // Add cases as needed here. + default: + return [NSString stringWithFormat:@"<Unknown Type:%c>", *type]; + } +} + +// Return a string describing an argument at |index| that's known to an +// objective-C object. +- (NSString*)objectDescriptionAtIndex:(NSInteger)index { + id object; + + [self getArgument:&object atIndex:index]; + if (!object) + return @"nil"; + + NSString* description = [object description]; + NSString* className = NSStringFromClass([object class]); + if (!description) { + return + [NSString stringWithFormat:@"<%@ object, no description>", className]; + } + + // Wrap strings in @" ... ". + if ([object isKindOfClass:[NSString class]]) + return [NSString stringWithFormat:@"@\"%@\"", description]; + + // Remove the address of objects from their descriptions, so (for example): + // <NSObject: 0xc00lf0ccac1a> + // becomes just: + // <NSObject> + NSRange range = NSMakeRange(0, description.length); + NSString* classPlusAddress = + [className stringByAppendingString:@": 0x[0-9a-c]+"]; + return [description + stringByReplacingOccurrencesOfString:classPlusAddress + withString:className + options:NSRegularExpressionSearch + range:range]; +} + +@end
diff --git a/ios/showcase/tab_grid/BUILD.gn b/ios/showcase/tab_grid/BUILD.gn index 8f0eb14..fb4d36d 100644 --- a/ios/showcase/tab_grid/BUILD.gn +++ b/ios/showcase/tab_grid/BUILD.gn
@@ -9,6 +9,7 @@ ] deps = [ "//base", + "//ios/chrome/browser/ui/commands:commands_clean_skeleton", "//ios/chrome/browser/ui/tab_grid:tab_grid_ui", "//ios/showcase/common", ]
diff --git a/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm b/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm index 59cb68b..453052a 100644 --- a/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm +++ b/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
@@ -5,25 +5,35 @@ #import "ios/showcase/tab_grid/sc_tab_grid_coordinator.h" #import "base/format_macros.h" +#import "ios/chrome/browser/ui/commands/tab_commands.h" #import "ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h" +#import "ios/showcase/common/protocol_alerter.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif -@interface SCTabGridCoordinator ()<TabGridDataSource, TabGridActionDelegate> +@interface SCTabGridCoordinator ()<TabGridDataSource> @property(nonatomic, strong) TabGridViewController* viewController; +@property(nonatomic, strong) ProtocolAlerter* alerter; @end @implementation SCTabGridCoordinator @synthesize baseViewController = _baseViewController; @synthesize viewController = _viewController; +@synthesize alerter = _alerter; - (void)start { + self.alerter = + [[ProtocolAlerter alloc] initWithProtocols:@[ @protocol(TabCommands) ]]; + self.alerter.baseViewController = self.baseViewController; + self.viewController = [[TabGridViewController alloc] init]; self.viewController.title = @"Tab Grid"; self.viewController.dataSource = self; - self.viewController.actionDelegate = self; + self.viewController.tabCommandHandler = + static_cast<id<TabCommands>>(self.alerter); + [self.baseViewController setHidesBarsOnSwipe:YES]; [self.baseViewController pushViewController:self.viewController animated:YES]; } @@ -38,44 +48,4 @@ return [NSString stringWithFormat:@"Tab %" PRIdNS, index]; } -#pragma mark - TabGridActionDelegate - -- (void)showTabAtIndexPath:(NSIndexPath*)indexPath { - [self - showAlertWithTitle:NSStringFromProtocol(@protocol(TabGridActionDelegate)) - message:[NSString - stringWithFormat:@"showTabAtIndexPath:%" PRIdNS, - indexPath.item]]; -} - -- (void)showTabGrid { - [self - showAlertWithTitle:NSStringFromProtocol(@protocol(TabGridActionDelegate)) - message:NSStringFromSelector(_cmd)]; -} - -- (void)showSettings { - [self - showAlertWithTitle:NSStringFromProtocol(@protocol(TabGridActionDelegate)) - message:NSStringFromSelector(_cmd)]; -} - -#pragma mark - Private - -// Helper to show simple alert. -- (void)showAlertWithTitle:(NSString*)title message:(NSString*)message { - UIAlertController* alertController = - [UIAlertController alertControllerWithTitle:title - message:message - preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction* action = - [UIAlertAction actionWithTitle:@"Done" - style:UIAlertActionStyleCancel - handler:nil]; - [alertController addAction:action]; - [self.baseViewController presentViewController:alertController - animated:YES - completion:nil]; -} - @end
diff --git a/ios/showcase/tab_grid/sc_tab_grid_egtest.mm b/ios/showcase/tab_grid/sc_tab_grid_egtest.mm index 3521cbdb..68a4ad0a 100644 --- a/ios/showcase/tab_grid/sc_tab_grid_egtest.mm +++ b/ios/showcase/tab_grid/sc_tab_grid_egtest.mm
@@ -22,7 +22,7 @@ performAction:grey_tap()]; [[EarlGrey selectElementWithMatcher:grey_text(@"Tab 0")] performAction:grey_tap()]; - [[EarlGrey selectElementWithMatcher:grey_text(@"TabGridActionDelegate")] + [[EarlGrey selectElementWithMatcher:grey_text(@"TabCommands")] assertWithMatcher:grey_notNil()]; [[EarlGrey selectElementWithMatcher:grey_text(@"Done")] performAction:grey_tap()];
diff --git a/ios/web/public/web_state/ui/crw_native_content.h b/ios/web/public/web_state/ui/crw_native_content.h index 994ba6d..b3f336fe1 100644 --- a/ios/web/public/web_state/ui/crw_native_content.h +++ b/ios/web/public/web_state/ui/crw_native_content.h
@@ -85,6 +85,9 @@ // Called when a snapshot of the content will be taken. - (void)willUpdateSnapshot; +// Notifies the CRWNativeContent that it will be removed from superview. +- (void)willBeDismissed; + // The URL that will be displayed to the user when presenting this native // content. - (GURL)virtualURL;
diff --git a/ios/web/public/web_state/web_state_observer.h b/ios/web/public/web_state/web_state_observer.h index d0b76cd..ca05867 100644 --- a/ios/web/public/web_state/web_state_observer.h +++ b/ios/web/public/web_state/web_state_observer.h
@@ -29,10 +29,6 @@ // load events from WebState. class WebStateObserver { public: - // Key code associated to form events for which the key code is missing or - // irrelevant. - static int kInvalidFormKeyCode; - // Returns the web state associated with this observer. WebState* web_state() const { return web_state_; } @@ -77,12 +73,10 @@ // Called when the user is typing on a form field, with |error| indicating if // there is any error when parsing the form field information. - // |key_code| may be kInvalidFormKeyCode if there is no key code. virtual void FormActivityRegistered(const std::string& form_name, const std::string& field_name, const std::string& type, const std::string& value, - int key_code, bool input_missing) {} // Invoked when new favicon URL candidates are received.
diff --git a/ios/web/public/web_state/web_state_observer_bridge.h b/ios/web/public/web_state/web_state_observer_bridge.h index 2a689f4..a0ba092 100644 --- a/ios/web/public/web_state/web_state_observer_bridge.h +++ b/ios/web/public/web_state/web_state_observer_bridge.h
@@ -53,7 +53,6 @@ fieldName:(const std::string&)fieldName type:(const std::string&)type value:(const std::string&)value - keyCode:(int)keyCode inputMissing:(BOOL)inputMissing; // Invoked by WebStateObserverBridge::FaviconUrlUpdated. @@ -102,7 +101,6 @@ const std::string& field_name, const std::string& type, const std::string& value, - int key_code, bool input_missing) override; void FaviconUrlUpdated(const std::vector<FaviconURL>& candidates) override; void WebStateDestroyed() override;
diff --git a/ios/web/test/data/mojo_test.js b/ios/web/test/data/mojo_test.js index 7b3a9e4..6c48cca 100644 --- a/ios/web/test/data/mojo_test.js +++ b/ios/web/test/data/mojo_test.js
@@ -13,10 +13,9 @@ return new Promise(function(resolve, reject) { define([ 'mojo/public/js/bindings', - 'mojo/public/js/connection', 'ios/web/test/mojo_test.mojom', 'content/public/renderer/frame_interfaces', - ], function(bindings, connection, mojom, frameInterfaces) { + ], function(bindings, mojom, frameInterfaces) { var pageImpl, browserProxy; /** @constructor */ @@ -35,9 +34,8 @@ }, }; - browserProxy = connection.bindHandleToProxy( - frameInterfaces.getInterface(mojom.TestUIHandlerMojo.name), - mojom.TestUIHandlerMojo); + browserProxy = new mojom.TestUIHandlerMojoPtr( + frameInterfaces.getInterface(mojom.TestUIHandlerMojo.name)); pageImpl = new TestPageImpl(); browserProxy.setClientPage(pageImpl.binding.createInterfacePtrAndBind());
diff --git a/ios/web/web_state/js/resources/core.js b/ios/web/web_state/js/resources/core.js index 6ab7caca..2d6b86c 100644 --- a/ios/web/web_state/js/resources/core.js +++ b/ios/web/web_state/js/resources/core.js
@@ -293,8 +293,6 @@ 'type': evt.type, 'value': value }; - if (evt.keyCode) - msg.keyCode = evt.keyCode; invokeOnHost_(msg); }; @@ -648,7 +646,6 @@ var form = evt.target; var targetsFrame = form.target && hasFrame_(window, form.target); - // TODO(stuartmorgan): Handle external targets. crbug.com/233543 var action = form.getAttribute('action'); // Default action is to re-submit to same page.
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 9862aab..f2e9226 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -2903,7 +2903,6 @@ std::string fieldName; std::string type; std::string value; - int keyCode = web::WebStateObserver::kInvalidFormKeyCode; bool inputMissing = false; if (!message->GetString("formName", &formName) || !message->GetString("fieldName", &fieldName) || @@ -2912,14 +2911,8 @@ inputMissing = true; } - double keyCodeAsDouble = 0; - if (!message->GetDouble("keyCode", &keyCodeAsDouble) || keyCodeAsDouble < 0) { - keyCode = web::WebStateObserver::kInvalidFormKeyCode; - } else { - keyCode = static_cast<int>(keyCodeAsDouble); - } _webStateImpl->OnFormActivityRegistered(formName, fieldName, type, value, - keyCode, inputMissing); + inputMissing); return YES; } @@ -5741,7 +5734,6 @@ } - (void)removeObserver:(id<CRWWebControllerObserver>)observer { - // TODO(jimblackler): make _observers use NSMapTable. crbug.com/367992 DCHECK([_observers containsObject:observer]); [_observers removeObject:observer]; // Remove the associated WebControllerObserverBridge.
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.mm b/ios/web/web_state/ui/crw_web_controller_container_view.mm index 25e31bde..87e1363 100644 --- a/ios/web/web_state/ui/crw_web_controller_container_view.mm +++ b/ios/web/web_state/ui/crw_web_controller_container_view.mm
@@ -172,6 +172,9 @@ - (void)setNativeController:(id<CRWNativeContent>)nativeController { if (![_nativeController isEqual:nativeController]) { base::WeakNSProtocol<id> oldController(_nativeController); + if ([oldController respondsToSelector:@selector(willBeDismissed)]) { + [oldController willBeDismissed]; + } [[oldController view] removeFromSuperview]; _nativeController.reset([nativeController retain]); // TODO(crbug.com/503297): Re-enable this DCHECK once native controller
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm index 4f6fd35..69c625ca 100644 --- a/ios/web/web_state/ui/crw_web_controller_unittest.mm +++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -27,6 +27,7 @@ #import "ios/web/public/web_state/ui/crw_native_content_provider.h" #import "ios/web/public/web_state/ui/crw_web_view_content_view.h" #include "ios/web/public/web_state/url_verification_constants.h" +#include "ios/web/public/web_state/web_state_observer.h" #import "ios/web/test/web_test_with_web_controller.h" #import "ios/web/test/wk_web_view_crash_utils.h" #include "ios/web/web_state/blocked_popup_info.h" @@ -771,6 +772,41 @@ web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP); } +// Real WKWebView is required for CRWWebControllerFormActivityTest. +typedef web::WebTestWithWebController CRWWebControllerFormActivityTest; + +// Tests that keyup event correctly delivered to WebStateObserver. +TEST_F(CRWWebControllerFormActivityTest, KeyUpEvent) { + // Observes and verifies FormActivityRegistered call. + class FormActivityObserver : public web::WebStateObserver { + public: + explicit FormActivityObserver(web::WebState* web_state) + : web::WebStateObserver(web_state) {} + bool form_activity_registered() const { return form_activity_registered_; } + // WebStateObserver overrides: + void FormActivityRegistered(const std::string& form_name, + const std::string& field_name, + const std::string& type, + const std::string& value, + bool input_missing) override { + EXPECT_EQ("keyup", type); + EXPECT_FALSE(input_missing); + form_activity_registered_ = true; + } + + private: + bool form_activity_registered_ = false; + }; + FormActivityObserver form_activity_observer(web_state()); + FormActivityObserver& form_activity_observer_ref(form_activity_observer); + + LoadHtml(@"<p></p>"); + ExecuteJavaScript(@"document.dispatchEvent(new KeyboardEvent('keyup'));"); + base::test::ios::WaitUntilCondition(^{ + return form_activity_observer_ref.form_activity_registered(); + }); +} + // Real WKWebView is required for CRWWebControllerJSExecutionTest. typedef web::WebTestWithWebController CRWWebControllerJSExecutionTest;
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h index e511ffd..cb86de42 100644 --- a/ios/web/web_state/web_state_impl.h +++ b/ios/web/web_state/web_state_impl.h
@@ -114,7 +114,6 @@ const std::string& field_name, const std::string& type, const std::string& value, - int key_code, bool input_missing); // Called when new FaviconURL candidates are received.
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm index 42a89257..46897fa 100644 --- a/ios/web/web_state/web_state_impl.mm +++ b/ios/web/web_state/web_state_impl.mm
@@ -239,11 +239,10 @@ const std::string& field_name, const std::string& type, const std::string& value, - int key_code, bool input_missing) { for (auto& observer : observers_) { observer.FormActivityRegistered(form_name, field_name, type, value, - key_code, input_missing); + input_missing); } }
diff --git a/ios/web/web_state/web_state_observer.mm b/ios/web/web_state/web_state_observer.mm index dcb0204..6c250e9 100644 --- a/ios/web/web_state/web_state_observer.mm +++ b/ios/web/web_state/web_state_observer.mm
@@ -9,8 +9,6 @@ namespace web { -int WebStateObserver::kInvalidFormKeyCode = -1; - WebStateObserver::WebStateObserver(WebState* web_state) : web_state_(nullptr) { Observe(web_state); }
diff --git a/ios/web/web_state/web_state_observer_bridge.mm b/ios/web/web_state/web_state_observer_bridge.mm index 292470c..4ff34c2d 100644 --- a/ios/web/web_state/web_state_observer_bridge.mm +++ b/ios/web/web_state/web_state_observer_bridge.mm
@@ -85,14 +85,12 @@ const std::string& field_name, const std::string& type, const std::string& value, - int key_code, bool input_missing) { SEL selector = @selector(webState: didRegisterFormActivityWithFormNamed: fieldName: type: value: - keyCode: inputMissing:); if ([observer_ respondsToSelector:selector]) { [observer_ webState:web_state() @@ -100,7 +98,6 @@ fieldName:field_name type:type value:value - keyCode:key_code inputMissing:input_missing]; } }
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn index 2111b9e..96bdca3 100644 --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn
@@ -365,6 +365,7 @@ "//base", "//base/test:test_support", "//media", + "//media/base:test_support", "//testing/gtest", "//ui/base", "//ui/display/manager",
diff --git a/media/gpu/dxva_video_decode_accelerator_win.cc b/media/gpu/dxva_video_decode_accelerator_win.cc index 1d6919b..cd85fbc 100644 --- a/media/gpu/dxva_video_decode_accelerator_win.cc +++ b/media/gpu/dxva_video_decode_accelerator_win.cc
@@ -1154,11 +1154,14 @@ main_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&DXVAVideoDecodeAccelerator::NotifyResetDone, weak_ptr_)); + main_thread_task_runner_->PostTask( + FROM_HERE, + base::Bind(&DXVAVideoDecodeAccelerator::NotifyInputBuffersDropped, + weak_ptr_, std::move(pending_input_buffers_))); + pending_input_buffers_.clear(); StartDecoderThread(); SetState(kNormal); - - NotifyInputBuffersDropped(); } void DXVAVideoDecodeAccelerator::Destroy() { @@ -2064,19 +2067,18 @@ } } -void DXVAVideoDecodeAccelerator::NotifyInputBuffersDropped() { +void DXVAVideoDecodeAccelerator::NotifyInputBuffersDropped( + const PendingInputs& pending_buffers) { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); if (!client_) return; - for (PendingInputs::iterator it = pending_input_buffers_.begin(); - it != pending_input_buffers_.end(); ++it) { + for (const auto& buffer : pending_buffers) { LONGLONG input_buffer_id = 0; - RETURN_ON_HR_FAILURE((*it)->GetSampleTime(&input_buffer_id), + RETURN_ON_HR_FAILURE(buffer->GetSampleTime(&input_buffer_id), "Failed to get buffer id associated with sample", ); client_->NotifyEndOfBitstreamBuffer(input_buffer_id); } - pending_input_buffers_.clear(); } void DXVAVideoDecodeAccelerator::DecodePendingInputBuffers() {
diff --git a/media/gpu/dxva_video_decode_accelerator_win.h b/media/gpu/dxva_video_decode_accelerator_win.h index a4df35c..9706516 100644 --- a/media/gpu/dxva_video_decode_accelerator_win.h +++ b/media/gpu/dxva_video_decode_accelerator_win.h
@@ -147,6 +147,7 @@ friend class PbufferPictureBuffer; typedef void* EGLConfig; typedef void* EGLSurface; + typedef std::list<base::win::ScopedComPtr<IMFSample>> PendingInputs; // Returns the minimum resolution for the |profile| passed in. static std::pair<int, int> GetMinResolution(const VideoCodecProfile profile); @@ -244,7 +245,7 @@ // Sends pending input buffer processed acks to the client if we don't have // output samples waiting to be processed. - void NotifyInputBuffersDropped(); + void NotifyInputBuffersDropped(const PendingInputs& input_buffers); // Decodes pending input buffers. void DecodePendingInputBuffers(); @@ -468,7 +469,6 @@ uint32_t output_array_size_ = 0; // List of input samples waiting to be processed. - typedef std::list<base::win::ScopedComPtr<IMFSample>> PendingInputs; PendingInputs pending_input_buffers_; // Callback to get current GLContext.
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc index ed295a1..9611b14 100644 --- a/media/gpu/video_decode_accelerator_unittest.cc +++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -56,6 +56,7 @@ #include "build/build_config.h" #include "gpu/command_buffer/service/gpu_preferences.h" #include "gpu/config/gpu_driver_bug_workarounds.h" +#include "media/base/test_data_util.h" #include "media/filters/h264_parser.h" #include "media/gpu/fake_video_decode_accelerator.h" #include "media/gpu/gpu_video_decode_accelerator_factory.h" @@ -137,6 +138,10 @@ // requesting the VDA itself to allocate buffers. bool g_test_import = false; +// This is the location of the test files. If empty, they're in the current +// working directory. +base::FilePath g_test_file_path; + // Environment to store rendering thread. class VideoDecodeAcceleratorTestEnvironment; VideoDecodeAcceleratorTestEnvironment* g_env; @@ -188,13 +193,19 @@ const gfx::Size kThumbnailSize(160, 120); const int kMD5StringLength = 32; +base::FilePath GetTestDataFile(const base::FilePath& input_file) { + if (input_file.IsAbsolute()) + return input_file; + return base::MakeAbsoluteFilePath(g_test_file_path.Append(input_file)); +} + // Read in golden MD5s for the thumbnailed rendering of this video void ReadGoldenThumbnailMD5s(const TestVideoFile* video_file, std::vector<std::string>* md5_strings) { base::FilePath filepath(video_file->file_name); filepath = filepath.AddExtension(FILE_PATH_LITERAL(".md5")); std::string all_md5s; - base::ReadFileToString(base::MakeAbsoluteFilePath(filepath), &all_md5s); + base::ReadFileToString(GetTestDataFile(filepath), &all_md5s); *md5_strings = base::SplitString(all_md5s, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); // Check these are legitimate MD5s. @@ -1187,7 +1198,7 @@ // Read in the video data. base::FilePath filepath(video_file->file_name); - LOG_ASSERT(base::ReadFileToString(base::MakeAbsoluteFilePath(filepath), + LOG_ASSERT(base::ReadFileToString(GetTestDataFile(filepath), &video_file->data_str)) << "test_video_file: " << filepath.MaybeAsASCII(); @@ -1483,8 +1494,9 @@ base::FilePath filepath(test_video_files_[0]->file_name); filepath = filepath.AddExtension(FILE_PATH_LITERAL(".bad_thumbnails")); filepath = filepath.AddExtension(FILE_PATH_LITERAL(".png")); - int num_bytes = base::WriteFile( - filepath, reinterpret_cast<char*>(&png[0]), png.size()); + int num_bytes = + base::WriteFile(GetTestDataFile(filepath), + reinterpret_cast<char*>(&png[0]), png.size()); ASSERT_EQ(num_bytes, static_cast<int>(png.size())); } ASSERT_NE(match, golden_md5s.end()); @@ -1819,6 +1831,10 @@ media::g_test_import = true; continue; } + if (it->first == "use-test-data-path") { + media::g_test_file_path = media::GetTestDataFilePath(""); + continue; + } } base::ShadowingAtExitManager at_exit_manager;
diff --git a/media/remoting/remote_renderer_impl.cc b/media/remoting/remote_renderer_impl.cc index b87b7f6..b814b00cb 100644 --- a/media/remoting/remote_renderer_impl.cc +++ b/media/remoting/remote_renderer_impl.cc
@@ -35,14 +35,14 @@ rpc_broker_(remoting_renderer_controller_->GetRpcBroker()), rpc_handle_(rpc_broker_->GetUniqueHandle()), remote_renderer_handle_(remoting::kInvalidHandle), - interstitial_ui_(video_renderer_sink, - remoting_renderer_controller->pipeline_metadata()), + video_renderer_sink_(video_renderer_sink), weak_factory_(this) { VLOG(2) << __func__; // The constructor is running on the main thread. - DCHECK(remoting_renderer_controller); - - UpdateInterstitial(); + DCHECK(remoting_renderer_controller_); + remoting_renderer_controller_->SetShowInterstitialCallback( + base::Bind(&RemoteRendererImpl::RequestUpdateInterstitialOnMainThread, + media_task_runner_, weak_factory_.GetWeakPtr())); const remoting::RpcBroker::ReceiveMessageCallback receive_callback = base::Bind(&RemoteRendererImpl::OnMessageReceivedOnMainThread, @@ -53,6 +53,17 @@ RemoteRendererImpl::~RemoteRendererImpl() { VLOG(2) << __func__; DCHECK(media_task_runner_->BelongsToCurrentThread()); + + UpdateInterstitial(interstitial_background_, canvas_size_, + RemotingInterstitialType::BETWEEN_SESSIONS); + + // Post task on main thread to unset the interstial callback. + main_task_runner_->PostTask( + FROM_HERE, + base::Bind(&RemotingRendererController::SetShowInterstitialCallback, + remoting_renderer_controller_, + RemotingRendererController::ShowInterstitialCallback())); + // Post task on main thread to unregister message receiver. main_task_runner_->PostTask( FROM_HERE, @@ -597,10 +608,6 @@ VLOG(2) << __func__ << " with PipelineStatus error=" << error; - main_task_runner_->PostTask( - FROM_HERE, base::Bind(&RemoteRendererImpl::UpdateInterstitial, - weak_factory_.GetWeakPtr())); - const State old_state = state_; state_ = STATE_ERROR; @@ -618,12 +625,29 @@ client_->OnError(error); } -void RemoteRendererImpl::UpdateInterstitial() { - DCHECK(main_task_runner_->BelongsToCurrentThread()); +// static +void RemoteRendererImpl::RequestUpdateInterstitialOnMainThread( + scoped_refptr<base::SingleThreadTaskRunner> media_task_runner, + base::WeakPtr<RemoteRendererImpl> remote_renderer_impl, + const SkBitmap& background_image, + const gfx::Size& canvas_size, + RemotingInterstitialType interstitial_type) { + media_task_runner->PostTask( + FROM_HERE, + base::Bind(&RemoteRendererImpl::UpdateInterstitial, remote_renderer_impl, + background_image, canvas_size, interstitial_type)); +} - interstitial_ui_.ShowInterstitial( - remoting_renderer_controller_->remoting_source()->state() == - RemotingSessionState::SESSION_STARTED); +void RemoteRendererImpl::UpdateInterstitial( + const SkBitmap& background_image, + const gfx::Size& canvas_size, + RemotingInterstitialType interstitial_type) { + DCHECK(media_task_runner_->BelongsToCurrentThread()); + if (!background_image.drawsNothing()) + interstitial_background_ = background_image; + canvas_size_ = canvas_size; + PaintRemotingInterstitial(interstitial_background_, canvas_size_, + interstitial_type, video_renderer_sink_); } } // namespace media
diff --git a/media/remoting/remote_renderer_impl.h b/media/remoting/remote_renderer_impl.h index b557c3d..38947f9 100644 --- a/media/remoting/remote_renderer_impl.h +++ b/media/remoting/remote_renderer_impl.h
@@ -67,6 +67,15 @@ base::WeakPtr<RemoteRendererImpl> self, std::unique_ptr<remoting::pb::RpcMessage> message); + // Callback when remoting interstitial needs to be updated. Will post task to + // media thread to avoid threading race condition. + static void RequestUpdateInterstitialOnMainThread( + scoped_refptr<base::SingleThreadTaskRunner> media_task_runner, + base::WeakPtr<RemoteRendererImpl> remote_renderer_impl, + const SkBitmap& background_image, + const gfx::Size& canvas_size, + RemotingInterstitialType interstitial_type); + public: // media::Renderer implementation. void Initialize(DemuxerStreamProvider* demuxer_stream_provider, @@ -121,12 +130,16 @@ void OnStatisticsUpdate(std::unique_ptr<remoting::pb::RpcMessage> message); void OnDurationChange(std::unique_ptr<remoting::pb::RpcMessage> message); + // Called to update the remoting interstitial. Draw remoting interstitial on + // |interstitial_background_| if |background_image| is empty. Update + // |interstitial_background_| if |background_image| is not empty. + void UpdateInterstitial(const SkBitmap& background_image, + const gfx::Size& canvas_size, + RemotingInterstitialType interstitial_type); + // Shut down remoting session. void OnFatalError(PipelineStatus status); - // Show interstial accordingly. - void UpdateInterstitial(); - State state_; const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; @@ -160,7 +173,12 @@ CdmAttachedCB cdm_attached_cb_; base::Closure flush_cb_; - RemotingInterstitialUI interstitial_ui_; + VideoRendererSink* const video_renderer_sink_; // Outlives this class. + // The background image for remoting interstitial. When |this| is destructed, + // |interstitial_background_| will be paint to clear the cast messages on + // the interstitial. + SkBitmap interstitial_background_; + gfx::Size canvas_size_; base::WeakPtrFactory<RemoteRendererImpl> weak_factory_;
diff --git a/media/remoting/remoting_interstitial_ui.cc b/media/remoting/remoting_interstitial_ui.cc index 9e3f01d..fc0e28c33 100644 --- a/media/remoting/remoting_interstitial_ui.cc +++ b/media/remoting/remoting_interstitial_ui.cc
@@ -8,6 +8,7 @@ #include "media/base/video_frame.h" #include "media/base/video_renderer_sink.h" #include "media/base/video_util.h" +#include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkTypeface.h" #include "third_party/skia/include/effects/SkBlurImageFilter.h" @@ -21,45 +22,31 @@ namespace { -gfx::Size GetRotatedVideoSize(VideoRotation rotation, gfx::Size natural_size) { - if (rotation == VIDEO_ROTATION_90 || rotation == VIDEO_ROTATION_270) - return gfx::Size(natural_size.height(), natural_size.width()); - return natural_size; +SkBitmap ResizeImage(const SkBitmap& image, const gfx::Size& scaled_size) { + DCHECK(!scaled_size.IsEmpty()); + + if (image.width() == scaled_size.width() && + image.height() == scaled_size.height()) + return image; + + return skia::ImageOperations::Resize( + image, skia::ImageOperations::RESIZE_BEST, scaled_size.width(), + scaled_size.height()); } -} // namespace - -RemotingInterstitialUI::RemotingInterstitialUI( - VideoRendererSink* video_renderer_sink, - const PipelineMetadata& pipeline_metadata) - : video_renderer_sink_(video_renderer_sink), - pipeline_metadata_(pipeline_metadata) { - DCHECK(pipeline_metadata_.has_video); - pipeline_metadata_.natural_size = GetRotatedVideoSize( - pipeline_metadata_.video_rotation, pipeline_metadata_.natural_size); -} - -RemotingInterstitialUI::~RemotingInterstitialUI() {} - -scoped_refptr<VideoFrame> RemotingInterstitialUI::GetInterstitial( - const SkBitmap& background_image, - bool is_remoting_successful) { - const gfx::Size canvas_size = - gfx::Size(background_image.width(), background_image.height()); - DCHECK(canvas_size == pipeline_metadata_.natural_size); - - color_utils::HSL shift = {-1, 0, 0.2}; // Make monochromatic. - SkBitmap modified_bitmap = - SkBitmapOperations::CreateHSLShiftedBitmap(background_image, shift); - - SkCanvas canvas(modified_bitmap); +void RenderCastMessage(const gfx::Size& canvas_size, + RemotingInterstitialType type, + SkCanvas* canvas) { + DCHECK(canvas); + if (type == RemotingInterstitialType::BETWEEN_SESSIONS) + return; // Blur the background image. SkScalar sigma = SkDoubleToScalar(10); SkPaint paint_blur; paint_blur.setImageFilter(SkBlurImageFilter::Make(sigma, sigma, nullptr)); - canvas.saveLayer(0, &paint_blur); - canvas.restore(); + canvas->saveLayer(0, &paint_blur); + canvas->restore(); // Create SkPaint for text and icon bitmap. // After |paint| draws, the new canvas should look like this: @@ -84,59 +71,84 @@ // Draw the appropriate text. const std::string remote_playback_message = - is_remoting_successful - ? GetLocalizedStringUTF8(MEDIA_REMOTING_CASTING_VIDEO_TEXT) - : GetLocalizedStringUTF8(MEDIA_REMOTING_CAST_ERROR_TEXT); + (type == RemotingInterstitialType::IN_SESSION + ? GetLocalizedStringUTF8(MEDIA_REMOTING_CASTING_VIDEO_TEXT) + : GetLocalizedStringUTF8(MEDIA_REMOTING_CAST_ERROR_TEXT)); size_t display_text_width = paint.measureText(remote_playback_message.data(), remote_playback_message.size()); SkScalar sk_text_offset_x = (canvas_size.width() - display_text_width) / 2.0; SkScalar sk_text_offset_y = (canvas_size.height() / 2.0) + text_size; - canvas.drawText(remote_playback_message.data(), - remote_playback_message.size(), sk_text_offset_x, - sk_text_offset_y, paint); + canvas->drawText(remote_playback_message.data(), + remote_playback_message.size(), sk_text_offset_x, + sk_text_offset_y, paint); // Draw the appropriate Cast icon. gfx::VectorIconId current_icon = - is_remoting_successful ? gfx::VectorIconId::MEDIA_ROUTER_ACTIVE - : gfx::VectorIconId::MEDIA_ROUTER_WARNING; + (type == RemotingInterstitialType::IN_SESSION + ? gfx::VectorIconId::MEDIA_ROUTER_ACTIVE + : gfx::VectorIconId::MEDIA_ROUTER_WARNING); gfx::ImageSkia icon_image = gfx::CreateVectorIcon(current_icon, 65, SK_ColorLTGRAY); const SkBitmap* icon_bitmap = icon_image.bitmap(); SkScalar sk_image_offset_x = (canvas_size.width() - icon_image.width()) / 2.0; SkScalar sk_image_offset_y = (canvas_size.height() / 2.0) - icon_image.height(); - canvas.drawBitmap(*icon_bitmap, sk_image_offset_x, sk_image_offset_y, &paint); + canvas->drawBitmap(*icon_bitmap, sk_image_offset_x, sk_image_offset_y, + &paint); +} + +scoped_refptr<VideoFrame> RenderInterstitialFrame( + const SkBitmap& image, + const gfx::Size& canvas_size, + RemotingInterstitialType type) { + SkBitmap canvas_bitmap; + canvas_bitmap.allocN32Pixels(canvas_size.width(), canvas_size.height()); + canvas_bitmap.eraseColor(SK_ColorBLACK); + SkCanvas canvas(canvas_bitmap); + + // Draw background image on the canvas. + if (!image.drawsNothing()) { + gfx::Rect centered_rect = ComputeLetterboxRegion( + gfx::Rect(canvas_size), gfx::Size(image.width(), image.height())); + SkBitmap processed_image = ResizeImage(image, centered_rect.size()); + if (type != RemotingInterstitialType::BETWEEN_SESSIONS) { + color_utils::HSL shift = {-1, 0, 0.2}; // Make monochromatic. + processed_image = + SkBitmapOperations::CreateHSLShiftedBitmap(processed_image, shift); + } + canvas.writePixels(processed_image, centered_rect.x(), centered_rect.y()); + } + + RenderCastMessage(canvas_size, type, &canvas); // Create a new VideoFrame, copy the bitmap, then return it. scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame( media::PIXEL_FORMAT_I420, canvas_size, gfx::Rect(canvas_size), canvas_size, base::TimeDelta()); - modified_bitmap.lockPixels(); + canvas_bitmap.lockPixels(); media::CopyRGBToVideoFrame( - reinterpret_cast<uint8_t*>(modified_bitmap.getPixels()), - modified_bitmap.rowBytes(), + reinterpret_cast<uint8_t*>(canvas_bitmap.getPixels()), + canvas_bitmap.rowBytes(), gfx::Rect(canvas_size.width(), canvas_size.height()), video_frame.get()); - modified_bitmap.unlockPixels(); + canvas_bitmap.unlockPixels(); return video_frame; } -void RemotingInterstitialUI::ShowInterstitial(bool is_remoting_successful) { - if (!video_renderer_sink_) - return; +} // namespace - // TODO(xjz): Provide poster image, if available, rather than a blank, black - // image. - SkBitmap background_image; - const gfx::Size size = pipeline_metadata_.natural_size; - background_image.allocN32Pixels(size.width(), size.height()); - background_image.eraseColor(SK_ColorBLACK); +void PaintRemotingInterstitial(const SkBitmap& image, + const gfx::Size& canvas_size, + RemotingInterstitialType interstitial_type, + VideoRendererSink* video_renderer_sink) { + if (canvas_size.IsEmpty()) + return; const scoped_refptr<VideoFrame> interstitial = - GetInterstitial(background_image, is_remoting_successful); + RenderInterstitialFrame(image, canvas_size, interstitial_type); + if (!interstitial) return; - - video_renderer_sink_->PaintSingleFrame(interstitial); + video_renderer_sink->PaintSingleFrame(interstitial); } } // namespace media
diff --git a/media/remoting/remoting_interstitial_ui.h b/media/remoting/remoting_interstitial_ui.h index 7807628..51c0a730 100644 --- a/media/remoting/remoting_interstitial_ui.h +++ b/media/remoting/remoting_interstitial_ui.h
@@ -5,36 +5,33 @@ #ifndef MEDIA_REMOTING_REMOTING_INTERSTITIAL_UI_H_ #define MEDIA_REMOTING_REMOTING_INTERSTITIAL_UI_H_ -#include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "media/base/pipeline_metadata.h" #include "third_party/skia/include/core/SkBitmap.h" +namespace gfx { +class Size; +} + namespace media { -class VideoFrame; class VideoRendererSink; -class RemotingInterstitialUI { - public: - RemotingInterstitialUI(VideoRendererSink* video_renderer_sink, - const PipelineMetadata& pipeline_metadata); - ~RemotingInterstitialUI(); - - void ShowInterstitial(bool is_remoting_successful); - - private: - // Gets an 'interstitial' VideoFrame to paint on the media player when the - // video is being played remotely. - scoped_refptr<VideoFrame> GetInterstitial(const SkBitmap& background_image, - bool is_remoting_successful); - - VideoRendererSink* const video_renderer_sink_; // Outlives this class. - PipelineMetadata pipeline_metadata_; - - DISALLOW_COPY_AND_ASSIGN(RemotingInterstitialUI); +enum RemotingInterstitialType { + BETWEEN_SESSIONS, // Show background image only. + IN_SESSION, // Show MEDIA_REMOTING_CASTING_VIDEO_TEXT. + ENCRYPTED_MEDIA_FATAL_ERROR, // Show MEDIA_REMOTING_CAST_ERROR_TEXT. }; +// Paint an interstitial frame and send it to the given VideoRendererSink. +// |background_image| will be scaled to fit in |canvas_size|, but keep its +// aspect ratio. When has different aspect ratio with |canvas_size|, scaled +// |background_image| will be centered in the canvas. When |background_image| +// is empty, interstitial will be drawn on a blank and black background. When +// |interstial_type| is BETWEEN_SESSIONS, show background image only. +void PaintRemotingInterstitial(const SkBitmap& background_image, + const gfx::Size& canvas_size, + RemotingInterstitialType interstitial_type, + VideoRendererSink* video_renderer_sink); + } // namespace media #endif // MEDIA_REMOTING_REMOTING_INTERSTITIAL_UI_H_
diff --git a/media/remoting/remoting_renderer_controller.cc b/media/remoting/remoting_renderer_controller.cc index c58c17a..0464d9ed 100644 --- a/media/remoting/remoting_renderer_controller.cc +++ b/media/remoting/remoting_renderer_controller.cc
@@ -7,10 +7,21 @@ #include "base/bind.h" #include "base/logging.h" #include "base/threading/thread_checker.h" +#include "media/base/video_util.h" #include "media/remoting/remoting_cdm_context.h" namespace media { +namespace { + +gfx::Size GetRotatedVideoSize(VideoRotation rotation, gfx::Size natural_size) { + if (rotation == VIDEO_ROTATION_90 || rotation == VIDEO_ROTATION_270) + return gfx::Size(natural_size.height(), natural_size.width()); + return natural_size; +} + +} // namespace + RemotingRendererController::RemotingRendererController( scoped_refptr<RemotingSourceImpl> remoting_source) : remoting_source_(remoting_source), weak_factory_(this) { @@ -46,6 +57,7 @@ if (!sink_available_changed_cb_.is_null()) sink_available_changed_cb_.Run(IsRemoteSinkAvailable()); + UpdateInterstitial(); UpdateAndMaybeSwitch(); } @@ -146,17 +158,22 @@ const PipelineMetadata& metadata) { DCHECK(thread_checker_.CalledOnValidThread()); + const gfx::Size old_size = pipeline_metadata_.natural_size; pipeline_metadata_ = metadata; is_encrypted_ = false; if (has_video()) { - video_decoder_config_ = metadata.video_decoder_config; - is_encrypted_ |= video_decoder_config_.is_encrypted(); + is_encrypted_ |= metadata.video_decoder_config.is_encrypted(); + pipeline_metadata_.natural_size = GetRotatedVideoSize( + pipeline_metadata_.video_rotation, pipeline_metadata_.natural_size); } if (has_audio()) { - audio_decoder_config_ = metadata.audio_decoder_config; - is_encrypted_ |= audio_decoder_config_.is_encrypted(); + is_encrypted_ |= metadata.audio_decoder_config.is_encrypted(); } + + if (pipeline_metadata_.natural_size != old_size) + UpdateInterstitial(); + UpdateAndMaybeSwitch(); } @@ -164,13 +181,13 @@ DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(has_video()); - switch (video_decoder_config_.codec()) { + switch (pipeline_metadata_.video_decoder_config.codec()) { case VideoCodec::kCodecH264: case VideoCodec::kCodecVP8: return true; default: VLOG(2) << "Remoting does not support video codec: " - << video_decoder_config_.codec(); + << pipeline_metadata_.video_decoder_config.codec(); return false; } } @@ -179,7 +196,7 @@ DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(has_audio()); - switch (audio_decoder_config_.codec()) { + switch (pipeline_metadata_.audio_decoder_config.codec()) { case AudioCodec::kCodecAAC: case AudioCodec::kCodecMP3: case AudioCodec::kCodecPCM: @@ -199,7 +216,7 @@ return true; default: VLOG(2) << "Remoting does not support audio codec: " - << audio_decoder_config_.codec(); + << pipeline_metadata_.audio_decoder_config.codec(); return false; } } @@ -309,4 +326,36 @@ } } +void RemotingRendererController::SetShowInterstitialCallback( + const ShowInterstitialCallback& cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + show_interstitial_cb_ = cb; + UpdateInterstitial(); +} + +void RemotingRendererController::UpdateInterstitial() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (show_interstitial_cb_.is_null() || + pipeline_metadata_.natural_size.IsEmpty()) + return; + + RemotingInterstitialType type = RemotingInterstitialType::BETWEEN_SESSIONS; + switch (remoting_source_->state()) { + case SESSION_STARTED: + type = RemotingInterstitialType::IN_SESSION; + break; + case SESSION_PERMANENTLY_STOPPED: + type = RemotingInterstitialType::ENCRYPTED_MEDIA_FATAL_ERROR; + break; + case SESSION_UNAVAILABLE: + case SESSION_CAN_START: + case SESSION_STARTING: + case SESSION_STOPPING: + break; + } + + // TODO(xjz): Download poster image when available. + show_interstitial_cb_.Run(SkBitmap(), pipeline_metadata_.natural_size, type); +} + } // namespace media
diff --git a/media/remoting/remoting_renderer_controller.h b/media/remoting/remoting_renderer_controller.h index c3a35bd..797e44f 100644 --- a/media/remoting/remoting_renderer_controller.h +++ b/media/remoting/remoting_renderer_controller.h
@@ -8,6 +8,7 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "media/base/media_observer.h" +#include "media/remoting/remoting_interstitial_ui.h" #include "media/remoting/remoting_source_impl.h" namespace media { @@ -46,6 +47,12 @@ void SetRemoteSinkAvailableChangedCallback( const base::Callback<void(bool)>& cb); + using ShowInterstitialCallback = base::Callback< + void(const SkBitmap&, const gfx::Size&, RemotingInterstitialType type)>; + // Called by RemoteRendererImpl constructor to set the callback to draw and + // show remoting interstial. + void SetShowInterstitialCallback(const ShowInterstitialCallback& cb); + base::WeakPtr<RemotingRendererController> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } @@ -70,11 +77,6 @@ base::WeakPtr<remoting::RpcBroker> GetRpcBroker() const; - PipelineMetadata pipeline_metadata() const { - DCHECK(thread_checker_.CalledOnValidThread()); - return pipeline_metadata_; - } - private: bool has_audio() const { return pipeline_metadata_.has_audio && @@ -97,6 +99,17 @@ // necessary. void UpdateAndMaybeSwitch(); + // Called when any of the following happens: + // 1. SetShowInterstitialCallback() is called (RemoteRendererImpl is + // constructed); + // 2. The remoting session is shut down (to update the status message in the + // interstitial). + // 3. The size of the canvas is changed (to update the background image and + // the position of the status message). + // TODO(xjz): Call this when poster url is set/changed. Download poster image + // when available, and draw interstitial on it. + void UpdateInterstitial(); + // Indicates whether this media element or its ancestor is in full screen. bool is_fullscreen_ = false; @@ -106,10 +119,6 @@ // Indicates whether audio or video is encrypted. bool is_encrypted_ = false; - // Current audio/video config. - VideoDecoderConfig video_decoder_config_; - AudioDecoderConfig audio_decoder_config_; - // Indicates whether remote playback is currently disabled. This starts out as // true, and should be updated at least once via a call to // OnRemotePlaybackDisabled() at some point in the future. A web page @@ -141,6 +150,11 @@ PipelineMetadata pipeline_metadata_; + // The callback to show remoting interstitial. It is set when entering the + // remoting mode (RemotingRendererImpl is constructed) by calling + // SetShowInterstitialCallback(), and is reset when leaving the remoting mode. + ShowInterstitialCallback show_interstitial_cb_; + base::WeakPtrFactory<RemotingRendererController> weak_factory_; DISALLOW_COPY_AND_ASSIGN(RemotingRendererController);
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc index 758d96c..5cf70e5 100644 --- a/mojo/edk/embedder/embedder.cc +++ b/mojo/edk/embedder/embedder.cc
@@ -5,6 +5,7 @@ #include "mojo/edk/embedder/embedder.h" #include <stdint.h> +#include <utility> #include "base/bind.h" #include "base/location.h"
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc index f37d42ef..cfcb777 100644 --- a/mojo/edk/system/core.cc +++ b/mojo/edk/system/core.cc
@@ -45,8 +45,8 @@ // This is an unnecessarily large limit that is relatively easy to enforce. const uint32_t kMaxHandlesPerMessage = 1024 * 1024; -// TODO: Maybe we could negotiate a debugging pipe ID for cross-process pipes -// too; for now we just use a constant. This only affects bootstrap pipes. +// TODO(rockot): Maybe we could negotiate a debugging pipe ID for cross-process +// pipes too; for now we just use a constant. This only affects bootstrap pipes. const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL; void CallWatchCallback(MojoWatchCallback callback, @@ -815,12 +815,12 @@ create_options.struct_size = sizeof(MojoCreateDataPipeOptions); create_options.flags = options ? options->flags : 0; create_options.element_num_bytes = options ? options->element_num_bytes : 1; - // TODO: Use Configuration to get default data pipe capacity. + // TODO(rockot): Use Configuration to get default data pipe capacity. create_options.capacity_num_bytes = options && options->capacity_num_bytes ? options->capacity_num_bytes : 64 * 1024; - // TODO: Broker through the parent when necessary. + // TODO(rockot): Broker through the parent when necessary. scoped_refptr<PlatformSharedBuffer> ring_buffer = GetNodeController()->CreateSharedBuffer( create_options.capacity_num_bytes); @@ -1126,7 +1126,7 @@ const MojoHandleSignals* signals, uint32_t num_handles, MojoDeadline deadline, - uint32_t *result_index, + uint32_t* result_index, HandleSignalsState* signals_states) { CHECK(handles); CHECK(signals);
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h index e32e2d2..21b44b1 100644 --- a/mojo/edk/system/core.h +++ b/mojo/edk/system/core.h
@@ -6,6 +6,7 @@ #define MOJO_EDK_SYSTEM_CORE_H_ #include <memory> +#include <string> #include <vector> #include "base/callback.h" @@ -39,7 +40,7 @@ // are thread-safe. class MOJO_SYSTEM_IMPL_EXPORT Core { public: - explicit Core(); + Core(); virtual ~Core(); // Called exactly once, shortly after construction, and before any other @@ -278,7 +279,7 @@ const MojoHandleSignals* signals, uint32_t num_handles, MojoDeadline deadline, - uint32_t *result_index, + uint32_t* result_index, HandleSignalsState* signals_states); // Used to pass ownership of our NodeController over to the IO thread in the
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc index aa19c606..11bb405 100644 --- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc +++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -443,7 +443,8 @@ } char message[128]; - sprintf(message, "hello %d", static_cast<int>(pipe_count)); + snprintf(message, sizeof(message), "hello %d", + static_cast<int>(pipe_count)); ASSERT_EQ(MOJO_RESULT_OK, MojoWriteMessage(h, message, static_cast<uint32_t>(strlen(message)), @@ -465,9 +466,9 @@ #if !defined(OS_ANDROID) INSTANTIATE_TEST_CASE_P(PipeCount, MultiprocessMessagePipeTestWithPipeCount, - // TODO: Re-enable the 140-pipe case when ChannelPosix - // has support for sending lots of handles. - testing::Values(1u, 128u/*, 140u*/)); + // TODO(rockot): Re-enable the 140-pipe case when + // ChannelPosix has support for sending lots of handles. + testing::Values(1u, 128u /*, 140u*/)); #endif DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) { @@ -997,7 +998,7 @@ } } - for (auto& pipe: named_pipes) + for (auto& pipe : named_pipes) CloseHandle(pipe.second); return 0;
diff --git a/net/quic/chromium/quic_test_packet_maker.cc b/net/quic/chromium/quic_test_packet_maker.cc index cd0941a5..43a99db 100644 --- a/net/quic/chromium/quic_test_packet_maker.cc +++ b/net/quic/chromium/quic_test_packet_maker.cc
@@ -641,7 +641,7 @@ bool should_include_version, QuicStreamOffset* offset) { SpdySettingsIR settings_frame; - settings_frame.AddSetting(id, false, false, value); + settings_frame.AddSetting(id, value); SpdySerializedFrame spdy_frame( spdy_request_framer_.SerializeFrame(settings_frame)); InitializeHeader(packet_number, should_include_version);
diff --git a/net/quic/core/quic_headers_stream.cc b/net/quic/core/quic_headers_stream.cc index 6c162fd5..0ea432a 100644 --- a/net/quic/core/quic_headers_stream.cc +++ b/net/quic/core/quic_headers_stream.cc
@@ -168,7 +168,7 @@ CloseConnection("SPDY RST_STREAM frame received."); } - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override { + void OnSetting(SpdySettingsIds id, uint32_t value) override { if (!FLAGS_quic_respect_http2_settings_frame) { CloseConnection("SPDY SETTINGS frame received."); return; @@ -590,7 +590,7 @@ size_t QuicHeadersStream::SendMaxHeaderListSize(size_t value) { SpdySettingsIR settings_frame; - settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, false, false, value); + settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, value); SpdySerializedFrame frame(spdy_framer_.SerializeFrame(settings_frame)); WriteOrBufferData(StringPiece(frame.data(), frame.size()), false, nullptr);
diff --git a/net/quic/core/quic_headers_stream_test.cc b/net/quic/core/quic_headers_stream_test.cc index 2feb739..73ca0a93 100644 --- a/net/quic/core/quic_headers_stream_test.cc +++ b/net/quic/core/quic_headers_stream_test.cc
@@ -80,8 +80,7 @@ MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id, SpdyRstStreamStatus status)); MOCK_METHOD1(OnSettings, void(bool clear_persisted)); - MOCK_METHOD3(OnSetting, - void(SpdySettingsIds id, uint8_t flags, uint32_t value)); + MOCK_METHOD2(OnSetting, void(SpdySettingsIds id, uint32_t value)); MOCK_METHOD0(OnSettingsAck, void()); MOCK_METHOD0(OnSettingsEnd, void()); MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); @@ -539,7 +538,7 @@ session_.OnConfigNegotiated(); SpdySettingsIR data; // Respect supported settings frames SETTINGS_ENABLE_PUSH. - data.AddSetting(SETTINGS_ENABLE_PUSH, true, true, 0); + data.AddSetting(SETTINGS_ENABLE_PUSH, 0); SpdySerializedFrame frame(framer_->SerializeFrame(data)); stream_frame_.data_buffer = frame.data(); stream_frame_.data_length = frame.size(); @@ -734,7 +733,7 @@ TEST_P(QuicHeadersStreamTest, ProcessSpdySettingsFrame) { FLAGS_quic_respect_http2_settings_frame = false; SpdySettingsIR data; - data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, true, true, 0); + data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 0); SpdySerializedFrame frame(framer_->SerializeFrame(data)); EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, "SPDY SETTINGS frame received.", _)) @@ -752,8 +751,8 @@ SpdySettingsIR data; // Respect supported settings frames SETTINGS_HEADER_TABLE_SIZE, // SETTINGS_MAX_HEADER_LIST_SIZE. - data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, true, true, kTestHeaderTableSize); - data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, true, true, 2000); + data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, kTestHeaderTableSize); + data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, 2000); SpdySerializedFrame frame(framer_->SerializeFrame(data)); stream_frame_.data_buffer = frame.data(); stream_frame_.data_length = frame.size(); @@ -770,10 +769,10 @@ // Does not support SETTINGS_MAX_CONCURRENT_STREAMS, // SETTINGS_INITIAL_WINDOW_SIZE, SETTINGS_ENABLE_PUSH and // SETTINGS_MAX_FRAME_SIZE. - data.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, true, true, 100); - data.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, true, true, 100); - data.AddSetting(SETTINGS_ENABLE_PUSH, true, true, 1); - data.AddSetting(SETTINGS_MAX_FRAME_SIZE, true, true, 1250); + data.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 100); + data.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 100); + data.AddSetting(SETTINGS_ENABLE_PUSH, 1); + data.AddSetting(SETTINGS_MAX_FRAME_SIZE, 1250); SpdySerializedFrame frame(framer_->SerializeFrame(data)); EXPECT_CALL( *connection_,
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc index a1479c0b..c8fa123e 100644 --- a/net/spdy/buffered_spdy_framer.cc +++ b/net/spdy/buffered_spdy_framer.cc
@@ -130,10 +130,8 @@ visitor_->OnSettings(); } -void BufferedSpdyFramer::OnSetting(SpdySettingsIds id, - uint8_t flags, - uint32_t value) { - visitor_->OnSetting(id, flags, value); +void BufferedSpdyFramer::OnSetting(SpdySettingsIds id, uint32_t value) { + visitor_->OnSetting(id, value); } void BufferedSpdyFramer::OnSettingsAck() { @@ -251,14 +249,9 @@ SpdySerializedFrame* BufferedSpdyFramer::CreateSettings( const SettingsMap& values) const { SpdySettingsIR settings_ir; - for (SettingsMap::const_iterator it = values.begin(); - it != values.end(); + for (SettingsMap::const_iterator it = values.begin(); it != values.end(); ++it) { - settings_ir.AddSetting( - it->first, - (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0, - (it->second.first & SETTINGS_FLAG_PERSISTED) != 0, - it->second.second); + settings_ir.AddSetting(it->first, it->second); } return new SpdySerializedFrame(spdy_framer_.SerializeSettings(settings_ir)); }
diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h index f6cb978..8f5b1ec 100644 --- a/net/spdy/buffered_spdy_framer.h +++ b/net/spdy/buffered_spdy_framer.h
@@ -68,7 +68,7 @@ // Called when an individual setting within a SETTINGS frame has been parsed // and validated. - virtual void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) = 0; + virtual void OnSetting(SpdySettingsIds id, uint32_t value) = 0; // Called when a SETTINGS frame is received with the ACK flag set. virtual void OnSettingsAck() {} @@ -151,7 +151,7 @@ SpdyStreamId stream_id) override; void OnHeaderFrameEnd(SpdyStreamId stream_id, bool end_headers) override; void OnSettings(bool clear_persisted) override; - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override; + void OnSetting(SpdySettingsIds id, uint32_t value) override; void OnSettingsAck() override; void OnSettingsEnd() override; void OnPing(SpdyPingId unique_id, bool is_ack) override;
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc index 82a6637..a5bf1c1f 100644 --- a/net/spdy/buffered_spdy_framer_unittest.cc +++ b/net/spdy/buffered_spdy_framer_unittest.cc
@@ -4,6 +4,7 @@ #include "net/spdy/buffered_spdy_framer.h" +#include <algorithm> #include <utility> #include "base/logging.h" @@ -16,7 +17,7 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface { public: - explicit TestBufferedSpdyVisitor() + TestBufferedSpdyVisitor() : buffered_spdy_framer_(), error_count_(0), setting_count_(0), @@ -74,7 +75,7 @@ void OnSettings() override {} - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override { + void OnSetting(SpdySettingsIds id, uint32_t value) override { setting_count_++; } @@ -181,8 +182,8 @@ TEST_F(BufferedSpdyFramerTest, OnSetting) { SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); SpdySettingsIR settings_ir; - settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, false, false, 2); - settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, false, false, 3); + settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 2); + settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 3); SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir)); TestBufferedSpdyVisitor visitor;
diff --git a/net/spdy/hpack/hpack_encoder.cc b/net/spdy/hpack/hpack_encoder.cc index f186b6a4..0ec5e8f 100644 --- a/net/spdy/hpack/hpack_encoder.cc +++ b/net/spdy/hpack/hpack_encoder.cc
@@ -157,17 +157,22 @@ } void HpackEncoder::EmitIndex(const HpackEntry* entry) { + DVLOG(2) << "Emitting index " << header_table_.IndexOf(entry); output_stream_.AppendPrefix(kIndexedOpcode); output_stream_.AppendUint32(header_table_.IndexOf(entry)); } void HpackEncoder::EmitIndexedLiteral(const Representation& representation) { + DVLOG(2) << "Emitting indexed literal: (" << representation.first << ", " + << representation.second << ")"; output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode); EmitLiteral(representation); header_table_.TryAddEntry(representation.first, representation.second); } void HpackEncoder::EmitNonIndexedLiteral(const Representation& representation) { + DVLOG(2) << "Emitting nonindexed literal: (" << representation.first << ", " + << representation.second << ")"; output_stream_.AppendPrefix(kLiteralNoIndexOpcode); output_stream_.AppendUint32(0); EmitString(representation.first); @@ -189,10 +194,12 @@ size_t encoded_size = enable_compression_ ? huffman_table_.EncodedSize(str) : str.size(); if (encoded_size < str.size()) { + DVLOG(2) << "Emitted Huffman-encoded string of length " << encoded_size; output_stream_.AppendPrefix(kStringLiteralHuffmanEncoded); output_stream_.AppendUint32(encoded_size); huffman_table_.EncodeString(str, &output_stream_); } else { + DVLOG(2) << "Emitted literal string of length " << str.size(); output_stream_.AppendPrefix(kStringLiteralIdentityEncoded); output_stream_.AppendUint32(str.size()); output_stream_.AppendBytes(str);
diff --git a/net/spdy/hpack/hpack_entry.cc b/net/spdy/hpack/hpack_entry.cc index 50bb718..9a47079 100644 --- a/net/spdy/hpack/hpack_entry.cc +++ b/net/spdy/hpack/hpack_entry.cc
@@ -37,8 +37,8 @@ } else { name_ = other.name_; value_ = other.value_; - name_ref_.set(name_.data(), name_.size()); - value_ref_.set(value_.data(), value_.size()); + name_ref_ = StringPiece(name_.data(), name_.size()); + value_ref_ = StringPiece(value_.data(), value_.size()); } } @@ -52,8 +52,8 @@ } name_ = other.name_; value_ = other.value_; - name_ref_.set(name_.data(), name_.size()); - value_ref_.set(value_.data(), value_.size()); + name_ref_ = StringPiece(name_.data(), name_.size()); + value_ref_ = StringPiece(value_.data(), value_.size()); return *this; }
diff --git a/net/spdy/http2_frame_decoder_adapter.cc b/net/spdy/http2_frame_decoder_adapter.cc index 756a1ee..5153a9d 100644 --- a/net/spdy/http2_frame_decoder_adapter.cc +++ b/net/spdy/http2_frame_decoder_adapter.cc
@@ -380,7 +380,7 @@ DVLOG(1) << "Ignoring invalid setting id: " << setting_fields; return; } - visitor()->OnSetting(setting_id, 0, setting_fields.value); + visitor()->OnSetting(setting_id, setting_fields.value); } void OnSettingsEnd() override {
diff --git a/net/spdy/mock_spdy_framer_visitor.h b/net/spdy/mock_spdy_framer_visitor.h index 7f33c1dd1..4f48ed0 100644 --- a/net/spdy/mock_spdy_framer_visitor.h +++ b/net/spdy/mock_spdy_framer_visitor.h
@@ -37,8 +37,7 @@ MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id, SpdyRstStreamStatus status)); MOCK_METHOD1(OnSettings, void(bool clear_persisted)); - MOCK_METHOD3(OnSetting, - void(SpdySettingsIds id, uint8_t flags, uint32_t value)); + MOCK_METHOD2(OnSetting, void(SpdySettingsIds id, uint32_t value)); MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); MOCK_METHOD0(OnSettingsEnd, void()); MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id,
diff --git a/net/spdy/spdy_deframer_visitor.cc b/net/spdy/spdy_deframer_visitor.cc index 40ff7f52..15ff32c 100644 --- a/net/spdy/spdy_deframer_visitor.cc +++ b/net/spdy/spdy_deframer_visitor.cc
@@ -169,7 +169,7 @@ bool end) override; void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override; bool OnRstStreamFrameData(const char* rst_stream_data, size_t len) override; - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override; + void OnSetting(SpdySettingsIds id, uint32_t value) override; void OnSettings(bool clear_persisted) override; void OnSettingsAck() override; void OnSettingsEnd() override; @@ -636,16 +636,13 @@ // Called for an individual setting. There is no negotiation, the sender is // stating the value that the sender is using. -void SpdyTestDeframerImpl::OnSetting(SpdySettingsIds id, - uint8_t flags, - uint32_t value) { - DVLOG(1) << "OnSetting id: " << id << std::hex << " flags: " << flags - << " value: " << value; +void SpdyTestDeframerImpl::OnSetting(SpdySettingsIds id, uint32_t value) { + DVLOG(1) << "OnSetting id: " << id << std::hex << " value: " << value; CHECK_EQ(frame_type_, SETTINGS) << " frame_type_=" << Http2FrameTypeToString(frame_type_); CHECK(settings_); settings_->push_back(std::make_pair(id, value)); - settings_ir_->AddSetting(id, true, true, value); + settings_ir_->AddSetting(id, value); } // Called at the start of a SETTINGS frame with setting entries, but not the
diff --git a/net/spdy/spdy_deframer_visitor_test.cc b/net/spdy/spdy_deframer_visitor_test.cc index 4652049..f2dc4c74 100644 --- a/net/spdy/spdy_deframer_visitor_test.cc +++ b/net/spdy/spdy_deframer_visitor_test.cc
@@ -228,7 +228,7 @@ ASSERT_NE(cf0.frame_ir, nullptr); SpdySettingsIR expected_ir; - expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, true, true, 255); + expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 255); EXPECT_TRUE(cf0.VerifyHasFrame(expected_ir)); SettingVector expected_settings; @@ -241,7 +241,7 @@ expected_settings.push_back({SETTINGS_INITIAL_WINDOW_SIZE, 65536}); EXPECT_FALSE(cf0.VerifyHasSettings(expected_settings)); - expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, true, true, 65536); + expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 65536); EXPECT_FALSE(cf0.VerifyHasFrame(expected_ir)); SpdySettingsIR unexpected_ir;
diff --git a/net/spdy/spdy_frame_reader.cc b/net/spdy/spdy_frame_reader.cc index 4727caa..7e9ded5 100644 --- a/net/spdy/spdy_frame_reader.cc +++ b/net/spdy/spdy_frame_reader.cc
@@ -10,6 +10,8 @@ namespace net { +using base::StringPiece; + SpdyFrameReader::SpdyFrameReader(const char* data, const size_t len) : data_(data), len_(len), @@ -115,7 +117,7 @@ return true; } -bool SpdyFrameReader::ReadStringPiece16(base::StringPiece* result) { +bool SpdyFrameReader::ReadStringPiece16(StringPiece* result) { // Read resultant length. uint16_t result_len; if (!ReadUInt16(&result_len)) { @@ -130,7 +132,7 @@ } // Set result. - result->set(data_ + ofs_, result_len); + *result = StringPiece(data_ + ofs_, result_len); // Iterate. ofs_ += result_len; @@ -138,7 +140,7 @@ return true; } -bool SpdyFrameReader::ReadStringPiece32(base::StringPiece* result) { +bool SpdyFrameReader::ReadStringPiece32(StringPiece* result) { // Read resultant length. uint32_t result_len; if (!ReadUInt32(&result_len)) { @@ -153,7 +155,7 @@ } // Set result. - result->set(data_ + ofs_, result_len); + *result = StringPiece(data_ + ofs_, result_len); // Iterate. ofs_ += result_len;
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 9f5140d..da6b953 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc
@@ -128,20 +128,6 @@ } while (false) #endif -SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat(uint32_t wire) { - return SettingsFlagsAndId(base::NetToHost32(wire) >> 24, - base::NetToHost32(wire) & 0x00ffffff); -} - -SettingsFlagsAndId::SettingsFlagsAndId(uint8_t flags, uint32_t id) - : flags_(flags), id_(id & 0x00ffffff) { - SPDY_BUG_IF(id > (1u << 24)) << "HTTP2 setting ID too large: " << id; -} - -uint32_t SettingsFlagsAndId::GetWireFormat() const { - return base::HostToNet32(id_ & 0x00ffffff) | base::HostToNet32(flags_ << 24); -} - bool SpdyFramerVisitorInterface::OnGoAwayFrameData(const char* goaway_data, size_t len) { return true; @@ -252,8 +238,7 @@ return SpdyConstants::kFrameHeaderSize; } -// TODO(jamessynge): Rename this to GetRstStreamSize as the frame is fixed size. -size_t SpdyFramer::GetRstStreamMinimumSize() const { +size_t SpdyFramer::GetRstStreamSize() const { // Size, in bytes, of a RST_STREAM frame. // Calculated as: // frame prefix + 4 (status code) @@ -826,7 +811,7 @@ // Do some sanity checking on the control frame sizes and flags. switch (current_frame_type_) { case RST_STREAM: - if (current_frame_length_ != GetRstStreamMinimumSize()) { + if (current_frame_length_ != GetRstStreamSize()) { set_error(SPDY_INVALID_CONTROL_FRAME_SIZE); } else if (current_frame_flags_ != 0) { VLOG(1) << "Undefined frame flags for RST_STREAM frame: " << hex @@ -1380,7 +1365,7 @@ } // Validation succeeded. Pass on to visitor. - visitor_->OnSetting(setting_id, /*flags=*/0, value); + visitor_->OnSetting(setting_id, value); return true; } @@ -1523,7 +1508,7 @@ // Check if we had already read enough bytes to parse the fixed-length portion // of the RST_STREAM frame. - const size_t header_size = GetRstStreamMinimumSize(); + const size_t header_size = GetRstStreamSize(); size_t unread_header_bytes = header_size - current_frame_buffer_.len(); bool already_parsed_header = (unread_header_bytes == 0); if (!already_parsed_header) { @@ -1888,12 +1873,7 @@ SpdySerializedFrame SpdyFramer::SerializeRstStream( const SpdyRstStreamIR& rst_stream) const { - // TODO(jgraettinger): For now, Chromium will support parsing RST_STREAM - // payloads, but will not emit them. This is used for draft HTTP/2, - // which doesn't currently include RST_STREAM payloads. GFE flags have been - // commented but left in place to simplify future patching. - // Compute the output buffer size, taking opaque data into account. - size_t expected_length = GetRstStreamMinimumSize(); + size_t expected_length = GetRstStreamSize(); SpdyFrameBuilder builder(expected_length); builder.BeginNewFrame(*this, RST_STREAM, 0, rst_stream.stream_id()); @@ -1912,7 +1892,7 @@ if (settings.is_ack()) { flags |= SETTINGS_FLAG_ACK; } - const SpdySettingsIR::ValueMap* values = &(settings.values()); + const SettingsMap* values = &(settings.values()); int setting_size = 6; // Size, in bytes, of this SETTINGS frame. @@ -1927,12 +1907,12 @@ } DCHECK_EQ(GetSettingsMinimumSize(), builder.length()); - for (SpdySettingsIR::ValueMap::const_iterator it = values->begin(); - it != values->end(); ++it) { + for (SettingsMap::const_iterator it = values->begin(); it != values->end(); + ++it) { int setting_id = it->first; DCHECK_GE(setting_id, 0); builder.WriteUInt16(static_cast<uint16_t>(setting_id)); - builder.WriteUInt32(it->second.value); + builder.WriteUInt32(it->second); } DCHECK_EQ(size, builder.length()); return builder.take(); @@ -2253,6 +2233,89 @@ SpdySerializedFrame frame_; }; +// TODO(diannahu): Use also in frame serialization. +class FlagsSerializationVisitor : public SpdyFrameVisitor { + public: + void VisitData(const SpdyDataIR& data) override { + flags_ = DATA_FLAG_NONE; + if (data.fin()) { + flags_ |= DATA_FLAG_FIN; + } + if (data.padded()) { + flags_ |= DATA_FLAG_PADDED; + } + } + + void VisitRstStream(const SpdyRstStreamIR& rst_stream) override { + flags_ = kNoFlags; + } + + void VisitSettings(const SpdySettingsIR& settings) override { + flags_ = kNoFlags; + if (settings.is_ack()) { + flags_ |= SETTINGS_FLAG_ACK; + } + } + + void VisitPing(const SpdyPingIR& ping) override { + flags_ = kNoFlags; + if (ping.is_ack()) { + flags_ |= PING_FLAG_ACK; + } + } + + void VisitGoAway(const SpdyGoAwayIR& goaway) override { flags_ = kNoFlags; } + + // TODO(diannahu): The END_HEADERS flag is incorrect for HEADERS that require + // CONTINUATION frames. + void VisitHeaders(const SpdyHeadersIR& headers) override { + flags_ = HEADERS_FLAG_END_HEADERS; + if (headers.fin()) { + flags_ |= CONTROL_FLAG_FIN; + } + if (headers.padded()) { + flags_ |= HEADERS_FLAG_PADDED; + } + if (headers.has_priority()) { + flags_ |= HEADERS_FLAG_PRIORITY; + } + } + + void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) override { + flags_ = kNoFlags; + } + + void VisitBlocked(const SpdyBlockedIR& blocked) override { + flags_ = kNoFlags; + } + + // TODO(diannahu): The END_PUSH_PROMISE flag is incorrect for PUSH_PROMISEs + // that require CONTINUATION frames. + void VisitPushPromise(const SpdyPushPromiseIR& push_promise) override { + flags_ = PUSH_PROMISE_FLAG_END_PUSH_PROMISE; + if (push_promise.padded()) { + flags_ |= PUSH_PROMISE_FLAG_PADDED; + } + } + + // TODO(diannahu): The END_HEADERS flag is incorrect for CONTINUATIONs that + // require CONTINUATION frames. + void VisitContinuation(const SpdyContinuationIR& continuation) override { + flags_ = HEADERS_FLAG_END_HEADERS; + } + + void VisitAltSvc(const SpdyAltSvcIR& altsvc) override { flags_ = kNoFlags; } + + void VisitPriority(const SpdyPriorityIR& priority) override { + flags_ = kNoFlags; + } + + uint8_t flags() const { return flags_; } + + private: + uint8_t flags_ = kNoFlags; +}; + } // namespace SpdySerializedFrame SpdyFramer::SerializeFrame(const SpdyFrameIR& frame) { @@ -2261,6 +2324,12 @@ return visitor.ReleaseSerializedFrame(); } +uint8_t SpdyFramer::GetSerializedFlags(const SpdyFrameIR& frame) { + FlagsSerializationVisitor visitor; + frame.Visit(&visitor); + return visitor.flags(); +} + size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) { DCHECK_GT(size, kMaxControlFrameSize); size_t overflow = size - kMaxControlFrameSize;
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index 79f66a5..fff856a30 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h
@@ -47,31 +47,6 @@ } // namespace test -// A datastructure for holding the ID and flag fields for SETTINGS. -// Conveniently handles converstion to/from wire format. -class NET_EXPORT_PRIVATE SettingsFlagsAndId { - public: - static SettingsFlagsAndId FromWireFormat(uint32_t wire); - - SettingsFlagsAndId() : flags_(0), id_(0) {} - - // TODO(hkhalil): restrict to enums instead of free-form ints. - SettingsFlagsAndId(uint8_t flags, uint32_t id); - - uint32_t GetWireFormat() const; - - uint32_t id() const { return id_; } - uint8_t flags() const { return flags_; } - - private: - uint8_t flags_; - uint32_t id_; -}; - -// SettingsMap has unique (flags, value) pair for given SpdySettingsIds ID. -typedef std::pair<SpdySettingsFlags, uint32_t> SettingsFlagsAndValue; -typedef std::map<SpdySettingsIds, SettingsFlagsAndValue> SettingsMap; - // SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer. // Implement this interface to receive event callbacks as frames are // decoded from the framer. @@ -150,7 +125,7 @@ // Called when a complete setting within a SETTINGS frame has been parsed and // validated. - virtual void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) = 0; + virtual void OnSetting(SpdySettingsIds id, uint32_t value) = 0; // Called when a SETTINGS frame is received with the ACK flag set. virtual void OnSettingsAck() {} @@ -343,6 +318,9 @@ // Retrieve serialized length of SpdyHeaderBlock. static size_t GetSerializedLength(const SpdyHeaderBlock* headers); + // Gets the serialized flags for the given |frame|. + static uint8_t GetSerializedFlags(const SpdyFrameIR& frame); + explicit SpdyFramer(CompressionOption option); // Used recursively from the above constructor in order to support @@ -492,7 +470,7 @@ // Returns the (minimum) size of frames (sans variable-length portions). size_t GetDataFrameMinimumSize() const; size_t GetFrameHeaderSize() const; - size_t GetRstStreamMinimumSize() const; + size_t GetRstStreamSize() const; size_t GetSettingsMinimumSize() const; size_t GetPingSize() const; size_t GetGoAwayMinimumSize() const;
diff --git a/net/spdy/spdy_framer_decoder_adapter.cc b/net/spdy/spdy_framer_decoder_adapter.cc index b68f04a..3e7a7fe 100644 --- a/net/spdy/spdy_framer_decoder_adapter.cc +++ b/net/spdy/spdy_framer_decoder_adapter.cc
@@ -92,9 +92,8 @@ } void SpdyFramerVisitorAdapter::OnSetting(SpdySettingsIds id, - uint8_t flags, uint32_t value) { - visitor_->OnSetting(id, flags, value); + visitor_->OnSetting(id, value); } void SpdyFramerVisitorAdapter::OnPing(SpdyPingId unique_id, bool is_ack) {
diff --git a/net/spdy/spdy_framer_decoder_adapter.h b/net/spdy/spdy_framer_decoder_adapter.h index 20b70f6..59a33d09a 100644 --- a/net/spdy/spdy_framer_decoder_adapter.h +++ b/net/spdy/spdy_framer_decoder_adapter.h
@@ -116,7 +116,7 @@ SpdyStreamId stream_id) override; void OnHeaderFrameEnd(SpdyStreamId stream_id, bool end_headers) override; void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override; - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override; + void OnSetting(SpdySettingsIds id, uint32_t value) override; void OnPing(SpdyPingId unique_id, bool is_ack) override; void OnSettings(bool clear_persisted) override; void OnSettingsAck() override;
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 4b0333b..c3eb9a9c7 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc
@@ -151,9 +151,7 @@ SpdyRstStreamStatus status) override { LOG(FATAL); } - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override { - LOG(FATAL); - } + void OnSetting(SpdySettingsIds id, uint32_t value) override { LOG(FATAL); } void OnPing(SpdyPingId unique_id, bool is_ack) override { LOG(FATAL); } void OnSettingsEnd() override { LOG(FATAL); } void OnGoAway(SpdyStreamId last_accepted_stream_id, @@ -373,9 +371,8 @@ return true; } - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override { - VLOG(1) << "OnSetting(" << id << ", " << std::hex << flags << ", " << value - << ")"; + void OnSetting(SpdySettingsIds id, uint32_t value) override { + VLOG(1) << "OnSetting(" << id << ", " << std::hex << ", " << value << ")"; ++setting_count_; } @@ -1692,10 +1689,8 @@ uint32_t kValue = 0x0a0b0c0d; SpdySettingsIR settings_ir; - SpdySettingsFlags kFlags = static_cast<SpdySettingsFlags>(0x01); SpdySettingsIds kId = SETTINGS_INITIAL_WINDOW_SIZE; - settings_ir.AddSetting(kId, kFlags & SETTINGS_FLAG_PLEASE_PERSIST, - kFlags & SETTINGS_FLAG_PERSISTED, kValue); + settings_ir.AddSetting(kId, kValue); SpdySerializedFrame frame(framer.SerializeSettings(settings_ir)); CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); @@ -1722,22 +1717,10 @@ }; SpdySettingsIR settings_ir; - settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, - false, // persist - false, // persisted - 5); - settings_ir.AddSetting(SETTINGS_ENABLE_PUSH, - false, // persist - false, // persisted - 6); - settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, - false, // persist - false, // persisted - 7); - settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, - false, // persist - false, // persisted - 8); + settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 5); + settings_ir.AddSetting(SETTINGS_ENABLE_PUSH, 6); + settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 7); + settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 8); SpdySerializedFrame frame(framer.SerializeSettings(settings_ir)); CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); @@ -2776,10 +2759,8 @@ // overflow when calling SimulateInFramer() below. These settings must be // distinct parameters because SpdySettingsIR has a map for settings, and will // collapse multiple copies of the same parameter. - settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, false, false, - 0x00000002); - settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, false, false, - 0x00000002); + settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0x00000002); + settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 0x00000002); SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir)); const size_t kNewLength = 8; SetFrameLength(&control_frame, kNewLength); @@ -2799,18 +2780,9 @@ TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) { SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); SpdySettingsIR settings_ir; - settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, - false, // persist - false, // persisted - 5); - settings_ir.AddSetting(SETTINGS_ENABLE_PUSH, - false, // persist - false, // persisted - 6); - settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, - false, // persist - false, // persisted - 7); + settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 5); + settings_ir.AddSetting(SETTINGS_ENABLE_PUSH, 6); + settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 7); SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir)); EXPECT_LT(SpdyFramerPeer::ControlFrameBufferSize(), control_frame.size()); @@ -3373,10 +3345,7 @@ // Follow it up with a valid control frame to make sure we handle // subsequent frames correctly. SpdySettingsIR settings_ir; - settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, - false, // persist - false, // persisted - 10); + settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 10); SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir)); visitor.SimulateInFramer( reinterpret_cast<unsigned char*>(control_frame.data()), @@ -3424,7 +3393,7 @@ SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); EXPECT_EQ(9u, framer.GetDataFrameMinimumSize()); EXPECT_EQ(9u, framer.GetFrameHeaderSize()); - EXPECT_EQ(13u, framer.GetRstStreamMinimumSize()); + EXPECT_EQ(13u, framer.GetRstStreamSize()); EXPECT_EQ(9u, framer.GetSettingsMinimumSize()); EXPECT_EQ(17u, framer.GetPingSize()); EXPECT_EQ(17u, framer.GetGoAwayMinimumSize()); @@ -3623,7 +3592,7 @@ framer.set_visitor(&visitor); SpdySettingsIR settings_ir; - settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 0, 16); + settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 16); SpdySerializedFrame frame(framer.SerializeSettings(settings_ir)); SetFrameFlags(&frame, flags); @@ -3631,7 +3600,7 @@ EXPECT_CALL(visitor, OnError(_)); } else { EXPECT_CALL(visitor, OnSettings(flags & SETTINGS_FLAG_ACK)); - EXPECT_CALL(visitor, OnSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 16)); + EXPECT_CALL(visitor, OnSetting(SETTINGS_INITIAL_WINDOW_SIZE, 16)); EXPECT_CALL(visitor, OnSettingsEnd()); } @@ -3865,18 +3834,6 @@ // TODO(hkhalil): Add TEST_F(SpdyFramerTest, BlockedFrameFlags) -TEST_P(SpdyFramerTest, SettingsFlagsAndId) { - const uint32_t kId = 0x020304; - const uint32_t kFlags = 0x01; - const uint32_t kWireFormat = base::HostToNet32(0x01020304); - - SettingsFlagsAndId id_and_flags = - SettingsFlagsAndId::FromWireFormat(kWireFormat); - EXPECT_EQ(kId, id_and_flags.id()); - EXPECT_EQ(kFlags, id_and_flags.flags()); - EXPECT_EQ(kWireFormat, id_and_flags.GetWireFormat()); -} - // Test handling of a RST_STREAM with out-of-bounds status codes. TEST_P(SpdyFramerTest, RstStreamStatusBounds) { const unsigned char kH2RstStreamInvalid[] = {
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index f070219..939e0c0 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -930,8 +930,7 @@ SettingsMap settings; const uint32_t max_concurrent_streams = 1; - settings[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams); + settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; SpdySerializedFrame settings_frame( spdy_util_.ConstructSpdySettings(settings)); SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); @@ -1061,8 +1060,7 @@ SettingsMap settings; const uint32_t max_concurrent_streams = 1; - settings[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams); + settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; SpdySerializedFrame settings_frame( spdy_util_.ConstructSpdySettings(settings)); SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); @@ -1195,8 +1193,7 @@ SettingsMap settings; const uint32_t max_concurrent_streams = 1; - settings[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams); + settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; SpdySerializedFrame settings_frame( spdy_util_.ConstructSpdySettings(settings)); SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); @@ -1320,8 +1317,7 @@ SettingsMap settings; const uint32_t max_concurrent_streams = 1; - settings[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams); + settings[SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams; SpdySerializedFrame settings_frame( spdy_util_.ConstructSpdySettings(settings)); SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); @@ -5322,12 +5318,10 @@ stream_max_recv_window_size / 2 + kChunkSize; SettingsMap initial_settings; - initial_settings[SETTINGS_HEADER_TABLE_SIZE] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxHeaderTableSize); + initial_settings[SETTINGS_HEADER_TABLE_SIZE] = kMaxHeaderTableSize; initial_settings[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxConcurrentPushedStreams); - initial_settings[SETTINGS_INITIAL_WINDOW_SIZE] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, stream_max_recv_window_size); + kMaxConcurrentPushedStreams; + initial_settings[SETTINGS_INITIAL_WINDOW_SIZE] = stream_max_recv_window_size; SpdySerializedFrame initial_settings_frame( spdy_util_.ConstructSpdySettings(initial_settings)); SpdySerializedFrame initial_window_update( @@ -5722,8 +5716,7 @@ // Construct read frame for SETTINGS that gives enough space to upload the // rest of the data. SettingsMap settings; - settings[SETTINGS_INITIAL_WINDOW_SIZE] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, initial_window_size * 2); + settings[SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size * 2; SpdySerializedFrame settings_frame_large( spdy_util_.ConstructSpdySettings(settings)); @@ -5884,8 +5877,7 @@ // Construct read frame for SETTINGS that makes the send_window_size // negative. SettingsMap new_settings; - new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, initial_window_size / 2); + new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size / 2; SpdySerializedFrame settings_frame_small( spdy_util_.ConstructSpdySettings(new_settings)); // Construct read frames for WINDOW_UPDATE that makes the send_window_size
diff --git a/net/spdy/spdy_no_op_visitor.h b/net/spdy/spdy_no_op_visitor.h index 573d813..cd3d495 100644 --- a/net/spdy/spdy_no_op_visitor.h +++ b/net/spdy/spdy_no_op_visitor.h
@@ -37,7 +37,7 @@ void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {} void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override {} - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override {} + void OnSetting(SpdySettingsIds id, uint32_t value) override {} void OnPing(SpdyPingId unique_id, bool is_ack) override {} void OnSettingsEnd() override {} void OnSettingsAck() override {}
diff --git a/net/spdy/spdy_protocol.cc b/net/spdy/spdy_protocol.cc index cd773394..6993100 100644 --- a/net/spdy/spdy_protocol.cc +++ b/net/spdy/spdy_protocol.cc
@@ -450,9 +450,7 @@ return visitor->VisitRstStream(*this); } -SpdySettingsIR::SpdySettingsIR() - : clear_settings_(false), - is_ack_(false) {} +SpdySettingsIR::SpdySettingsIR() : is_ack_(false) {} SpdySettingsIR::~SpdySettingsIR() {}
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index bbd959a..0f29e6eb 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h
@@ -130,13 +130,6 @@ SETTINGS_FLAG_ACK = 0x01, }; -// Flags for settings within a SETTINGS frame. -enum SpdySettingsFlags { - SETTINGS_FLAG_NONE = 0x00, - SETTINGS_FLAG_PLEASE_PERSIST = 0x01, - SETTINGS_FLAG_PERSISTED = 0x02, -}; - enum SpdySettingsIds { // HPACK header table maximum size. SETTINGS_HEADER_TABLE_SIZE = 0x1, @@ -154,6 +147,8 @@ SETTINGS_MAX = SETTINGS_MAX_HEADER_LIST_SIZE }; +using SettingsMap = std::map<SpdySettingsIds, uint32_t>; + // Status codes for RST_STREAM frames. enum SpdyRstStreamStatus { RST_STREAM_NO_ERROR = 0, @@ -592,33 +587,13 @@ class NET_EXPORT_PRIVATE SpdySettingsIR : public SpdyFrameIR { public: - // Associates flags with a value. - struct Value { - Value() : persist_value(false), - persisted(false), - value(0) {} - bool persist_value; - bool persisted; - int32_t value; - }; - typedef std::map<SpdySettingsIds, Value> ValueMap; - SpdySettingsIR(); - ~SpdySettingsIR() override; // Overwrites as appropriate. - const ValueMap& values() const { return values_; } - void AddSetting(SpdySettingsIds id, - bool persist_value, - bool persisted, - int32_t value) { - values_[id].persist_value = persist_value; - values_[id].persisted = persisted; - values_[id].value = value; - } + const SettingsMap& values() const { return values_; } + void AddSetting(SpdySettingsIds id, int32_t value) { values_[id] = value; } - bool clear_settings() const { return clear_settings_; } bool is_ack() const { return is_ack_; } void set_is_ack(bool is_ack) { is_ack_ = is_ack; @@ -627,8 +602,7 @@ void Visit(SpdyFrameVisitor* visitor) const override; private: - ValueMap values_; - bool clear_settings_; + SettingsMap values_; bool is_ack_; DISALLOW_COPY_AND_ASSIGN(SpdySettingsIR);
diff --git a/net/spdy/spdy_protocol_test.cc b/net/spdy/spdy_protocol_test.cc index 4be43d5c..4013600 100644 --- a/net/spdy/spdy_protocol_test.cc +++ b/net/spdy/spdy_protocol_test.cc
@@ -33,18 +33,6 @@ namespace test { -TEST(SpdyProtocolDeathTest, TestSpdySettingsAndIdOutOfBounds) { - std::unique_ptr<SettingsFlagsAndId> flags_and_id; - - EXPECT_SPDY_BUG(flags_and_id.reset(new SettingsFlagsAndId(1, 0xffffffff)), - "HTTP2 setting ID too large."); - // Make sure that we get expected values in opt mode. - if (flags_and_id.get() != nullptr) { - EXPECT_EQ(1, flags_and_id->flags()); - EXPECT_EQ(0xffffffu, flags_and_id->id()); - } -} - TEST(SpdyProtocolTest, IsValidHTTP2FrameStreamId) { // Stream-specific frames must have non-zero stream ids EXPECT_TRUE(SpdyConstants::IsValidHTTP2FrameStreamId(1, DATA));
diff --git a/net/spdy/spdy_protocol_test_utils.cc b/net/spdy/spdy_protocol_test_utils.cc index dd600ded..73cc067 100644 --- a/net/spdy/spdy_protocol_test_utils.cc +++ b/net/spdy/spdy_protocol_test_utils.cc
@@ -164,8 +164,8 @@ DVLOG(1) << "actual doesn't contain param: " << param; return ::testing::AssertionFailure(); } - uint32_t expected_value = entry.second.value; - uint32_t actual_value = actual_itr->second.value; + uint32_t expected_value = entry.second; + uint32_t actual_value = actual_itr->second; if (expected_value != actual_value) { DVLOG(1) << "Values don't match for parameter: " << param; return ::testing::AssertionFailure();
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 59e331a..80fdd6e 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc
@@ -142,12 +142,10 @@ std::unique_ptr<base::Value> NetLogSpdySettingCallback( SpdySettingsIds id, - SpdySettingsFlags flags, uint32_t value, NetLogCaptureMode /* capture_mode */) { std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetInteger("id", id); - dict->SetInteger("flags", flags); dict->SetInteger("value", value); return std::move(dict); } @@ -160,10 +158,9 @@ for (SettingsMap::const_iterator it = settings->begin(); it != settings->end(); ++it) { const SpdySettingsIds id = it->first; - const SpdySettingsFlags flags = it->second.first; - const uint32_t value = it->second.second; + const uint32_t value = it->second; settings_list->AppendString( - base::StringPrintf("[id:%u flags:%u value:%u]", id, flags, value)); + base::StringPrintf("[id:%u value:%u]", id, value)); } dict->Set("settings", std::move(settings_list)); return std::move(dict); @@ -2098,15 +2095,14 @@ buffered_spdy_framer_->SerializeFrame(settings_ir)))); } -void SpdySession::OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) { +void SpdySession::OnSetting(SpdySettingsIds id, uint32_t value) { CHECK(in_io_loop_); HandleSetting(id, value); // Log the setting. net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTING, - base::Bind(&NetLogSpdySettingCallback, id, - static_cast<SpdySettingsFlags>(flags), value)); + base::Bind(&NetLogSpdySettingCallback, id, value)); } void SpdySession::OnSendCompressedFrame( @@ -2651,13 +2647,10 @@ // First, notify the server about the settings they should use when // communicating with us. SettingsMap settings_map; - settings_map[SETTINGS_HEADER_TABLE_SIZE] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxHeaderTableSize); - settings_map[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxConcurrentPushedStreams); + settings_map[SETTINGS_HEADER_TABLE_SIZE] = kMaxHeaderTableSize; + settings_map[SETTINGS_MAX_CONCURRENT_STREAMS] = kMaxConcurrentPushedStreams; if (stream_max_recv_window_size_ != kDefaultInitialWindowSize) { - settings_map[SETTINGS_INITIAL_WINDOW_SIZE] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, stream_max_recv_window_size_); + settings_map[SETTINGS_INITIAL_WINDOW_SIZE] = stream_max_recv_window_size_; } SendSettings(settings_map);
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 9dbf5cd..c5afee5 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h
@@ -875,7 +875,7 @@ void OnStreamEnd(SpdyStreamId stream_id) override; void OnStreamPadding(SpdyStreamId stream_id, size_t len) override; void OnSettings() override; - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override; + void OnSetting(SpdySettingsIds id, uint32_t value) override; void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override; void OnPushPromise(SpdyStreamId stream_id, SpdyStreamId promised_stream_id,
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc index 476cb76..12b8c01c 100644 --- a/net/spdy/spdy_session_unittest.cc +++ b/net/spdy/spdy_session_unittest.cc
@@ -1132,8 +1132,7 @@ // Receive SETTINGS frame that sets max_concurrent_streams to zero. SettingsMap settings_zero; - settings_zero[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 0); + settings_zero[SETTINGS_MAX_CONCURRENT_STREAMS] = 0; SpdySerializedFrame settings_frame_zero( spdy_util_.ConstructSpdySettings(settings_zero)); @@ -1142,8 +1141,7 @@ // Receive SETTINGS frame that sets max_concurrent_streams to one. SettingsMap settings_one; - settings_one[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 1); + settings_one[SETTINGS_MAX_CONCURRENT_STREAMS] = 1; SpdySerializedFrame settings_frame_one( spdy_util_.ConstructSpdySettings(settings_one)); @@ -1782,8 +1780,7 @@ SettingsMap new_settings; const uint32_t max_concurrent_streams = kInitialMaxConcurrentStreams + 1; - new_settings[kSpdySettingsIds] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams); + new_settings[kSpdySettingsIds] = max_concurrent_streams; SpdySerializedFrame settings_frame( spdy_util_.ConstructSpdySettings(new_settings)); MockRead reads[] = { @@ -1892,10 +1889,8 @@ }; SettingsMap settings; - settings[SETTINGS_HEADER_TABLE_SIZE] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxHeaderTableSize); - settings[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxConcurrentPushedStreams); + settings[SETTINGS_HEADER_TABLE_SIZE] = kMaxHeaderTableSize; + settings[SETTINGS_MAX_CONCURRENT_STREAMS] = kMaxConcurrentPushedStreams; SpdySerializedFrame settings_frame( spdy_util_.ConstructSpdySettings(settings)); MockWrite writes[] = {MockWrite(ASYNC, kHttp2ConnectionHeaderPrefix, @@ -2619,8 +2614,7 @@ SettingsMap new_settings; const SpdySettingsIds kSpdySettingsIds1 = SETTINGS_MAX_CONCURRENT_STREAMS; const uint32_t max_concurrent_streams = 1; - new_settings[kSpdySettingsIds1] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams); + new_settings[kSpdySettingsIds1] = max_concurrent_streams; SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); SpdySerializedFrame req1( @@ -3659,8 +3653,7 @@ // gets sent. SettingsMap new_settings; int32_t window_size = 1; - new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, window_size); + new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = window_size; // Set up the socket so we read a SETTINGS frame that sets // INITIAL_WINDOW_SIZE. @@ -4880,8 +4873,7 @@ // limit for a long time. TEST_F(SpdySessionTest, PushedStreamShouldNotCountToClientConcurrencyLimit) { SettingsMap new_settings; - new_settings[SETTINGS_MAX_CONCURRENT_STREAMS] = - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 2); + new_settings[SETTINGS_MAX_CONCURRENT_STREAMS] = 2; SpdySerializedFrame settings_frame( spdy_util_.ConstructSpdySettings(new_settings)); SpdySerializedFrame pushed(spdy_util_.ConstructSpdyPush(
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc index 913287c..b172e1c 100644 --- a/net/spdy/spdy_test_util_common.cc +++ b/net/spdy/spdy_test_util_common.cc
@@ -211,7 +211,7 @@ void OnStreamEnd(SpdyStreamId stream_id) override {} void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {} void OnSettings() override {} - void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override {} + void OnSetting(SpdySettingsIds id, uint32_t value) override {} void OnPing(SpdyPingId unique_id, bool is_ack) override {} void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override {} @@ -408,7 +408,7 @@ class AllowAnyCertCTPolicyEnforcer : public CTPolicyEnforcer { public: - AllowAnyCertCTPolicyEnforcer(){}; + AllowAnyCertCTPolicyEnforcer() {} ~AllowAnyCertCTPolicyEnforcer() override = default; ct::CertPolicyCompliance DoesConformToCertPolicy( @@ -746,14 +746,9 @@ SpdySerializedFrame SpdyTestUtil::ConstructSpdySettings( const SettingsMap& settings) { SpdySettingsIR settings_ir; - for (SettingsMap::const_iterator it = settings.begin(); - it != settings.end(); + for (SettingsMap::const_iterator it = settings.begin(); it != settings.end(); ++it) { - settings_ir.AddSetting( - it->first, - (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0, - (it->second.first & SETTINGS_FLAG_PERSISTED) != 0, - it->second.second); + settings_ir.AddSetting(it->first, it->second); } return SpdySerializedFrame( headerless_spdy_framer_.SerializeFrame(settings_ir)); @@ -1048,21 +1043,19 @@ SpdySerializedFrame SpdyTestUtil::ConstructSpdyDataFrame(int stream_id, bool fin) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); SpdyDataIR data_ir(stream_id, base::StringPiece(kUploadData, kUploadDataSize)); data_ir.set_fin(fin); - return SpdySerializedFrame(framer.SerializeData(data_ir)); + return SpdySerializedFrame(headerless_spdy_framer_.SerializeData(data_ir)); } SpdySerializedFrame SpdyTestUtil::ConstructSpdyDataFrame(int stream_id, const char* data, uint32_t len, bool fin) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); SpdyDataIR data_ir(stream_id, base::StringPiece(data, len)); data_ir.set_fin(fin); - return SpdySerializedFrame(framer.SerializeData(data_ir)); + return SpdySerializedFrame(headerless_spdy_framer_.SerializeData(data_ir)); } SpdySerializedFrame SpdyTestUtil::ConstructSpdyDataFrame(int stream_id, @@ -1070,11 +1063,10 @@ uint32_t len, bool fin, int padding_length) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); SpdyDataIR data_ir(stream_id, base::StringPiece(data, len)); data_ir.set_fin(fin); data_ir.set_padding_len(padding_length); - return SpdySerializedFrame(framer.SerializeData(data_ir)); + return SpdySerializedFrame(headerless_spdy_framer_.SerializeData(data_ir)); } SpdySerializedFrame SpdyTestUtil::ConstructWrappedSpdyFrame(
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index 9fda719..470fe25 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc
@@ -1827,7 +1827,7 @@ QuicSessionPeer::GetCryptoStream(server_session)->flow_controller()); SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION); SpdySettingsIR settings_frame; - settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, false, false, + settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize); SpdySerializedFrame frame(spdy_framer.SerializeFrame(settings_frame)); QuicFlowController* client_header_stream_flow_controller =
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn index e4c33bb..32f4922 100644 --- a/services/device/BUILD.gn +++ b/services/device/BUILD.gn
@@ -13,9 +13,17 @@ deps = [ "//base", + "//device/time_zone_monitor", "//services/service_manager/public/cpp", ] + public_deps = [ + # TODO(blundell): This dep shouldn't be necessary at all, + # but content_shell fails to link in the component build if + # this isn't here as a public_dep. + "//device/time_zone_monitor/public/interfaces", + ] + data_deps = [ ":manifest", ]
diff --git a/services/device/DEPS b/services/device/DEPS new file mode 100644 index 0000000..56fffa1 --- /dev/null +++ b/services/device/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+device", +]
diff --git a/services/device/device_service.cc b/services/device/device_service.cc index 924c803..27c020d1d 100644 --- a/services/device/device_service.cc +++ b/services/device/device_service.cc
@@ -7,7 +7,10 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" +#include "base/threading/thread_task_runner_handle.h" +#include "device/time_zone_monitor/time_zone_monitor.h" #include "services/service_manager/public/cpp/connection.h" +#include "services/service_manager/public/cpp/interface_registry.h" namespace device { @@ -26,7 +29,15 @@ bool DeviceService::OnConnect(const service_manager::ServiceInfo& remote_info, service_manager::InterfaceRegistry* registry) { + registry->AddInterface<mojom::TimeZoneMonitor>(this); return true; } +void DeviceService::Create(const service_manager::Identity& remote_identity, + mojom::TimeZoneMonitorRequest request) { + if (!time_zone_monitor_) + time_zone_monitor_ = device::TimeZoneMonitor::Create(file_task_runner_); + time_zone_monitor_->Bind(std::move(request)); +} + } // namespace device
diff --git a/services/device/device_service.h b/services/device/device_service.h index 6f55953..cea6c11 100644 --- a/services/device/device_service.h +++ b/services/device/device_service.h
@@ -5,21 +5,24 @@ #ifndef SERVICES_DEVICE_DEVICE_SERVICE_H_ #define SERVICES_DEVICE_DEVICE_SERVICE_H_ -#include "base/callback_forward.h" #include "base/memory/ref_counted.h" +#include "device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "services/service_manager/public/cpp/interface_factory.h" #include "services/service_manager/public/cpp/service.h" namespace device { +class TimeZoneMonitor; + std::unique_ptr<service_manager::Service> CreateDeviceService( scoped_refptr<base::SingleThreadTaskRunner> file_task_runner); -class DeviceService : public service_manager::Service { +class DeviceService + : public service_manager::Service, + public service_manager::InterfaceFactory<mojom::TimeZoneMonitor> { public: - explicit DeviceService( - scoped_refptr<base::SingleThreadTaskRunner> file_task_runner); + DeviceService(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner); ~DeviceService() override; private: @@ -28,6 +31,11 @@ bool OnConnect(const service_manager::ServiceInfo& remote_info, service_manager::InterfaceRegistry* registry) override; + // InterfaceFactory<mojom::TimeZoneMonitor>: + void Create(const service_manager::Identity& remote_identity, + mojom::TimeZoneMonitorRequest request) override; + + std::unique_ptr<device::TimeZoneMonitor> time_zone_monitor_; scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_; DISALLOW_COPY_AND_ASSIGN(DeviceService);
diff --git a/services/device/manifest.json b/services/device/manifest.json index bdc1d8d8..4d57086 100644 --- a/services/device/manifest.json +++ b/services/device/manifest.json
@@ -5,6 +5,7 @@ "interface_provider_specs": { "service_manager:connector": { "provides": { + "device:time_zone_monitor": [ "device::mojom::TimeZoneMonitor" ] }, "requires": { }
diff --git a/services/device/public/cpp/constants.cc b/services/device/public/cpp/constants.cc deleted file mode 100644 index 8bf119d4..0000000 --- a/services/device/public/cpp/constants.cc +++ /dev/null
@@ -1,11 +0,0 @@ -// Copyright 2016 The Chromium 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 "services/device/public/cpp/constants.h" - -namespace device { - -const char kDeviceServiceName[] = "device"; - -} // namespace device
diff --git a/services/device/public/cpp/constants.h b/services/device/public/cpp/constants.h deleted file mode 100644 index 3a48f45..0000000 --- a/services/device/public/cpp/constants.h +++ /dev/null
@@ -1,14 +0,0 @@ -// Copyright 2016 The Chromium 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 SERVICES_DEVICE_PUBLIC_CPP_CONSTANTS_H_ -#define SERVICES_DEVICE_PUBLIC_CPP_CONSTANTS_H_ - -namespace device { - -extern const char kDeviceServiceName[]; - -} // namespace device - -#endif // SERVICES_DEVICE_PUBLIC_CPP_CONSTANTS_H_
diff --git a/services/device/public/cpp/BUILD.gn b/services/device/public/interfaces/BUILD.gn similarity index 65% rename from services/device/public/cpp/BUILD.gn rename to services/device/public/interfaces/BUILD.gn index f341a18b..dec07695 100644 --- a/services/device/public/cpp/BUILD.gn +++ b/services/device/public/interfaces/BUILD.gn
@@ -2,9 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -source_set("cpp") { +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("interfaces") { sources = [ - "constants.cc", - "constants.h", + "constants.mojom", ] }
diff --git a/services/device/public/interfaces/OWNERS b/services/device/public/interfaces/OWNERS new file mode 100644 index 0000000..08850f4 --- /dev/null +++ b/services/device/public/interfaces/OWNERS
@@ -0,0 +1,2 @@ +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/services/device/public/interfaces/constants.mojom b/services/device/public/interfaces/constants.mojom new file mode 100644 index 0000000..de400fc --- /dev/null +++ b/services/device/public/interfaces/constants.mojom
@@ -0,0 +1,7 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module device.mojom; + +const string kServiceName = "device";
diff --git a/services/ui/public/interfaces/window_manager.mojom b/services/ui/public/interfaces/window_manager.mojom index 788f706..2239dde 100644 --- a/services/ui/public/interfaces/window_manager.mojom +++ b/services/ui/public/interfaces/window_manager.mojom
@@ -75,7 +75,6 @@ const string kAlwaysOnTop_Property = "prop:always_on_top"; // The application icon; typically larger for shelf icons, etc. Type: SkBitmap - // TODO(msw): map to aura::client::kAppIconKey http://crbug.com/663522? const string kAppIcon_Property = "prop:app-icon"; // The application ID (eg. 'mojo:foo'). Maps to @@ -123,7 +122,6 @@ const string kShowState_Property = "prop:show-state"; // The window icon; typically 16x16 for titlebars. Type: SkBitmap - // TODO(msw): map to aura::client::kAppIconKey http://crbug.com/663522? const string kWindowIcon_Property = "prop:window-icon"; // A flag controlling the window's presence on the mash shelf. Type: bool
diff --git a/storage/browser/fileapi/file_system_operation_context.cc b/storage/browser/fileapi/file_system_operation_context.cc index b392d7b..6f03ff45b 100644 --- a/storage/browser/fileapi/file_system_operation_context.cc +++ b/storage/browser/fileapi/file_system_operation_context.cc
@@ -27,7 +27,7 @@ } FileSystemOperationContext::~FileSystemOperationContext() { - DetachUserDataThread(); + DetachFromSequence(); setter_thread_checker_.DetachFromThread(); }
diff --git a/storage/browser/fileapi/file_system_operation_impl.cc b/storage/browser/fileapi/file_system_operation_impl.cc index 196f693..9defb3c 100644 --- a/storage/browser/fileapi/file_system_operation_impl.cc +++ b/storage/browser/fileapi/file_system_operation_impl.cc
@@ -384,7 +384,7 @@ pending_operation_(kOperationNone), weak_factory_(this) { DCHECK(operation_context_.get()); - operation_context_->DetachUserDataThread(); + operation_context_->DetachFromSequence(); async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type()); DCHECK(async_file_util_); }
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json index 63b97a7..4599c63 100644 --- a/testing/buildbot/chromium.perf.json +++ b/testing/buildbot/chromium.perf.json
@@ -3511,6 +3511,25 @@ } }, { + "args": [], + "isolate_name": "media_perftests", + "name": "media_perftests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "102b:0534", + "id": "build151-m1", + "os": "Ubuntu-14.04", + "pool": "Chrome-perf" + } + ], + "expiration": 21600, + "hard_timeout": 7200, + "io_timeout": 3600 + } + }, + { "args": [ "memory.blink_memory_mobile", "-v", @@ -25426,6 +25445,25 @@ } }, { + "args": [], + "isolate_name": "media_perftests", + "name": "media_perftests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "8086:0166", + "id": "build104-b1", + "os": "Mac-10.11", + "pool": "Chrome-perf" + } + ], + "expiration": 21600, + "hard_timeout": 7200, + "io_timeout": 3600 + } + }, + { "args": [ "memory.blink_memory_mobile", "-v", @@ -80166,6 +80204,25 @@ } }, { + "args": [], + "isolate_name": "media_perftests", + "name": "media_perftests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "102b:0534", + "id": "build134-m1", + "os": "Windows-10-10240", + "pool": "Chrome-perf" + } + ], + "expiration": 21600, + "hard_timeout": 7200, + "io_timeout": 3600 + } + }, + { "args": [ "memory.blink_memory_mobile", "-v", @@ -91152,6 +91209,25 @@ } }, { + "args": [], + "isolate_name": "media_perftests", + "name": "media_perftests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "1002:6779", + "id": "build104-m1", + "os": "Windows-2008ServerR2-SP1", + "pool": "Chrome-perf" + } + ], + "expiration": 21600, + "hard_timeout": 7200, + "io_timeout": 3600 + } + }, + { "args": [ "memory.blink_memory_mobile", "-v", @@ -113105,6 +113181,25 @@ } }, { + "args": [], + "isolate_name": "media_perftests", + "name": "media_perftests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "10de:104a", + "id": "build95-m1", + "os": "Windows-2008ServerR2-SP1", + "pool": "Chrome-perf" + } + ], + "expiration": 21600, + "hard_timeout": 7200, + "io_timeout": 3600 + } + }, + { "args": [ "memory.blink_memory_mobile", "-v", @@ -124091,6 +124186,25 @@ } }, { + "args": [], + "isolate_name": "media_perftests", + "name": "media_perftests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "102b:0532", + "id": "build188-m1", + "os": "Windows-2008ServerR2-SP1", + "pool": "Chrome-perf" + } + ], + "expiration": 21600, + "hard_timeout": 7200, + "io_timeout": 3600 + } + }, + { "args": [ "memory.blink_memory_mobile", "-v", @@ -146063,6 +146177,25 @@ } }, { + "args": [], + "isolate_name": "media_perftests", + "name": "media_perftests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "102b:0532", + "id": "build146-m1", + "os": "Windows-2012ServerR2-SP0", + "pool": "Chrome-perf" + } + ], + "expiration": 21600, + "hard_timeout": 7200, + "io_timeout": 3600 + } + }, + { "args": [ "memory.blink_memory_mobile", "-v",
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter index d68ce32..6f36796 100644 --- a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter +++ b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
@@ -1,5 +1,3 @@ --ChromeServiceWorkerTest.FallbackMainResourceRequestWhenJSDisabled - # NavigationHandle::IsRendererInitiated some times return differently in # browser side navigation tests. -SBNavigationObserverBrowserTest.DirectDownloadNoReferrerTargetBlank
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter index 60e438de..15285ed 100644 --- a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter +++ b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
@@ -1,5 +1,4 @@ -NavigationControllerBrowserTest.EnsureSamePageNavigationUpdatesFrameNavigationEntry --RequestDataResourceDispatcherHostBrowserTest.* # Browser-initiated fragment navigations are handled improperly. https://crbug.com/663777 -NavigationControllerBrowserTest.SamePageBrowserInitiated
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index 8b21a0a..e4bc8a3 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -852,6 +852,14 @@ "cc_perftests", ], }, + "media_perftests": { + "label": "//media:media_perftests", + "type": "script", + "script": "//testing/scripts/run_gtest_perf_test.py", + "args": [ + "media_perftests", + ], + }, "load_library_perf_tests": { "label": "//chrome/test:load_library_perf_tests", "type": "script",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index dd6bbd7..6716526 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -212,7 +212,10 @@ ], "experiments": [ { - "name": "Default" + "name": "Default", + "params": { + "RedirectSequencedWorkerPools": "true" + } }, { "name": "EnableViaFlag",
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests index eb6163b..9ff65fe 100644 --- a/third_party/WebKit/LayoutTests/NeverFixTests +++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -172,6 +172,14 @@ crbug.com/551843 [ Linux ] fast/text/fallback-traits-fixup.html [ WontFix ] crbug.com/551843 [ Linux Win ] fast/text/emoji-font-weight-mac.html [ WontFix ] +# Variable system font only supported on Mac atm. +crbug.com/670246 [ Linux Android Win Mac10.9 Mac10.10 ] fast/text/variable-fonts/variable-mac-system-font.html [ WontFix ] +# OpenType Variations not supported on Windows at the moment +# Linux disabled until we can ship a statically linked newer version of FreeType +crbug.com/669453 [ Win Linux Mac10.9 Mac10.10 ] fast/text/variable-fonts/variable-box-font.html [ WontFix ] +crbug.com/669453 [ Win Linux Mac10.9 Mac10.10 ] http/tests/webfont/variable-box-font-arraybuffer.html [ WontFix ] +crbug.com/669453 [ Win Linux Mac10.9 Mac10.10 ] virtual/mojo-loading/http/tests/webfont/variable-box-font-arraybuffer.html [ WontFix ] + # prefer_compositing_to_lcd_text causes things to get composited regardless of their opaqueness, causing the test to fail crbug.com/381840 virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-scroll-background-opaque-to-transparent.html [ WontFix ] crbug.com/381840 virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-scroll-background-transparent-to-opaque.html [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 3705e47..22c8948 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -56,6 +56,8 @@ crbug.com/517449 [ Android ] images/optimize-contrast-image.html [ Failure ] +crbug.com/676561 [ Android ] images/paletted-png-with-color-profile.html [ Skip ] + crbug.com/531286 virtual/gpu/fast/canvas/yuv-video-on-accelerated-canvas.html [ Failure ] # The failure in the next line is due to virtual/gpu bots using osmesa # Under osmesa, skia's filter quality doesn't work correctly. This test is expected to pass @@ -800,8 +802,6 @@ crbug.com/596968 [ Win ] inspector-protocol/input/eventTimestamp.html [ Failure Pass ] -crbug.com/667286 inspector/sources/debugger-ui/function-generator-details.html [ NeedsManualRebaseline ] - crbug.com/487281 [ Mac ] fast/forms/select/menulist-narrow-width.html [ Failure ] crbug.com/543110 [ Mac ] fast/text/international/text-shaping-arabic.html [ Failure ] @@ -1974,6 +1974,8 @@ crbug.com/508728 imported/wpt/webstorage/document-domain.html [ Failure Timeout ] crbug.com/508728 virtual/mojo-localstorage/imported/wpt/webstorage/document-domain.html [ Failure Timeout ] +crbug.com/676572 [ Debug ] virtual/mojo-localstorage/imported/wpt/webstorage/storage_local_setitem_quotaexceedederr.html [ Pass Timeout ] + # Bug(dpranke) # crbug.com/508728 imported/wpt/workers/Worker_cross_origin_security_err.htm [ Timeout ] @@ -2036,8 +2038,7 @@ crbug.com/669473 imported/csswg-test/css-ui-3/box-sizing-019.html [ Failure ] crbug.com/669473 imported/csswg-test/css-ui-3/box-sizing-024.html [ Failure ] crbug.com/669473 imported/csswg-test/css-ui-3/box-sizing-025.html [ Failure ] -crbug.com/669490 imported/csswg-test/css-ui-3/caret-color-018.html [ Failure ] -crbug.com/669490 imported/csswg-test/css-ui-3/caret-color-020.html [ Failure ] +crbug.com/676295 imported/csswg-test/css-ui-3/caret-color-021.html [ Failure ] crbug.com/669473 imported/csswg-test/css-ui-3/text-overflow-002.html [ Failure ] crbug.com/669473 imported/csswg-test/css-ui-3/text-overflow-004.html [ Failure ] @@ -2363,7 +2364,6 @@ crbug.com/664855 virtual/scroll_customization/fast/scroll-behavior/main-frame-interrupted-scroll.html [ Pass Failure ] crbug.com/664856 virtual/sharedarraybuffer/fast/workers/worker-gc.html [ Pass Failure ] -crbug.com/664857 virtual/threaded/animations/animation-transition-collision-crash.html [ Pass Failure ] crbug.com/664857 virtual/threaded/animations/composited-animations-simple.html [ Pass Failure ] # Possible duplicate of crbug.com/665577
diff --git a/third_party/WebKit/LayoutTests/animations/animation-transition-collision-crash.html b/third_party/WebKit/LayoutTests/animations/animation-transition-collision-crash.html index a9601016..d0b756e 100644 --- a/third_party/WebKit/LayoutTests/animations/animation-transition-collision-crash.html +++ b/third_party/WebKit/LayoutTests/animations/animation-transition-collision-crash.html
@@ -1,3 +1,5 @@ +<!DOCTYPE html> +<meta charset="utf-8"> <script src="../resources/testharness.js"></script> <script src="../resources/testharnessreport.js"></script> <style> @@ -19,15 +21,17 @@ </style> <div id="target"></div> <script> -var asyncHandle = async_test('Don\'t crash when animating transitioned properties'); -var frameWait = 7; -function frame() { - frameWait--; - if (frameWait) { - requestAnimationFrame(frame); - } else { - asyncHandle.done(); +'use strict'; +async_test(t => { + let frameWait = 5; + function frame() { + frameWait--; + if (frameWait) { + requestAnimationFrame(frame); + } else { + t.done(); + } } -} -requestAnimationFrame(frame); -</script> \ No newline at end of file + requestAnimationFrame(frame); +}, 'Don\'t crash when animating transitioned properties'); +</script>
diff --git a/third_party/WebKit/LayoutTests/animations/composition/caret-color-composition.html b/third_party/WebKit/LayoutTests/animations/composition/caret-color-composition.html new file mode 100644 index 0000000..82d5d6d --- /dev/null +++ b/third_party/WebKit/LayoutTests/animations/composition/caret-color-composition.html
@@ -0,0 +1,43 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<style> +.target { + display: inline-block; + font-size: 60pt; + color: rgb(50, 50, 50); +} +.expected { + margin-right: 15px; +} +</style> +<body contenteditable> +<template id="target-template">T</template> +<script src="../interpolation/resources/interpolation-test.js"></script> +<script> +assertComposition({ + property: 'caret-color', + underlying: 'rgb(50, 50, 50)', + addFrom: 'rgb(100, 100, 100)', + addTo: 'rgb(200, 200, 200)', +}, [ + {at: -0.3, is: 'rgb(120, 120, 120)'}, + {at: 0, is: 'rgb(150, 150, 150)'}, + {at: 0.5, is: 'rgb(200, 200, 200)'}, + {at: 1, is: 'rgb(250, 250, 250)'}, + {at: 1.5, is: 'rgb(255, 255, 255)'}, +]); + +assertComposition({ + property: 'caret-color', + underlying: 'auto', + addFrom: 'rgb(100, 100, 100)', + addTo: 'rgb(200, 200, 200)', +}, [ + {at: -0.3, is: 'rgb(120, 120, 120)'}, + {at: 0, is: 'rgb(150, 150, 150)'}, + {at: 0.5, is: 'rgb(200, 200, 200)'}, + {at: 1, is: 'rgb(250, 250, 250)'}, + {at: 1.5, is: 'rgb(255, 255, 255)'}, +]); +</script> +</body>
diff --git a/third_party/WebKit/LayoutTests/animations/interpolation/caret-color-interpolation.html b/third_party/WebKit/LayoutTests/animations/interpolation/caret-color-interpolation.html new file mode 100644 index 0000000..3f0a8cfc --- /dev/null +++ b/third_party/WebKit/LayoutTests/animations/interpolation/caret-color-interpolation.html
@@ -0,0 +1,123 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<style> +.parent { + caret-color: blue; +} +.target { + display: inline-block; + font-size: 60pt; + caret-color: yellow; +} +.expected { + margin-right: 15px; +} +</style> +<body alink="red" link="blue" contenteditable> +<template id="target-template">T</template> +<script src="resources/interpolation-test.js"></script> +<script> +assertInterpolation({ + property: 'caret-color', + from: neutralKeyframe, + to: 'green', +}, [ + {at: -0.3, is: 'rgb(255, 255, 0)'}, + {at: 0, is: 'rgb(255, 255, 0)'}, + {at: 0.3, is: 'rgb(179, 217, 0)'}, + {at: 0.6, is: 'rgb(102, 179, 0)'}, + {at: 1, is: 'rgb(0, 128, 0)'}, + {at: 1.5, is: 'rgb(0, 65, 0)'}, +]); + +assertNoInterpolation({ + property: 'caret-color', + from: 'initial', + to: 'green', +}); + +assertNoInterpolation({ + property: 'caret-color', + from: 'auto', + to: 'green', +}); + +assertInterpolation({ + property: 'caret-color', + from: 'currentColor', + to: 'green', +}, [ + {at: -0.3, is: 'rgb(0, 0, 0)'}, + {at: 0, is: 'rgb(0, 0, 0)'}, + {at: 0.3, is: 'rgb(0, 38, 0)'}, + {at: 0.6, is: 'rgb(0, 77, 0)'}, + {at: 1, is: 'rgb(0, 128, 0)'}, + {at: 1.5, is: 'rgb(0, 192, 0)'}, +]); + +assertInterpolation({ + property: 'caret-color', + from: 'inherit', + to: 'green', +}, [ + {at: -0.3, is: 'rgb(0, 0, 255)'}, + {at: 0, is: 'rgb(0, 0, 255)'}, + {at: 0.3, is: 'rgb(0, 38, 179)'}, + {at: 0.6, is: 'rgb(0, 77, 102)'}, + {at: 1, is: 'rgb(0, 128, 0)'}, + {at: 1.5, is: 'rgb(0, 192, 0)'}, +]); + +assertInterpolation({ + property: 'caret-color', + from: 'unset', + to: 'green', +}, [ + {at: -0.3, is: 'rgb(0, 0, 255)'}, + {at: 0, is: 'rgb(0, 0, 255)'}, + {at: 0.3, is: 'rgb(0, 38, 179)'}, + {at: 0.6, is: 'rgb(0, 77, 102)'}, + {at: 1, is: 'rgb(0, 128, 0)'}, + {at: 1.5, is: 'rgb(0, 192, 0)'}, +]); + +assertInterpolation({ + property: 'caret-color', + from: 'black', + to: 'orange', +}, [ + {at: -0.3, is: 'rgb(0, 0, 0)'}, + {at: 0, is: 'rgb(0, 0, 0)'}, + {at: 0.3, is: 'rgb(77, 50, 0)'}, + {at: 0.6, is: 'rgb(153, 99, 0)'}, + {at: 1, is: 'rgb(255, 165, 0)'}, + {at: 1.5, is: 'rgb(255, 248, 0)'}, +]); + +assertInterpolation({ + property: 'caret-color', + from: '-webkit-activelink', + to: 'green', +}, [ + {at: -0.3, is: 'rgb(255, 0, 0)'}, + {at: 0, is: 'rgb(255, 0, 0)'}, + {at: 0.3, is: 'rgb(179, 38, 0)'}, + {at: 0.6, is: 'rgb(102, 77, 0)'}, + {at: 1, is: 'rgb(0, 128, 0)'}, + {at: 1.5, is: 'rgb(0, 192, 0)'}, +]); + +assertInterpolation({ + property: 'caret-color', + from: '-webkit-link', + to: 'green', +}, [ + {at: -0.3, is: 'rgb(0, 0, 255)'}, + {at: 0, is: 'rgb(0, 0, 255)'}, + {at: 0.3, is: 'rgb(0, 38, 179)'}, + {at: 0.6, is: 'rgb(0, 77, 102)'}, + {at: 1, is: 'rgb(0, 128, 0)'}, + {at: 1.5, is: 'rgb(0, 192, 0)'}, +]); +</script> +</body>
diff --git a/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js b/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js index d1346f2..e6739ef 100644 --- a/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js +++ b/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js
@@ -4,25 +4,20 @@ 'mockBatteryMonitor', ['device/battery/battery_monitor.mojom', 'device/battery/battery_status.mojom', - 'mojo/public/js/router', + 'mojo/public/js/bindings', ]).then(mojo => { - let [batteryMonitor, batteryStatus, router] = mojo.modules; + let [batteryMonitor, batteryStatus, bindings] = mojo.modules; - class MockBatteryMonitor extends batteryMonitor.BatteryMonitor.stubClass { + class MockBatteryMonitor { constructor(interfaceProvider) { - super(); interfaceProvider.addInterfaceOverrideForTesting( batteryMonitor.BatteryMonitor.name, - handle => this.connect_(handle)); + handle => this.bindingSet_.addBinding(this, handle)); this.interfaceProvider_ = interfaceProvider; this.pendingRequests_ = []; this.status_ = null; - } - - connect_(handle) { - this.router_ = new router.Router(handle); - this.router_.setIncomingReceiver(this); + this.bindingSet_ = new bindings.BindingSet(batteryMonitor.BatteryMonitor); } queryNextStatus() {
diff --git a/third_party/WebKit/LayoutTests/csspaint/style-background-image-expected.txt b/third_party/WebKit/LayoutTests/csspaint/style-background-image-expected.txt index 54ea2db..65fd4a2 100644 --- a/third_party/WebKit/LayoutTests/csspaint/style-background-image-expected.txt +++ b/third_party/WebKit/LayoutTests/csspaint/style-background-image-expected.txt
@@ -1,6 +1,6 @@ CONSOLE MESSAGE: line 14: --bar: [null] CONSOLE MESSAGE: line 14: --foo: [CSSStyleValue= bar] -CONSOLE MESSAGE: line 14: align-items: [CSSStyleValue=normal] +CONSOLE MESSAGE: line 14: align-items: [CSSKeywordValue=normal] CONSOLE MESSAGE: line 14: border-radius: [CSSStyleValue=2px] This tests the style information in the paint callback. @@ -8,5 +8,5 @@ --bar: [null] --foo: [CSSStyleValue= bar] -align-items: [CSSStyleValue=normal] +align-items: [CSSKeywordValue=normal] border-radius: [CSSStyleValue=2px]
diff --git a/third_party/WebKit/LayoutTests/csspaint/style-background-image.html b/third_party/WebKit/LayoutTests/csspaint/style-background-image.html index edb2d78..0fe62fc 100644 --- a/third_party/WebKit/LayoutTests/csspaint/style-background-image.html +++ b/third_party/WebKit/LayoutTests/csspaint/style-background-image.html
@@ -33,7 +33,7 @@ <p>See the devtools console for test output. The console should log:</p> --bar: [null]<br> --foo: [CSSStyleValue= bar]<br> -align-items: [CSSStyleValue=normal]<br> +align-items: [CSSKeywordValue=normal]<br> border-radius: [CSSStyleValue=2px] </body> </html>
diff --git a/third_party/WebKit/LayoutTests/custom-elements/spec/construct.html b/third_party/WebKit/LayoutTests/custom-elements/spec/construct.html index 4d33bb67..4bae586 100644 --- a/third_party/WebKit/LayoutTests/custom-elements/spec/construct.html +++ b/third_party/WebKit/LayoutTests/custom-elements/spec/construct.html
@@ -122,11 +122,31 @@ } } w.customElements.define('a-a', A); + let e = w.document.createElement('a-a'); + assert_true(e.matches(':defined'), + 'constructing an autonomous custom element with create element should ' + + 'not throw InvalidStateError ' + + 'and should return a "custom" element'); +}, 'Already constructed marker, create element'); + +test_with_window((w) => { + let flag = true; + class A extends w.HTMLElement { + constructor() { + if (flag) { + flag = false; + new A(); + } + super(); + } + } + w.customElements.define('a-a', A); + let d = w.document.createElement('div'); assert_reports(w, 'INVALID_STATE_ERR', () => { - w.document.createElement('a-a'); + d.innerHTML = '<a-a>'; }, 'Creating an element that is already constructed marker should report ' + 'InvalidStateError'); -}, 'Already constructed marker, create element'); +}, 'Already constructed marker, fragment parsing should set marker'); test_with_window((w) => { let errors = [];
diff --git a/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html b/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html index 9826f8aa..9b5b1ddec 100644 --- a/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html +++ b/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html
@@ -68,6 +68,31 @@ }); </script> +<template data-test="foreign content insertion executes connected"> + <script> + 'use strict'; + + customElements.define('a-a', class extends HTMLElement { + constructor() { + super(); + } + connectedCallback() { + window.connectedChildNodeCount = this.childNodes.length; + } + }); + </script> + <a-a><div></div></a-a> +</template> +<script> +'use strict'; + +test_with_content((w) => { + assert_equals(w.connectedChildNodeCount, 0, + 'the parser should have run the connected callback when inserting the ' + + 'element, before continuing tree construction'); +}); +</script> + <template data-test="element creation failure produces unknown element"> <script> 'use strict';
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Geolocation/resources/geolocation-mock.js b/third_party/WebKit/LayoutTests/fast/dom/Geolocation/resources/geolocation-mock.js index 836c79d..40b657d 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/Geolocation/resources/geolocation-mock.js +++ b/third_party/WebKit/LayoutTests/fast/dom/Geolocation/resources/geolocation-mock.js
@@ -10,9 +10,9 @@ ['device/geolocation/public/interfaces/geolocation.mojom', 'third_party/WebKit/public/platform/modules/permissions/permission.mojom', 'third_party/WebKit/public/platform/modules/permissions/permission_status.mojom', - 'mojo/public/js/router', + 'mojo/public/js/bindings', ]).then(mojo => { - let [geolocation, permission, permissionStatus, router] = + let [geolocation, permission, permissionStatus, bindings] = mojo.modules; class GeolocationServiceMock { @@ -51,6 +51,11 @@ this.permissionStatus_ = permissionStatus.PermissionStatus.ASK; this.rejectPermissionConnections_ = false; this.rejectGeolocationConnections_ = false; + + this.geolocationBindingSet_ = new bindings.BindingSet( + geolocation.GeolocationService); + this.permissionBindingSet_ = new bindings.BindingSet( + permission.PermissionService); } connectGeolocation_(handle) { @@ -58,10 +63,7 @@ mojo.core.close(handle); return; } - this.geolocationStub_ = new geolocation.GeolocationService.stubClass( - this); - this.geolocationRouter_ = new router.Router(handle); - this.geolocationRouter_.setIncomingReceiver(this.geolocationStub_); + this.geolocationBindingSet_.addBinding(this, handle); } connectPermission_(handle) { @@ -69,9 +71,7 @@ mojo.core.close(handle); return; } - this.permissionStub_ = new permission.PermissionService.stubClass(this); - this.permissionRouter_ = new router.Router(handle); - this.permissionRouter_.setIncomingReceiver(this.permissionStub_); + this.permissionBindingSet_.addBinding(this, handle); } setHighAccuracy(highAccuracy) {
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/client-rects-sole-empty-block.html b/third_party/WebKit/LayoutTests/fast/multicol/client-rects-sole-empty-block.html new file mode 100644 index 0000000..f074f658 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/multicol/client-rects-sole-empty-block.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<style>body { margin:8px; }</style> +<div style="margin:100px; columns:2; column-gap:20px; width:420px;"> + <div id="empty"></div> +</div> +<script> + test(function() { + var rects = document.getElementById("empty").getClientRects(); + assert_equals(rects.length, 1); + assert_equals(rects[0].left, 108); + assert_equals(rects[0].top, 100); + assert_equals(rects[0].right, 308); + assert_equals(rects[0].bottom, 100); + }, "Zero-height block in otherwise empty multicol"); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font-expected.html b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font-expected.html new file mode 100644 index 0000000..6d537932 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font-expected.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + @font-face { + font-family: variabletest_box; + src: url(../../../http/tests/resources/variabletest_box.ttf); + } + + body { + font-family: variabletest_box, sans-serif; + font-size: 200px; + } +</style> +â–„ â–€
diff --git a/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font.html b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font.html new file mode 100644 index 0000000..c83abcc --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-box-font.html
@@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + @font-face { + font-family: variabletest_box; + src: url(../../../http/tests/resources/variabletest_box.ttf); + } + + body { + font-family: variabletest_box, + sans-serif; + font-size: 200px; + } + + .a_low { + font-variation-settings: "upwd" 0; + } + + .a_up { + font-variation-settings: "upwd" 350; + } +</style> +<!-- The variabletest_box font has an A glyph that looks like a lower half box, + with deltas on the 'upwd' variation axis that allow shifting the box up. At + 350, the box is at the top. The font also has two glyphs for UPPER HALF BLOCK + and LOWER HALF BLOCK, which look identical to the respective variations of A. +--> +A <span class="a_up">A</span>
diff --git a/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-mac-system-font.html b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-mac-system-font.html new file mode 100644 index 0000000..2cbd637 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/text/variable-fonts/variable-mac-system-font.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<style> +.variation { +font-family: Skia, sans-serif; +font-size: 150px; +} + +#narrow { +font-variation-settings: "wdth" 0.61998; +} + +#wide { +font-variation-settings: "wdth" 1.29999; +} +</style> +<span class="variation" id="narrow">wwwwide</span><br> +<span class="variation" id="wide">wwwwide</span> +<script> +setup({ explicit_done: true }); +test(function() { assert_true(narrow.getBoundingClientRect().width < + wide.getBoundingClientRect().width * 0.6); }, + "Narrow Skia variation is less than 60% as wide."); +done(); +</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js b/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js index e404dbc..9e1f966 100644 --- a/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js +++ b/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
@@ -11,15 +11,15 @@ let budgetServiceMock = loadMojoModules( 'budgetServiceMock', ['third_party/WebKit/public/platform/modules/budget_service/budget_service.mojom', - 'mojo/public/js/router' + 'mojo/public/js/bindings' ]).then(mojo => { - const [budgetService, router] = mojo.modules; + const [budgetService, bindings] = mojo.modules; class BudgetServiceMock { constructor(interfaceProvider) { interfaceProvider.addInterfaceOverrideForTesting( budgetService.BudgetService.name, - handle => this.connectBudgetService_(handle)); + handle => this.bindingSet_.addBinding(this, handle)); this.interfaceProvider_ = interfaceProvider; @@ -27,12 +27,7 @@ this.cost_ = {}; this.budget_ = []; this.error_ = budgetService.BudgetServiceErrorType.NONE; - } - - connectBudgetService_(handle) { - this.budgetServiceStub_ = new budgetService.BudgetService.stubClass(this); - this.budgetServiceRouter_ = new router.Router(handle); - this.budgetServiceRouter_.setIncomingReceiver(this.budgetServiceStub_); + this.bindingSet_ = new bindings.BindingSet(budgetService.BudgetService); } // This is called directly from test JavaScript to set up the return value
diff --git a/third_party/WebKit/LayoutTests/http/tests/resources/variabletest_box.ttf b/third_party/WebKit/LayoutTests/http/tests/resources/variabletest_box.ttf new file mode 100644 index 0000000..840a78e --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/resources/variabletest_box.ttf Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer-expected.html b/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer-expected.html new file mode 100644 index 0000000..9adfa624 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer-expected.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + @font-face { + font-family: variabletest_box; + src: url(/resources/variabletest_box.ttf); + } + + body { + font-family: variabletest_box, sans-serif; + font-size: 200px; + } +</style> +â–„ â–€
diff --git a/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer.html b/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer.html new file mode 100644 index 0000000..ebe0bb40 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/webfont/variable-box-font-arraybuffer.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script> +if(window.testRunner) + testRunner.waitUntilDone(); + +fetch('/resources/variabletest_box.ttf').then(function(response) { + response.arrayBuffer().then(function(fontBuffer) { + var fontface = new FontFace("variabletest_box", fontBuffer); + fontface.load().then(function() { + document.fonts.add(fontface); + if(window.testRunner) + testRunner.notifyDone(); + }); + }); +}); +</script> +<style> + body { + font-family: variabletest_box; + font-size: 200px; + } + + .a_low { + font-variation-settings: "upwd" 0; + } + + .a_up { + font-variation-settings: "upwd" 350; + } +</style> +<!-- The variabletest_box font has an A glyph that looks like a lower half box, + with deltas on the 'upwd' variation axis that allow shifting the box up. At + 350, the box is at the top. The font also has two glyphs for UPPER HALF BLOCK + and LOWER HALF BLOCK, which look identical to the respective variations of A. +--> +A <span class="a_up">A</span>
diff --git a/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js b/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js index e712c32..5d543e6 100644 --- a/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js +++ b/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js
@@ -4,15 +4,14 @@ 'mockImageCapture', ['media/capture/mojo/image_capture.mojom', 'mojo/public/js/bindings', - 'mojo/public/js/connection', 'content/public/renderer/interfaces', - ], (imageCapture, bindings, connection, interfaces) => { + ], (imageCapture, bindings, interfaces) => { class MockImageCapture { constructor() { interfaces.addInterfaceOverrideForTesting( imageCapture.ImageCapture.name, - pipe => this.bindToPipe(pipe)); + handle => this.bindingSet_.addBinding(this, handle)); this.capabilities_ = { capabilities : { iso : { min : 100.0, max : 12000.0, current : 400.0, step : 1.0 }, @@ -34,11 +33,7 @@ sharpness : { min : 4.0, max : 7.0, current : 7.0, step : 1.0 }, }}; this.settings_ = null; - } - - bindToPipe(pipe) { - this.stub_ = connection.bindHandleToStub(pipe, imageCapture.ImageCapture); - bindings.StubBindings(this.stub_).delegate = this; + this.bindingSet_ = new bindings.BindingSet(imageCapture.ImageCapture); } getCapabilities(source_id) {
diff --git a/third_party/WebKit/LayoutTests/images/pixel-crack-image-background-webkit-transform-scale.html b/third_party/WebKit/LayoutTests/images/pixel-crack-image-background-webkit-transform-scale.html index ba3610bb..eba164e 100644 --- a/third_party/WebKit/LayoutTests/images/pixel-crack-image-background-webkit-transform-scale.html +++ b/third_party/WebKit/LayoutTests/images/pixel-crack-image-background-webkit-transform-scale.html
@@ -23,6 +23,23 @@ document.body.appendChild(div); } } + if (window.testRunner) { + testRunner.waitUntilDone(); + window.addEventListener('load', () => { + // This is a workaroud for an issue that the load event may be + // dispatched before loading the background image. See + // https://crbug.com/675870. + const id = setInterval(() => { + // Force layout. + document.body.offsetTop; + if (internals.isLoading('./resources/pixel-crack-image-background-webkit-transform-scale.png')) { + return; + } + clearInterval(id); + testRunner.notifyDone(); + }, 50); + }); + } </script> </head> <body onload="runTest()">
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/css-ui-3/caret-color-021.html b/third_party/WebKit/LayoutTests/imported/csswg-test/css-ui-3/caret-color-021.html new file mode 100644 index 0000000..1f1d0ae --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/css-ui-3/caret-color-021.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Basic User Interface Test: Default caret-color test animation</title> +<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com"> +<link rel="help" href="http://www.w3.org/TR/css3-ui/#caret-color"> +<link rel="help" href="https://www.w3.org/TR/web-animations-1/#dom-animatable-animate"> +<link rel="help" href="https://www.w3.org/TR/css3-color/#color0"> +<meta name="assert" content="Test checks that the default value for caret-color property, which is 'auto', is not interpolatible."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @keyframes caret-color-to-lime { + to { caret-color: lime; } + } + + #textarea { + color: magenta; + animation: caret-color-to-lime 2s -1s paused; + } +</style> +<body> + <textarea id="textarea"></textarea> + <div id=log></div> + + <script> + test( + function(){ + var textarea = document.getElementById("textarea"); + assert_equals(getComputedStyle(textarea).caretColor, 'rgb(0, 255, 0)'); + }, "Default caret-color is not interpolatible"); +</script> +</body> +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/custom-elements/parser/parser-uses-constructed-element-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/custom-elements/parser/parser-uses-constructed-element-expected.txt index 8835656..50198f6 100644 --- a/third_party/WebKit/LayoutTests/imported/wpt/custom-elements/parser/parser-uses-constructed-element-expected.txt +++ b/third_party/WebKit/LayoutTests/imported/wpt/custom-elements/parser/parser-uses-constructed-element-expected.txt
@@ -1,8 +1,5 @@ -CONSOLE ERROR: line 14: Uncaught InvalidStateError: Failed to construct 'HTMLElement': this instance is already constructed -CONSOLE ERROR: line 14: Uncaught InvalidStateError: Failed to construct 'HTMLElement': this instance is already constructed This is a testharness.js-based test. -Harness Error. harness_status.status = 1 , harness_status.message = Uncaught InvalidStateError: Failed to construct 'HTMLElement': this instance is already constructed -FAIL HTML parser must use the returned value of the custom element constructor instead of the one created before super() call assert_equals: expected (string) "HTML parser must insert the synchronously constructed custom element" but got (boolean) false -FAIL HTML parser must use the returned value of the custom element constructor instead using the one created in super() call assert_equals: expected (string) "HTML parser must insert the synchronously constructed custom element" but got (boolean) false +FAIL HTML parser must use the returned value of the custom element constructor instead of the one created before super() call assert_equals: expected (string) "HTML parser must insert the synchronously constructed custom element" but got (boolean) true +FAIL HTML parser must use the returned value of the custom element constructor instead using the one created in super() call assert_equals: expected (string) "HTML parser must insert the synchronously constructed custom element" but got (boolean) true Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/function-generator-details-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/function-generator-details-expected.txt index 0ad0a51..5245df17 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/function-generator-details-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/function-generator-details-expected.txt
@@ -14,6 +14,7 @@ lineNumber = 13 columnNumber = 13 script is valid: yes +[[Scopes]] = Scopes[2] Running: testIterSuspended iterSuspended: type = object, subtype = generator @@ -28,6 +29,7 @@ lineNumber = 15 columnNumber = 4 script is valid: yes +[[Scopes]] = Scopes[2] Running: testIterClosed iterClosed: type = object, subtype = generator @@ -42,6 +44,7 @@ lineNumber = 13 columnNumber = 13 script is valid: yes +[[Scopes]] = Scopes[2] Running: testIterObjGenerator iterObjGenerator: type = object, subtype = generator @@ -56,6 +59,7 @@ lineNumber = 24 columnNumber = 8 script is valid: yes +[[Scopes]] = Scopes[2] Running: testAnonymousGenIter anonymousGenIter: type = object, subtype = generator @@ -69,4 +73,5 @@ lineNumber = 38 columnNumber = 4 script is valid: yes +[[Scopes]] = Scopes[2]
diff --git a/third_party/WebKit/LayoutTests/media/mediasession/mojo/resources/mediasessionservice-mock.js b/third_party/WebKit/LayoutTests/media/mediasession/mojo/resources/mediasessionservice-mock.js index 2a73642..963e40e0 100644 --- a/third_party/WebKit/LayoutTests/media/mediasession/mojo/resources/mediasessionservice-mock.js +++ b/third_party/WebKit/LayoutTests/media/mediasession/mojo/resources/mediasessionservice-mock.js
@@ -42,9 +42,9 @@ let mediaSessionServiceMock = loadMojoModules( 'mediaSessionServiceMock', ['third_party/WebKit/public/platform/modules/mediasession/media_session.mojom', - 'mojo/public/js/router', + 'mojo/public/js/bindings', ]).then(mojo => { - let [mediaSessionService, router] = mojo.modules; + let [mediaSessionService, bindings] = mojo.modules; MediaSessionAction = mediaSessionService.MediaSessionAction; MediaSessionPlaybackState = mediaSessionService.MediaSessionPlaybackState; @@ -53,15 +53,11 @@ constructor(interfaceProvider) { interfaceProvider.addInterfaceOverrideForTesting( mediaSessionService.MediaSessionService.name, - handle => this.connectMediaSessionService_(handle)); + handle => this.bindingSet_.addBinding(this, handle)); this.interfaceProvider_ = interfaceProvider; this.pendingResponse_ = null; - } - - connectMediaSessionService_(handle) { - this.mediaSessionServiceStub_ = new mediaSessionService.MediaSessionService.stubClass(this); - this.mediaSessionServiceRouter_ = new router.Router(handle); - this.mediaSessionServiceRouter_.setIncomingReceiver(this.mediaSessionServiceStub_); + this.bindingSet_ = new bindings.BindingSet( + mediaSessionService.MediaSessionService); } setMetadata(metadata) {
diff --git a/third_party/WebKit/LayoutTests/nfc/resources/nfc-helpers.js b/third_party/WebKit/LayoutTests/nfc/resources/nfc-helpers.js index f9dcca2..ff8f03e 100644 --- a/third_party/WebKit/LayoutTests/nfc/resources/nfc-helpers.js +++ b/third_party/WebKit/LayoutTests/nfc/resources/nfc-helpers.js
@@ -70,9 +70,8 @@ function nfc_mocks(mojo) { return define('NFC mocks', [ 'mojo/public/js/bindings', - 'mojo/public/js/connection', 'device/nfc/nfc.mojom', - ], (bindings, connection, nfc) => { + ], (bindings, nfc) => { function toMojoNFCRecordType(type) { switch (type) { @@ -259,6 +258,8 @@ class MockNFC { constructor() { + this.bindingSet = new bindings.BindingSet(nfc.NFC); + this.hw_status_ = NFCHWStatus.ENABLED; this.pushed_message_ = null; this.push_options_ = null; @@ -270,7 +271,7 @@ this.watchers_ = []; } - // NFC.stubClass delegate functions + // NFC delegate functions push(message, options) { let error = this.isReady(); if (error) @@ -338,14 +339,6 @@ return Promise.resolve(createNFCError(null)); } - - // Mock utility functions - bindToPipe(pipe) { - this.stub_ = connection.bindHandleToStub( - pipe, nfc.NFC); - bindings.StubBindings(this.stub_).delegate = this; - } - isReady() { if (this.hw_status_ === NFCHWStatus.DISABLED) return createNFCError(nfc.NFCErrorType.DEVICE_DISABLED); @@ -408,8 +401,8 @@ let mockNFC = new MockNFC; mojo.frameInterfaces.addInterfaceOverrideForTesting( nfc.NFC.name, - pipe => { - mockNFC.bindToPipe(pipe); + handle => { + mockNFC.bindingSet.addBinding(mockNFC, handle); }); return Promise.resolve({
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png index 464fec30..d5a3eb8d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/presentation/resources/presentation-service-mock.js b/third_party/WebKit/LayoutTests/presentation/resources/presentation-service-mock.js index 9b2dbd0..6df94aa 100644 --- a/third_party/WebKit/LayoutTests/presentation/resources/presentation-service-mock.js +++ b/third_party/WebKit/LayoutTests/presentation/resources/presentation-service-mock.js
@@ -8,23 +8,19 @@ 'presentationServiceMock', [ 'third_party/WebKit/public/platform/modules/presentation/presentation.mojom', - 'mojo/public/js/router', + 'mojo/public/js/bindings', ]).then(mojo => { - let [ presentationService, router ] = mojo.modules; + let [ presentationService, bindings ] = mojo.modules; class PresentationServiceMock { constructor(interfaceProvider) { interfaceProvider.addInterfaceOverrideForTesting( presentationService.PresentationService.name, - handle => this.connectPresentationService_(handle)); + handle => this.bindingSet_.addBinding(this, handle)); this.interfaceProvider_ = interfaceProvider; this.pendingResponse_ = null; - } - - connectPresentationService_(handle) { - this.presentationServiceStub_ = new presentationService.PresentationService.stubClass(this); - this.presentationServiceRouter_ = new router.Router(handle); - this.presentationServiceRouter_.setIncomingReceiver(this.presentationServiceStub_); + this.bindingSet_ = new bindings.BindingSet( + presentationService.PresentationService); } startSession(urls) {
diff --git a/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js b/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js index 262145f..03f32d9 100644 --- a/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js +++ b/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js
@@ -21,10 +21,9 @@ return define('Generic Sensor API mocks', [ 'mojo/public/js/core', 'mojo/public/js/bindings', - 'mojo/public/js/connection', 'device/generic_sensor/public/interfaces/sensor_provider.mojom', 'device/generic_sensor/public/interfaces/sensor.mojom', - ], (core, bindings, connection, sensor_provider, sensor) => { + ], (core, bindings, sensor_provider, sensor) => { // Helper function that returns resolved promise with result. function sensorResponse(success) { @@ -33,9 +32,8 @@ // Class that mocks Sensor interface defined in sensor.mojom class MockSensor { - constructor(stub, handle, offset, size, reportingMode) { + constructor(sensorRequest, handle, offset, size, reportingMode) { this.client_ = null; - this.stub_ = stub; this.expects_modified_reading_ = false; this.start_should_fail_ = false; this.reporting_mode_ = reportingMode; @@ -52,10 +50,11 @@ this.buffer_array_ = rv.buffer; this.buffer_ = new Float64Array(this.buffer_array_); this.resetBuffer(); - bindings.StubBindings(this.stub_).delegate = this; - bindings.StubBindings(this.stub_).connectionErrorHandler = () => { + this.binding_ = new bindings.Binding(sensor.Sensor, this, + sensorRequest); + this.binding_.setConnectionErrorHandler(() => { this.reset(); - }; + }); } // Returns default configuration. @@ -137,7 +136,7 @@ this.resetBuffer(); core.unmapBuffer(this.buffer_array_); this.buffer_array_ = null; - bindings.StubBindings(this.stub_).close(); + this.binding_.close(); } // Zeroes shared buffer. @@ -241,6 +240,8 @@ this.resolve_func_ = null; this.is_continuous_ = false; this.max_frequency_ = 60; + this.binding_ = new bindings.Binding(sensor_provider.SensorProvider, + this); } // Returns initialized Sensor proxy to the client. @@ -258,8 +259,7 @@ } if (this.active_sensor_ == null) { - var stub = connection.bindHandleToStub(request.handle, sensor.Sensor); - let mockSensor = new MockSensor(stub, this.shared_buffer_handle_, + let mockSensor = new MockSensor(request, this.shared_buffer_handle_, offset, this.reading_size_in_bytes_, reporting_mode); this.active_sensor_ = mockSensor; } @@ -285,21 +285,17 @@ this.resolve_func_(this.active_sensor_); } - var client_request = new bindings.InterfaceRequest( - connection.bindProxy(proxy => { - this.active_sensor_.client_ = proxy; - }, sensor.SensorClient)); - return getSensorResponse(init_params, client_request); + this.active_sensor_.client_ = new sensor.SensorClientPtr(); + return getSensorResponse( + init_params, bindings.makeRequest(this.active_sensor_.client_)); } // Binds object to mojo message pipe bindToPipe(pipe) { - this.stub_ = connection.bindHandleToStub( - pipe, sensor_provider.SensorProvider); - bindings.StubBindings(this.stub_).delegate = this; - bindings.StubBindings(this.stub_).connectionErrorHandler = () => { + this.binding_.bind(pipe); + this.binding_.setConnectionErrorHandler(() => { this.reset(); - }; + }); } // Mock functions @@ -315,8 +311,7 @@ this.resolve_func_ = null; this.max_frequency_ = 60; this.is_continuous_ = false; - if (this.stub_) - bindings.StubBindings(this.stub_).close(); + this.binding_.close(); } // Sets flag that forces mock SensorProvider to fail when getSensor() is
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js index ed870ba..8395550 100644 --- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js +++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
@@ -4,22 +4,18 @@ 'mockBarcodeDetection', ['third_party/WebKit/public/platform/modules/shapedetection/barcodedetection.mojom', 'mojo/public/js/bindings', - 'mojo/public/js/connection', 'mojo/public/js/core', 'content/public/renderer/frame_interfaces', - ], (barcodeDetection, bindings, connection, mojo, interfaces) => { + ], (barcodeDetection, bindings, mojo, interfaces) => { class MockBarcodeDetection { constructor() { + this.bindingSet_ = new bindings.BindingSet( + barcodeDetection.BarcodeDetection); + interfaces.addInterfaceOverrideForTesting( barcodeDetection.BarcodeDetection.name, - pipe => this.bindToPipe(pipe)); - } - - bindToPipe(pipe) { - this.stub_ = connection.bindHandleToStub(pipe, - barcodeDetection.BarcodeDetection); - bindings.StubBindings(this.stub_).delegate = this; + handle => this.bindingSet_.addBinding(this, handle)); } detect(frame_data, width, height) {
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js index c934619..62220e8 100644 --- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js +++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
@@ -5,30 +5,22 @@ ['third_party/WebKit/public/platform/modules/shapedetection/facedetection.mojom', 'third_party/WebKit/public/platform/modules/shapedetection/facedetection_provider.mojom', 'mojo/public/js/bindings', - 'mojo/public/js/connection', 'mojo/public/js/core', 'content/public/renderer/frame_interfaces', - ], (faceDetection, faceDetectionProvider, bindings, connection, mojo, interfaces) => { + ], (faceDetection, faceDetectionProvider, bindings, mojo, interfaces) => { class MockFaceDetectionProvider { constructor() { + this.bindingSet_ = new bindings.BindingSet( + faceDetectionProvider.FaceDetectionProvider); + interfaces.addInterfaceOverrideForTesting( faceDetectionProvider.FaceDetectionProvider.name, - pipe => this.bindToPipe(pipe)); - } - - bindToPipe(pipe) { - this.stub_ = connection.bindHandleToStub( - pipe, faceDetectionProvider.FaceDetectionProvider); - bindings.StubBindings(this.stub_).delegate = this; + handle => this.bindingSet_.addBinding(this, handle)); } createFaceDetection(request, options) { - this.mock_service_ = new MockFaceDetection(options); - this.mock_service_.stub_ = connection.bindHandleToStub( - request.handle, faceDetection.FaceDetection); - bindings.StubBindings(this.mock_service_.stub_).delegate = - this.mock_service_; + this.mock_service_ = new MockFaceDetection(request, options); } getFrameData() { @@ -45,9 +37,11 @@ } class MockFaceDetection { - constructor(options) { + constructor(request, options) { this.maxDetectedFaces_ = options.max_detected_faces; this.fastMode_ = options.fast_mode; + this.binding_ = new bindings.Binding(faceDetection.FaceDetection, this, + request); } detect(frame_data, width, height) {
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/animation-direction-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/animation-direction-expected.txt index 46e0b02..94bf551 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/animation-direction-expected.txt +++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/animation-direction-expected.txt
@@ -15,15 +15,15 @@ PASS Setting animation-direction to invalid value hello throws PASS Setting animation-direction to invalid value [object Object] throws PASS Setting animation-direction to invalid value CSSKeywordValue throws -FAIL Getting animation-direction when it is set to normal assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting animation-direction when it is set to reverse assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting animation-direction when it is set to alternate assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting animation-direction when it is set to alternate-reverse assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting animation-direction when it is set to initial assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting animation-direction when it is set to inherit assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting animation-direction when it is set to unset assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL getAll for single-valued animation-direction assert_equals: Returned type is incorrect: expected "CSSKeywordValue" but got "CSSStyleValue" -FAIL getAll for list-valued animation-direction assert_equals: Expected getAll to return an array containing two instances of CSSStyleValue expected 2 but got 1 +PASS Getting animation-direction when it is set to normal +PASS Getting animation-direction when it is set to reverse +PASS Getting animation-direction when it is set to alternate +PASS Getting animation-direction when it is set to alternate-reverse +PASS Getting animation-direction when it is set to initial +PASS Getting animation-direction when it is set to inherit +PASS Getting animation-direction when it is set to unset +PASS getAll for single-valued animation-direction +PASS getAll for list-valued animation-direction PASS Delete animation-direction removes the value from the styleMap PASS animation-direction shows up in getProperties FAIL Set animation-direction to a sequence assert_equals: expected "normal, normal" but got "normal normal"
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom-expected.txt index 1cb38a58..8ca5f42 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom-expected.txt +++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom-expected.txt
@@ -16,10 +16,10 @@ PASS Setting bottom to invalid value hello throws PASS Setting bottom to invalid value [object Object] throws PASS Setting bottom to invalid value CSSKeywordValue throws -FAIL Getting bottom when it is set to auto assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting bottom when it is set to initial assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting bottom when it is set to inherit assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting bottom when it is set to unset assert_true: result instanceof CSSKeywordValue: expected true got false +PASS Getting bottom when it is set to auto +PASS Getting bottom when it is set to initial +PASS Getting bottom when it is set to inherit +PASS Getting bottom when it is set to unset PASS Getting bottom with a CSSSimpleLength whose value is 1px PASS Getting bottom with a CSSSimpleLength whose value is 2% PASS Getting bottom with a CSSSimpleLength whose value is 3em
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js index 04551e0..e3837b5 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js +++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js
@@ -219,11 +219,7 @@ let result = element.styleMap.getAll(propertyName); assert_equals(result.length, 2, 'Expected getAll to return an array containing two instances ' + - 'of CSSStyleValue'); - assert_equals(result[0].constructor, CSSStyleValue, - 'Expected first result to be an instance of CSSStyleValue'); - assert_equals(result[1].constructor, CSSStyleValue, - 'Expected second result to be an instance of CSSStyleValue'); + 'of ' + validObject.constructor.name); assert_equals(result[0].constructor.name, validObject.constructor.name); assert_equals(result[1].constructor.name, validObject.constructor.name); assert_equals(result[0].cssText, validObject.cssText);
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/transform-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/transform-expected.txt deleted file mode 100644 index b9af4b0..0000000 --- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/transform-expected.txt +++ /dev/null
@@ -1,88 +0,0 @@ -This is a testharness.js-based test. -PASS Setting transform to none -PASS Setting transform to initial -PASS Setting transform to inherit -PASS Setting transform to unset -PASS Setting transform to CSSTransformValue with value skew(30deg, 0deg) -PASS Setting transform to CSSTransformValue with value skew(10rad, 0deg) -PASS Setting transform to CSSTransformValue with value skew(2grad, 0deg) -PASS Setting transform to CSSTransformValue with value skew(0.2turn, 0deg) -PASS Setting transform to CSSTransformValue with value skew(0deg, 30deg) -PASS Setting transform to CSSTransformValue with value skew(0deg, 10rad) -PASS Setting transform to CSSTransformValue with value skew(0deg, 2grad) -PASS Setting transform to CSSTransformValue with value skew(0deg, 0.2turn) -PASS Setting transform to CSSTransformValue with value rotate(30deg) -PASS Setting transform to CSSTransformValue with value rotate(10rad) -PASS Setting transform to CSSTransformValue with value rotate(2grad) -PASS Setting transform to CSSTransformValue with value rotate(0.2turn) -PASS Setting transform to CSSTransformValue with value rotate3d(1, 2, 3, 30deg) -PASS Setting transform to CSSTransformValue with value rotate3d(1, 2, 3, 10rad) -PASS Setting transform to CSSTransformValue with value rotate3d(1, 2, 3, 2grad) -PASS Setting transform to CSSTransformValue with value rotate3d(1, 2, 3, 0.2turn) -PASS Setting transform to invalid value CSSSimpleLength throws -PASS Setting transform to invalid value null throws -PASS Setting transform to invalid value undefined throws -PASS Setting transform to invalid value true throws -PASS Setting transform to invalid value false throws -PASS Setting transform to invalid value 1 throws -PASS Setting transform to invalid value hello throws -PASS Setting transform to invalid value [object Object] throws -PASS Setting transform to invalid value CSSKeywordValue throws -FAIL Getting transform when it is set to none assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting transform when it is set to initial assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting transform when it is set to inherit assert_true: result instanceof CSSKeywordValue: expected true got false -FAIL Getting transform when it is set to unset assert_true: result instanceof CSSKeywordValue: expected true got false -PASS Getting transform with a CSSTransformValue whose value is skew(30deg, 0deg) -PASS Getting transform with a CSSTransformValue whose value is skew(10rad, 0deg) -PASS Getting transform with a CSSTransformValue whose value is skew(2grad, 0deg) -PASS Getting transform with a CSSTransformValue whose value is skew(0.2turn, 0deg) -PASS Getting transform with a CSSTransformValue whose value is skew(0deg, 30deg) -PASS Getting transform with a CSSTransformValue whose value is skew(0deg, 10rad) -PASS Getting transform with a CSSTransformValue whose value is skew(0deg, 2grad) -PASS Getting transform with a CSSTransformValue whose value is skew(0deg, 0.2turn) -PASS Getting transform with a CSSTransformValue whose value is rotate(30deg) -PASS Getting transform with a CSSTransformValue whose value is rotate(10rad) -PASS Getting transform with a CSSTransformValue whose value is rotate(2grad) -PASS Getting transform with a CSSTransformValue whose value is rotate(0.2turn) -PASS Getting transform with a CSSTransformValue whose value is rotate3d(1, 2, 3, 30deg) -PASS Getting transform with a CSSTransformValue whose value is rotate3d(1, 2, 3, 10rad) -PASS Getting transform with a CSSTransformValue whose value is rotate3d(1, 2, 3, 2grad) -PASS Getting transform with a CSSTransformValue whose value is rotate3d(1, 2, 3, 0.2turn) -PASS Getting transform when it is set to "skew(45deg)" via a string -PASS Getting transform when it is set to "skew(1rad)" via a string -PASS Getting transform when it is set to "skew(6.2grad)" via a string -PASS Getting transform when it is set to "skew(0.31turn)" via a string -PASS Getting transform when it is set to "skewX(45deg)" via a string -PASS Getting transform when it is set to "skewX(1rad)" via a string -PASS Getting transform when it is set to "skewX(6.2grad)" via a string -PASS Getting transform when it is set to "skewX(0.31turn)" via a string -PASS Getting transform when it is set to "skewY(45deg)" via a string -PASS Getting transform when it is set to "skewY(1rad)" via a string -PASS Getting transform when it is set to "skewY(6.2grad)" via a string -PASS Getting transform when it is set to "skewY(0.31turn)" via a string -PASS Getting transform when it is set to "rotateX(45deg)" via a string -PASS Getting transform when it is set to "rotateX(1rad)" via a string -PASS Getting transform when it is set to "rotateX(6.2grad)" via a string -PASS Getting transform when it is set to "rotateX(0.31turn)" via a string -PASS Getting transform when it is set to "rotateY(45deg)" via a string -PASS Getting transform when it is set to "rotateY(1rad)" via a string -PASS Getting transform when it is set to "rotateY(6.2grad)" via a string -PASS Getting transform when it is set to "rotateY(0.31turn)" via a string -PASS Getting transform when it is set to "rotateZ(45deg)" via a string -PASS Getting transform when it is set to "rotateZ(1rad)" via a string -PASS Getting transform when it is set to "rotateZ(6.2grad)" via a string -PASS Getting transform when it is set to "rotateZ(0.31turn)" via a string -PASS getAll for single-valued transform -PASS Delete transform removes the value from the styleMap -PASS transform shows up in getProperties -PASS Setting transform to a sequence throws -PASS Appending to transform throws -PASS Unhandled case doesn't crash. -PASS Getting transform when it has a rotate with a calc angle does not crash -PASS Getting transform when it has a rotate3d with a calc angle does not crash -PASS Getting transform when it has a skew with a calc angle does not crash -PASS Getting transform when it has a 2-argument skew with a calc angle does not crash -PASS Getting transform when it has a skewX with a calc angle does not crash -PASS Getting transform when it has a skewY with a calc angle does not crash -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/unsupported-properties.html b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/unsupported-properties.html index f9257e19..948d002 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/unsupported-properties.html +++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/unsupported-properties.html
@@ -12,31 +12,31 @@ // supporting some of these properties. test(function() { - testElement.style.backgroundColor = 'green'; + testElement.style.backgroundColor = '#00FF00'; var result = testElement.styleMap.get('background-color'); assert_equals(result.constructor, CSSStyleValue); - assert_equals(result.cssText, 'green'); + assert_equals(result.cssText, 'rgb(0, 255, 0)'); }, 'Unsupported property returns a base StyleValue with the correct cssText.'); test(function() { - testElement.style.backgroundColor = 'green'; + testElement.style.backgroundColor = '#00FF00'; secondElement.styleMap.set('background-color', testElement.styleMap.get('background-color')); var result = secondElement.styleMap.get('background-color'); assert_equals(result.constructor, CSSStyleValue); - assert_equals(result.cssText, 'green'); + assert_equals(result.cssText, 'rgb(0, 255, 0)'); }, 'Setting the same property using the result of getting an unknown value works'); test(function() { - testElement.style.color = 'green'; + testElement.style.color = '#00FF00'; secondElement.styleMap.set('border-left-color', testElement.styleMap.get('color')); var result = secondElement.styleMap.get('border-left-color'); assert_equals(result.constructor, CSSStyleValue); - assert_equals(result.cssText, 'green'); + assert_equals(result.cssText, 'rgb(0, 255, 0)'); }, 'Setting a different property using the result of getting an unknown value works'); </script>
diff --git a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node-expected.txt b/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node-expected.txt deleted file mode 100644 index 011db21..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node-expected.txt +++ /dev/null
@@ -1,11 +0,0 @@ -This test verifies that the AudioBasicInspectorNode based nodes work correctly. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - -PASS RealtimeAnalyserNode got pulled when connected from upstream node but not to downstream node. -PASS RealtimeAnalyserNode got pulled when connected from upstream node and to destination node. -PASS RealtimeAnalyserNode didn't get pulled when it should not. -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html b/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html index a46a49c..840bad8 100644 --- a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html +++ b/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html
@@ -2,23 +2,24 @@ <html> <head> -<script src="../../resources/js-test.js"></script> -<script src="../resources/compatibility.js"></script> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script src="../resources/audit-util.js"></script> -<script src="../resources/audio-testing.js"></script> +<script src="../resources/audit.js"></script> </head> <body> -<div id="description"></div> -<div id="console"></div> - <script> -description("This test verifies that the AudioBasicInspectorNode based nodes work correctly."); +// This test verifies that the AudioBasicInspectorNode based nodes work correctly + +var audit = Audit.createTaskRunner(); var sampleRate = 44100.0; -// We carefully arrange the renderLengthInFrames to be a multiple of the AudioNode rendering quantum (AudioNode::ProcessingSizeInFrames) -// so that AudioSourceNode will not feed tailing zeroes into the context and fail this test. +// We carefully arrange the renderLengthInFrames to be a multiple of the +// AudioNode rendering quantum (AudioNode::ProcessingSizeInFrames) so that +// AudioSourceNode will not feed tailing zeroes into the context and fail this +// test. var renderLengthInFrames = 256; var fftSize = 64; @@ -31,106 +32,92 @@ var analyser; function constructCommonGraph() { - // Create offline audio context. - context = new OfflineAudioContext(1, renderLengthInFrames, sampleRate); - constantBuffer = createConstantBuffer(context, renderLengthInFrames, 1); + // Create offline audio context. + context = new OfflineAudioContext(1, renderLengthInFrames, sampleRate); + constantBuffer = createConstantBuffer(context, renderLengthInFrames, 1); - bufferSource = context.createBufferSource(); - bufferSource.buffer = constantBuffer; + bufferSource = context.createBufferSource(); + bufferSource.buffer = constantBuffer; - analyser = context.createAnalyser(); - analyser.fftSize = fftSize; + analyser = context.createAnalyser(); + analyser.fftSize = fftSize; - bufferSource.connect(analyser); + bufferSource.connect(analyser); } -function test1Finished() { - var timeDomainData = new Uint8Array(fftSize); - analyser.getByteTimeDomainData(timeDomainData); +function test1Finished(should) { + var timeDomainData = new Uint8Array(fftSize); + analyser.getByteTimeDomainData(timeDomainData); - if (timeDomainData[0] >= audioDataOne) - testPassed("RealtimeAnalyserNode got pulled when connected from upstream node but not to downstream node."); - else - testFailed("RealtimeAnalyserNode failed to get pulled when connected from upstream node but not to downstream node."); - - test2(); + should(timeDomainData[0] >= audioDataOne, + "RealtimeAnalyserNode got pulled when connected from upstream node but not to downstream node" + ) + .beTrue(); } -// To verify the realtimeAnalyser can pull data when there is an upstream node connected to it -// but it's not connected to a downstream node. -function test1() { - constructCommonGraph(); +// To verify the realtimeAnalyser can pull data when there is an upstream node +// connected to it but it's not connected to a downstream node. +audit.define("test1", function (task, should) { + constructCommonGraph(); - bufferSource.start(0); + bufferSource.start(0); - context.oncomplete = test1Finished; - context.startRendering(); + context.startRendering() + .then(test1Finished(should)) + .then(task.done.bind(task)); +}); + +function test2Finished(should) { + var timeDomainData = new Uint8Array(fftSize); + analyser.getByteTimeDomainData(timeDomainData); + + should(timeDomainData[0] >= audioDataOne, + "RealtimeAnalyserNode got pulled when connected from upstream node and to destination node" + ) + .beTrue(); } -function test2Finished() { - var timeDomainData = new Uint8Array(fftSize); - analyser.getByteTimeDomainData(timeDomainData); +// To verify the realtimeAnalyser can process normally when there is an upstream +// node connected to it and it's also connected to a downstream node which +// ultimately connect to audio destination. +audit.define("test2", function (task, should) { + constructCommonGraph(); - if (timeDomainData[0] >= audioDataOne) - testPassed("RealtimeAnalyserNode got pulled when connected from upstream node and to destination node."); - else - testFailed("RealtimeAnalyserNode failed to be pulled when connected by upstream node and to destination node."); + analyser.connect(context.destination); - test3(); + bufferSource.start(0); + + context.startRendering() + .then(test2Finished(should)) + .then(task.done.bind(task)); +}); + +function test3Finished(should) { + var timeDomainData = new Uint8Array(fftSize); + analyser.getByteTimeDomainData(timeDomainData); + + // If realtimeAnalyser hasn't pulled any data, the data in buffer will be 0. + should(timeDomainData[0] == audioDataZero, + "RealtimeAnalyserNode didn't get pulled when it should not") + .beTrue();; } -// To verify the realtimeAnalyser can process normally when there is an upstream node connected to it -// and it's also connected to a downstream node which ultimately connect to audio destination. -function test2() { - constructCommonGraph(); +// To verify the realtimeAnalyser will stop pulling if it is connected to a +// downstream node which is not ultimatly connected to any audio destination. +audit.define("test3", function (task, should) { + constructCommonGraph(); - analyser.connect(context.destination); + var gain = context.createGain(); + analyser.connect(gain); - bufferSource.start(0); + bufferSource.start(0); - context.oncomplete = test2Finished; - context.startRendering(); -} + context.startRendering() + .then(test3Finished(should)) + .then(task.done.bind(task)); +}); -function test3Finished() { - var timeDomainData = new Uint8Array(fftSize); - analyser.getByteTimeDomainData(timeDomainData); - - // If realtimeAnalyser hasn't pulled any data, the data in buffer will be 0. - if (timeDomainData[0] == audioDataZero) - testPassed("RealtimeAnalyserNode didn't get pulled when it should not."); - else - testFailed("RealtimeAnalyserNode been pulled when it should not."); - - finishJSTest(); -} - -// To verify the realtimeAnalyser will stop pulling if it is connected to a downstream node -// which is not ultimatly connected to any audio destination. -function test3() { - constructCommonGraph(); - - var gain = context.createGain(); - analyser.connect(gain); - - bufferSource.start(0); - - context.oncomplete = test3Finished; - context.startRendering(); -} - -function runTest() { - if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); - } - - window.jsTestIsAsync = true; - - test1(); -} - -runTest(); +audit.run(); </script>
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/audit.js b/third_party/WebKit/LayoutTests/webaudio/resources/audit.js index 7d337e6f..cfadbf8 100644 --- a/third_party/WebKit/LayoutTests/webaudio/resources/audit.js +++ b/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
@@ -270,12 +270,16 @@ // Catch did not happen, so the test is failed. failDetail = '${actual} did not throw an exception.'; } catch (error) { - if (this._expected === undefined) { + if (this._expected === null) { + // The expected error type was not given. didThrowCorrectly = true; - passDetail = '${actual} threw an exception of ' + error.name + '.'; + passDetail = '${actual} threw ' + error.name + ': "' + + error.message + '".'; } else if (error.name === this._expected) { + // The expected error type match the actual one. didThrowCorrectly = true; - passDetail = '${actual} threw ${expected} : "' + error.message + '".'; + passDetail = '${actual} threw ${expected}: "' + + error.message + '".'; } else { didThrowCorrectly = false; failDetail = '${actual} threw "' + error.name
diff --git a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt index f38b660..f2fcf6a 100644 --- a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt +++ b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt
@@ -10,8 +10,9 @@ PASS < [numerical] All assertions passed. (total 5 assertions) PASS > [basic] Simple unit tests for basic assertions. PASS OfflineAudioContext does exist. -PASS Setting foo to 0 did not throw an exception. -PASS function () { var foo = bar; } threw ReferenceError : "bar is not defined". +PASS Setting foo1 to 0 did not throw an exception. +PASS function () { var foo2 = bar; } threw ReferenceError: "bar is not defined". +PASS function () { var foo3 = bar; } threw ReferenceError: "bar is not defined". PASS 3 < 5 is true. PASS false is false. PASS 1 is equal to 1. @@ -23,7 +24,7 @@ PASS 1 is less than or equal to 1. PASS Decoding audio data with no argument rejected correctly with TypeError: Failed to execute 'decodeAudioData' on 'BaseAudioContext': 1 argument required, but only 0 present.. PASS Start OAC rendering resolved correctly. -PASS < [basic] All assertions passed. (total 14 assertions) +PASS < [basic] All assertions passed. (total 15 assertions) PASS # AUDIT TASK RUNNER FINISHED: 2 tasks ran successfully. Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html index 73a034f..1624a68 100644 --- a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html +++ b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html
@@ -16,8 +16,9 @@ task.describe('Simple unit tests for basic assertions.'); should(OfflineAudioContext, 'OfflineAudioContext').exist(); - should(function () { var foo = 0; }, 'Setting foo to 0').notThrow(); - should(function () { var foo = bar; }).throw('ReferenceError'); + should(function () { var foo1 = 0; }, 'Setting foo1 to 0').notThrow(); + should(function () { var foo2 = bar; }).throw(); + should(function () { var foo3 = bar; }).throw('ReferenceError'); should(3 < 5, '3 < 5').beTrue(); should(false).beFalse(); should(1).beEqualTo(1)
diff --git a/third_party/WebKit/LayoutTests/webshare/resources/mock-share-service.js b/third_party/WebKit/LayoutTests/webshare/resources/mock-share-service.js index c1f1736..0e04983 100644 --- a/third_party/WebKit/LayoutTests/webshare/resources/mock-share-service.js +++ b/third_party/WebKit/LayoutTests/webshare/resources/mock-share-service.js
@@ -2,17 +2,18 @@ let mockShareService = loadMojoModules( 'mockShareService', - ['mojo/public/js/router', + ['mojo/public/js/bindings', 'third_party/WebKit/public/platform/modules/webshare/webshare.mojom', ]).then(mojo => { - let [router, webshare] = mojo.modules; + let [bindings, webshare] = mojo.modules; - class MockShareService extends webshare.ShareService.stubClass { + class MockShareService { constructor(interfaceProvider) { - super(); + this.bindingSet_ = new bindings.BindingSet(webshare.ShareService); + interfaceProvider.addInterfaceOverrideForTesting( webshare.ShareService.name, - handle => this.connect_(handle)); + handle => this.bindingSet_.addBinding(this, handle)); } // Returns a Promise that gets rejected if the test should fail. @@ -23,11 +24,6 @@ return new Promise((resolve, reject) => {this.reject_ = reject}); } - connect_(handle) { - this.router_ = new router.Router(handle); - this.router_.setIncomingReceiver(this); - } - share(title, text, url) { let callback = null; let result = new Promise(resolve => {callback = resolve;});
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp index 2079a19..5b020d90 100644 --- a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.cpp
@@ -17,6 +17,7 @@ #include "core/dom/custom/CustomElement.h" #include "core/events/ErrorEvent.h" #include "core/html/HTMLElement.h" +#include "core/html/imports/HTMLImportsController.h" #include "v8.h" #include "wtf/Allocator.h" @@ -185,16 +186,30 @@ // Create an element with the synchronous custom elements flag set. // https://dom.spec.whatwg.org/#concept-create-element - // Create an element and push to the construction stack. - // V8HTMLElement::constructorCustom() can only refer to - // window.document(), but it is different from the document here - // when it is an import document. This is not exactly what the - // spec defines, but the non-imports behavior matches to the spec. - Element* element = createElementForConstructor(document); + // TODO(dominicc): Implement step 5 which constructs customized + // built-in elements. + + Element* element = nullptr; { - ConstructionStackScope constructionStackScope(this, element); v8::TryCatch tryCatch(m_scriptState->isolate()); - element = runConstructor(); + + bool isImportDocument = document.importsController() && + document.importsController()->master() != document; + if (isImportDocument) { + // V8HTMLElement::constructorCustom() can only refer to + // window.document() which is not the import document. Create + // elements in import documents ahead of time so they end up in + // the right document. This subtly violates recursive + // construction semantics, but only in import documents. + element = createElementForConstructor(document); + DCHECK(!tryCatch.HasCaught()); + + ConstructionStackScope constructionStackScope(this, element); + element = callConstructor(); + } else { + element = callConstructor(); + } + if (tryCatch.HasCaught()) { exceptionState.rethrowV8Exception(tryCatch.Exception()); return handleCreateElementSyncException(document, tagName, isolate, @@ -224,9 +239,9 @@ v8::TryCatch tryCatch(isolate); tryCatch.SetVerbose(true); - Element* result = runConstructor(); + Element* result = callConstructor(); - // To report exception thrown from runConstructor() + // To report exception thrown from callConstructor() if (tryCatch.HasCaught()) return false; @@ -245,7 +260,7 @@ return true; } -Element* ScriptCustomElementDefinition::runConstructor() { +Element* ScriptCustomElementDefinition::callConstructor() { v8::Isolate* isolate = m_scriptState->isolate(); DCHECK(ScriptState::current(isolate) == m_scriptState); ExecutionContext* executionContext = m_scriptState->getExecutionContext();
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.h b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.h index 27eb3dd..eac09b8 100644 --- a/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.h +++ b/third_party/WebKit/Source/bindings/core/v8/ScriptCustomElementDefinition.h
@@ -73,7 +73,9 @@ // Implementations of |CustomElementDefinition| ScriptValue getConstructorForScript() final; bool runConstructor(Element*) override; - Element* runConstructor(); + + // Calls the constructor. The script scope, etc. must already be set up. + Element* callConstructor(); void runCallback(v8::Local<v8::Function>, Element*,
diff --git a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp index eb899ff..0a3d410 100644 --- a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp +++ b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
@@ -184,9 +184,10 @@ InterpolationValue CSSColorInterpolationType::maybeConvertInitial( const StyleResolverState&, ConversionCheckers& conversionCheckers) const { - const StyleColor initialColor = - ColorPropertyFunctions::getInitialColor(cssProperty()); - return convertStyleColorPair(initialColor, initialColor); + StyleColor initialColor; + if (ColorPropertyFunctions::getInitialColor(cssProperty(), initialColor)) + return convertStyleColorPair(initialColor, initialColor); + return nullptr; } InterpolationValue CSSColorInterpolationType::maybeConvertInherit(
diff --git a/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp b/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp index 7622bb5..2c8357f 100644 --- a/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp +++ b/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp
@@ -142,6 +142,7 @@ case CSSPropertyBorderLeftColor: case CSSPropertyBorderRightColor: case CSSPropertyBorderTopColor: + case CSSPropertyCaretColor: case CSSPropertyColor: case CSSPropertyFloodColor: case CSSPropertyLightingColor:
diff --git a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp index 5755a3c..c6dba58 100644 --- a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp +++ b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp
@@ -8,8 +8,14 @@ namespace blink { -StyleColor ColorPropertyFunctions::getInitialColor(CSSPropertyID property) { - return getUnvisitedColor(property, ComputedStyle::initialStyle()); +bool ColorPropertyFunctions::getInitialColor(CSSPropertyID property, + StyleColor& initialColor) { + if (property == CSSPropertyCaretColor) + return false; + // TODO(rego): Make getUnvisitedColor() return a bool, so we don't need a + // special case for caret-color here (http://crbug.com/676295). + initialColor = getUnvisitedColor(property, ComputedStyle::initialStyle()); + return true; } StyleColor ColorPropertyFunctions::getUnvisitedColor( @@ -26,6 +32,12 @@ return style.borderTopColor(); case CSSPropertyBorderBottomColor: return style.borderBottomColor(); + case CSSPropertyCaretColor: + // TODO(rego): "auto" value for caret-color should not interpolate + // (http://crbug.com/676295). + if (style.caretColor().isAutoColor()) + return StyleColor::currentColor(); + return style.caretColor().toStyleColor(); case CSSPropertyColor: return style.color(); case CSSPropertyOutlineColor: @@ -67,6 +79,12 @@ return style.visitedLinkBorderTopColor(); case CSSPropertyBorderBottomColor: return style.visitedLinkBorderBottomColor(); + case CSSPropertyCaretColor: + // TODO(rego): "auto" value for caret-color should not interpolate + // (http://crbug.com/676295). + if (style.visitedLinkCaretColor().isAutoColor()) + return StyleColor::currentColor(); + return style.visitedLinkCaretColor().toStyleColor(); case CSSPropertyColor: return style.visitedLinkColor(); case CSSPropertyOutlineColor: @@ -114,6 +132,8 @@ case CSSPropertyBorderTopColor: style.setBorderTopColor(color); return; + case CSSPropertyCaretColor: + return style.setCaretColor(color); case CSSPropertyColor: style.setColor(color); return; @@ -163,6 +183,8 @@ case CSSPropertyBorderTopColor: style.setVisitedLinkBorderTopColor(color); return; + case CSSPropertyCaretColor: + return style.setVisitedLinkCaretColor(color); case CSSPropertyColor: style.setVisitedLinkColor(color); return;
diff --git a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.h b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.h index 7fe546a..47fe8c7 100644 --- a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.h +++ b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.h
@@ -14,7 +14,7 @@ class ColorPropertyFunctions { public: - static StyleColor getInitialColor(CSSPropertyID); + static bool getInitialColor(CSSPropertyID, StyleColor& initialColor); static StyleColor getUnvisitedColor(CSSPropertyID, const ComputedStyle&); static StyleColor getVisitedColor(CSSPropertyID, const ComputedStyle&); static void setUnvisitedColor(CSSPropertyID, ComputedStyle&, const Color&);
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp index 90d0100..868d4fa 100644 --- a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp +++ b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
@@ -417,6 +417,12 @@ CSSIdentifierValue::create(CSSValueAuto)); } return createFromLengthBox(style.clip(), style); + case CSSPropertyCaretColor: + if (style.caretColor().isAutoColor()) { + return AnimatableUnknown::create( + CSSIdentifierValue::create(CSSValueAuto)); + } + return createFromColor(property, style); case CSSPropertyColor: return createFromColor(property, style); case CSSPropertyFillOpacity:
diff --git a/third_party/WebKit/Source/core/css/BinaryDataFontFaceSource.cpp b/third_party/WebKit/Source/core/css/BinaryDataFontFaceSource.cpp index 0970c2c..7249eff 100644 --- a/third_party/WebKit/Source/core/css/BinaryDataFontFaceSource.cpp +++ b/third_party/WebKit/Source/core/css/BinaryDataFontFaceSource.cpp
@@ -28,7 +28,8 @@ m_customPlatformData->fontPlatformData( fontDescription.effectiveFontSize(), fontDescription.isSyntheticBold(), - fontDescription.isSyntheticItalic(), fontDescription.orientation()), + fontDescription.isSyntheticItalic(), fontDescription.orientation(), + fontDescription.variationSettings()), CustomFontData::create()); }
diff --git a/third_party/WebKit/Source/core/css/CSSFontFaceSourceTest.cpp b/third_party/WebKit/Source/core/css/CSSFontFaceSourceTest.cpp index e9d3bc9..7094669d 100644 --- a/third_party/WebKit/Source/core/css/CSSFontFaceSourceTest.cpp +++ b/third_party/WebKit/Source/core/css/CSSFontFaceSourceTest.cpp
@@ -44,9 +44,9 @@ DummyFontFaceSource fontFaceSource; // Even if the hash value collide, fontface cache should return different // value for different fonts. - EXPECT_EQ(simulateHashCalculation(2821), simulateHashCalculation(3346)); - EXPECT_NE(fontFaceSource.getFontDataForSize(2821), - fontFaceSource.getFontDataForSize(3346)); + EXPECT_EQ(simulateHashCalculation(8775), simulateHashCalculation(418)); + EXPECT_NE(fontFaceSource.getFontDataForSize(8775), + fontFaceSource.getFontDataForSize(418)); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.in b/third_party/WebKit/Source/core/css/CSSProperties.in index 36a63603..5a30f1d1 100644 --- a/third_party/WebKit/Source/core/css/CSSProperties.in +++ b/third_party/WebKit/Source/core/css/CSSProperties.in
@@ -221,7 +221,7 @@ break-inside type_name=EBreak buffered-rendering svg caption-side inherited, keyword_only, independent, keywords=[top|bottom|left|right], initial_keyword=top -caret-color inherited, custom_all +caret-color interpolable, inherited, custom_all clear clip interpolable, converter=convertClip, custom_all clip-path interpolable, converter=convertClipPath
diff --git a/third_party/WebKit/Source/core/css/RemoteFontFaceSource.cpp b/third_party/WebKit/Source/core/css/RemoteFontFaceSource.cpp index 122663e..3d2e777 100644 --- a/third_party/WebKit/Source/core/css/RemoteFontFaceSource.cpp +++ b/third_party/WebKit/Source/core/css/RemoteFontFaceSource.cpp
@@ -218,7 +218,8 @@ m_font->platformDataFromCustomData(fontDescription.effectiveFontSize(), fontDescription.isSyntheticBold(), fontDescription.isSyntheticItalic(), - fontDescription.orientation()), + fontDescription.orientation(), + fontDescription.variationSettings()), CustomFontData::create()); }
diff --git a/third_party/WebKit/Source/core/css/StyleAutoColor.h b/third_party/WebKit/Source/core/css/StyleAutoColor.h index c3bb6ea76..c35799d 100644 --- a/third_party/WebKit/Source/core/css/StyleAutoColor.h +++ b/third_party/WebKit/Source/core/css/StyleAutoColor.h
@@ -34,8 +34,10 @@ } StyleColor toStyleColor() const { + DCHECK(m_type != ValueType::Auto); if (m_type == ValueType::SpecifiedColor) return StyleColor(m_color); + DCHECK(m_type == ValueType::CurrentColor); return StyleColor::currentColor(); }
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp index eb43610..8530c01 100644 --- a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp +++ b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp
@@ -21,6 +21,30 @@ return new CSSKeywordValue(keyword); } +CSSKeywordValue* CSSKeywordValue::fromCSSValue(const CSSValue& value) { + if (value.isInheritedValue()) + return new CSSKeywordValue(getValueName(CSSValueInherit)); + if (value.isInitialValue()) + return new CSSKeywordValue(getValueName(CSSValueInitial)); + if (value.isUnsetValue()) + return new CSSKeywordValue(getValueName(CSSValueUnset)); + if (value.isIdentifierValue()) { + return new CSSKeywordValue( + getValueName(toCSSIdentifierValue(value).getValueID())); + } + if (value.isCustomIdentValue()) { + const CSSCustomIdentValue& identValue = toCSSCustomIdentValue(value); + if (identValue.isKnownPropertyID()) { + // CSSPropertyID represents the LHS of a CSS declaration, and + // CSSKeywordValue represents a RHS. + return nullptr; + } + return new CSSKeywordValue(identValue.value()); + } + NOTREACHED(); + return nullptr; +} + const AtomicString& CSSKeywordValue::keywordValue() const { return m_keywordValue; }
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.h b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.h index edf620f8..d08d969 100644 --- a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.h +++ b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.h
@@ -20,6 +20,7 @@ public: static CSSKeywordValue* create(const AtomicString& keyword, ExceptionState&); + static CSSKeywordValue* fromCSSValue(const CSSValue&); StyleValueType type() const override { return KeywordType; }
diff --git a/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp b/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp index 70a5f9b..06565694 100644 --- a/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp +++ b/third_party/WebKit/Source/core/css/cssom/StyleValueFactory.cpp
@@ -6,6 +6,7 @@ #include "core/css/CSSImageValue.h" #include "core/css/CSSValue.h" +#include "core/css/cssom/CSSKeywordValue.h" #include "core/css/cssom/CSSNumberValue.h" #include "core/css/cssom/CSSSimpleLength.h" #include "core/css/cssom/CSSStyleValue.h" @@ -19,37 +20,51 @@ namespace { -CSSStyleValue* styleValueForPrimitiveValue( +CSSStyleValue* createStyleValueFromPrimitiveValue( const CSSPrimitiveValue& primitiveValue) { if (primitiveValue.isNumber()) return CSSNumberValue::create(primitiveValue.getDoubleValue()); if (primitiveValue.isLength() || primitiveValue.isPercentage()) return CSSSimpleLength::fromCSSValue(primitiveValue); - return nullptr; } -CSSStyleValue* styleValueForProperty(CSSPropertyID propertyID, - const CSSValue& value) { +CSSStyleValue* createStyleValueWithPropertyInternal(CSSPropertyID propertyID, + const CSSValue& value) { switch (propertyID) { case CSSPropertyTransform: return CSSTransformValue::fromCSSValue(value); default: - // TODO(meade): Implement other complex properties. + // TODO(meade): Implement other properties. break; } + return nullptr; +} +CSSStyleValue* createStyleValue(const CSSValue& value) { + if (value.isCSSWideKeyword() || value.isIdentifierValue() || + value.isCustomIdentValue()) + return CSSKeywordValue::fromCSSValue(value); if (value.isPrimitiveValue()) - return styleValueForPrimitiveValue(toCSSPrimitiveValue(value)); + return createStyleValueFromPrimitiveValue(toCSSPrimitiveValue(value)); if (value.isVariableReferenceValue()) return CSSUnparsedValue::fromCSSValue(toCSSVariableReferenceValue(value)); - if (value.isImageValue()) + if (value.isImageValue()) { return CSSURLImageValue::create( toCSSImageValue(value).valueWithURLMadeAbsolute()); - + } return nullptr; } +CSSStyleValue* createStyleValueWithProperty(CSSPropertyID propertyID, + const CSSValue& value) { + CSSStyleValue* styleValue = + createStyleValueWithPropertyInternal(propertyID, value); + if (styleValue) + return styleValue; + return createStyleValue(value); +} + CSSStyleValueVector unsupportedCSSValue(const CSSValue& value) { CSSStyleValueVector styleValueVector; styleValueVector.push_back(CSSUnsupportedStyleValue::create(value.cssText())); @@ -60,25 +75,26 @@ CSSStyleValueVector StyleValueFactory::cssValueToStyleValueVector( CSSPropertyID propertyID, - const CSSValue& value) { + const CSSValue& cssValue) { CSSStyleValueVector styleValueVector; - CSSStyleValue* styleValue = styleValueForProperty(propertyID, value); + + CSSStyleValue* styleValue = + createStyleValueWithProperty(propertyID, cssValue); if (styleValue) { styleValueVector.push_back(styleValue); return styleValueVector; } - if (!value.isValueList()) { - return unsupportedCSSValue(value); + if (!cssValue.isValueList()) { + return unsupportedCSSValue(cssValue); } // If it's a list, we can try it as a list valued property. - const CSSValueList& cssValueList = toCSSValueList(value); + const CSSValueList& cssValueList = toCSSValueList(cssValue); for (const CSSValue* innerValue : cssValueList) { - styleValue = styleValueForProperty(propertyID, *innerValue); - if (!styleValue) { - return unsupportedCSSValue(value); - } + styleValue = createStyleValueWithProperty(propertyID, *innerValue); + if (!styleValue) + return unsupportedCSSValue(cssValue); styleValueVector.push_back(styleValue); } return styleValueVector;
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp index b605508..19b0fb2 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp
@@ -375,7 +375,9 @@ double alpha = 0; if (!parseDouble(string, end, terminator, alpha)) return false; - value = negative ? 0 : static_cast<int>(alpha * nextafter(256.0, 0.0)); + value = negative + ? 0 + : static_cast<int>(std::min(alpha, 1.0) * nextafter(256.0, 0.0)); string = end; return true; }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserFastPathsTest.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserFastPathsTest.cpp index a464c2d..250a261 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSParserFastPathsTest.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSParserFastPathsTest.cpp
@@ -4,6 +4,7 @@ #include "core/css/parser/CSSParserFastPaths.h" +#include "core/css/CSSColorValue.h" #include "core/css/CSSIdentifierValue.h" #include "core/css/CSSValueList.h" #include "testing/gtest/include/gtest/gtest.h" @@ -98,4 +99,12 @@ ASSERT_EQ(nullptr, value); } +TEST(CSSParserFastPathsTest, ParseColorWithLargeAlpha) { + CSSValue* value = CSSParserFastPaths::parseColor("rgba(0,0,0,1893205797.13)", + HTMLStandardMode); + EXPECT_NE(nullptr, value); + EXPECT_TRUE(value->isColorValue()); + EXPECT_EQ(Color::black, toCSSColorValue(*value).value()); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.cpp b/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.cpp index 375a0cc..3709e4a 100644 --- a/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.cpp +++ b/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.cpp
@@ -97,9 +97,9 @@ return effectiveSizeDefaultValue(); } -unsigned SizesAttributeParser::effectiveSizeDefaultValue() { +float SizesAttributeParser::effectiveSizeDefaultValue() { // Returning the equivalent of "100vw" - return m_mediaValues->viewportWidth(); + return clampTo<float>(m_mediaValues->viewportWidth()); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.h b/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.h index e1fd557..ed96f556 100644 --- a/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.h +++ b/third_party/WebKit/Source/core/css/parser/SizesAttributeParser.h
@@ -27,7 +27,7 @@ float effectiveSize(); bool calculateLengthInPixels(CSSParserTokenRange, float& result); bool mediaConditionMatches(MediaQuerySet* mediaCondition); - unsigned effectiveSizeDefaultValue(); + float effectiveSizeDefaultValue(); Member<MediaQuerySet> m_mediaCondition; Member<MediaValues> m_mediaValues;
diff --git a/third_party/WebKit/Source/core/css/parser/SizesAttributeParserTest.cpp b/third_party/WebKit/Source/core/css/parser/SizesAttributeParserTest.cpp index 3011b67..972e5fd8 100644 --- a/third_party/WebKit/Source/core/css/parser/SizesAttributeParserTest.cpp +++ b/third_party/WebKit/Source/core/css/parser/SizesAttributeParserTest.cpp
@@ -96,4 +96,85 @@ } } +TEST(SizesAttributeParserTest, FloatViewportWidth) { + TestCase testCases[] = { + {"screen", 500.5}, + {"(min-width:500px)", 500.5}, + {"(min-width:500px) 200px", 200}, + {"(min-width:500px) 50vw", 250.25}, + {"(min-width:500px) 200px, 400px", 200}, + {"400px, (min-width:500px) 200px", 400}, + {"40vw, (min-width:500px) 201px", 200.2}, + {"(min-width:500px) 201px, 40vw", 201}, + {"(min-width:5000px) 40vw, 201px", 201}, + {"(min-width:500px) calc(201px), calc(40vw)", 201}, + {"(min-width:5000px) calc(40vw), calc(201px)", 201}, + {"(min-width:5000px) 200px, 400px", 400}, + {"(blalbadfsdf) 200px, 400px", 400}, + {"0", 0}, + {"-0", 0}, + {"1", 500.5}, + {"300px, 400px", 300}, + {"(min-width:5000px) 200px, (min-width:500px) 400px", 400}, + {"", 500.5}, + {" ", 500.5}, + {" /**/ ", 500.5}, + {" /**/ 300px", 300}, + {"300px /**/ ", 300}, + {" /**/ (min-width:500px) /**/ 300px", 300}, + {"-100px, 200px", 200}, + {"-50vw, 20vw", 100.1}, + {"50asdf, 200px", 200}, + {"asdf, 200px", 200}, + {"(max-width: 3000px) 200w, 400w", 500.5}, + {",, , /**/ ,200px", 200}, + {"50vw", 250.25}, + {"50vh", 300}, + {"50vmin", 250.25}, + {"50vmax", 300}, + {"5em", 80}, + {"5rem", 80}, + {"calc(40vw*2)", 400.4}, + {"(min-width:5000px) calc(5000px/10), (min-width:500px) calc(1200px/3)", + 400}, + {"(min-width:500px) calc(1200/3)", 500.5}, + {"(min-width:500px) calc(1200px/(0px*14))", 500.5}, + {"(max-width: 3000px) 200px, 400px", 200}, + {"(max-width: 3000px) 20em, 40em", 320}, + {"(max-width: 3000px) 0, 40em", 0}, + {"(max-width: 3000px) 0px, 40em", 0}, + {"(max-width: 3000px) 50vw, 40em", 250.25}, + {"(max-width: 3000px) 50px, 40vw", 50}, + {"((),1px", 500.5}, + {"{{},1px", 500.5}, + {"[[],1px", 500.5}, + {"x(x(),1px", 500.5}, + {"(max-width: 3000px) 50.5px, 40vw", 50.5}, + {"not (blabla) 50px, 40vw", 200.2}, + {"not (max-width: 100px) 50px, 40vw", 50}, + {0, 0} // Do not remove the terminator line. + }; + + MediaValuesCached::MediaValuesCachedData data; + data.viewportWidth = 500.5; + data.viewportHeight = 600; + data.deviceWidth = 500; + data.deviceHeight = 500; + data.devicePixelRatio = 2.0; + data.colorBitsPerComponent = 24; + data.monochromeBitsPerComponent = 0; + data.primaryPointerType = PointerTypeFine; + data.defaultFontSize = 16; + data.threeDEnabled = true; + data.mediaType = MediaTypeNames::screen; + data.strictMode = true; + data.displayMode = WebDisplayModeBrowser; + MediaValues* mediaValues = MediaValuesCached::create(data); + + for (unsigned i = 0; testCases[i].input; ++i) { + SizesAttributeParser parser(mediaValues, testCases[i].input); + ASSERT_EQ(testCases[i].effectiveSize, parser.length()); + } +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp index d64c35f..710af22 100644 --- a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp +++ b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
@@ -421,6 +421,11 @@ case CSSPropertyBoxShadow: style->setBoxShadow(toAnimatableShadow(value)->getShadowList()); return; + case CSSPropertyCaretColor: + style->setCaretColor(toAnimatableColor(value)->getColor()); + style->setVisitedLinkCaretColor( + toAnimatableColor(value)->visitedLinkColor()); + return; case CSSPropertyClip: style->setClip(animatableValueToLengthBox(value, state)); return;
diff --git a/third_party/WebKit/Source/core/editing/BUILD.gn b/third_party/WebKit/Source/core/editing/BUILD.gn index 85a48ab..157594e 100644 --- a/third_party/WebKit/Source/core/editing/BUILD.gn +++ b/third_party/WebKit/Source/core/editing/BUILD.gn
@@ -234,6 +234,7 @@ "VisibleSelectionTest.cpp", "VisibleUnitsTest.cpp", "commands/ApplyBlockElementCommandTest.cpp", + "commands/ApplyStyleCommandTest.cpp", "commands/DeleteSelectionCommandTest.cpp", "commands/InsertListCommandTest.cpp", "commands/ReplaceSelectionCommandTest.cpp",
diff --git a/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.h b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.h index a2ba527..036b8b6 100644 --- a/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.h +++ b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommand.h
@@ -38,7 +38,7 @@ enum ShouldIncludeTypingStyle { IncludeTypingStyle, IgnoreTypingStyle }; -class ApplyStyleCommand final : public CompositeEditCommand { +class CORE_EXPORT ApplyStyleCommand final : public CompositeEditCommand { public: enum EPropertyLevel { PropertyDefault, ForceBlockProperties }; enum InlineStyleRemovalMode { RemoveIfNeeded, RemoveAlways, RemoveNone };
diff --git a/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommandTest.cpp b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommandTest.cpp new file mode 100644 index 0000000..5357b8e3 --- /dev/null +++ b/third_party/WebKit/Source/core/editing/commands/ApplyStyleCommandTest.cpp
@@ -0,0 +1,51 @@ +// Copyright 2016 The Chromium 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/editing/commands/ApplyStyleCommand.h" + +#include "core/css/StylePropertySet.h" +#include "core/dom/Document.h" +#include "core/editing/EditingTestBase.h" +#include "core/editing/FrameSelection.h" +#include "core/frame/LocalFrame.h" + +namespace blink { + +class ApplyStyleCommandTest : public EditingTestBase {}; + +// This is a regression test for https://crbug.com/675727 +TEST_F(ApplyStyleCommandTest, RemoveRedundantBlocksWithStarEditableStyle) { + // The second <div> below is redundant from Blink's perspective (no siblings + // && no attributes) and will be removed by + // |DeleteSelectionCommand::removeRedundantBlocks()|. + setBodyContent( + "<div><div>" + "<div></div>" + "<ul>" + "<li>" + "<div></div>" + "<input>" + "<style> * {-webkit-user-modify: read-write;}</style><div></div>" + "</li>" + "</ul></div></div>"); + + Element* li = document().querySelector("li"); + + LocalFrame* frame = document().frame(); + frame->selection().setSelection( + SelectionInDOMTree::Builder() + .collapse(Position(li, PositionAnchorType::BeforeAnchor)) + .build()); + + MutableStylePropertySet* style = + MutableStylePropertySet::create(HTMLQuirksMode); + style->setProperty(CSSPropertyTextAlign, "center"); + ApplyStyleCommand::create(document(), EditingStyle::create(style), + InputEvent::InputType::FormatJustifyCenter, + ApplyStyleCommand::ForceBlockProperties) + ->apply(EditCommandSource::kDOM); + // Shouldn't crash. +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp b/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp index 2f1bc1a..028d0fb 100644 --- a/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp +++ b/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp
@@ -1022,6 +1022,7 @@ Element* rootElement = rootEditableElement(*node); while (node != rootElement) { + ABORT_EDITING_COMMAND_IF(!node); if (isRemovableBlock(node)) { if (node == m_endingPosition.anchorNode()) updatePositionForNodeRemovalPreservingChildren(m_endingPosition, *node);
diff --git a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp index c7c0ecd..85fbcce8 100644 --- a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp +++ b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
@@ -201,6 +201,11 @@ if (!lastTypingCommand->willAddTypingToOpenCommand( source, InputEvent::InputType::DeleteContentBackward)) return; + + // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets + // needs to be audited. See http://crbug.com/590369 for more details. + document.updateStyleAndLayoutIgnorePendingStylesheets(); + // InputMethodController uses this function to delete composition // selection. It won't be aborted. lastTypingCommand->deleteSelection(options & SmartDelete, @@ -230,6 +235,12 @@ if (!lastTypingCommand->willAddTypingToOpenCommand( source, InputEvent::InputType::DeleteContentBackward)) return; + + // TODO(editing-dev): The use of + // updateStyleAndLayoutIgnorePendingStylesheets needs to be audited. + // See http://crbug.com/590369 for more details. + document.updateStyleAndLayoutIgnorePendingStylesheets(); + EditingState editingState; lastTypingCommand->deleteKeyPressed(granularity, options & KillRing, &editingState); @@ -259,6 +270,11 @@ if (!lastTypingCommand->willAddTypingToOpenCommand( source, InputEvent::InputType::DeleteContentForward)) return; + + // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets + // needs to be audited. See http://crbug.com/590369 for more details. + document.updateStyleAndLayoutIgnorePendingStylesheets(); + lastTypingCommand->forwardDeleteKeyPressed( granularity, options & KillRing, editingState); return; @@ -379,6 +395,11 @@ if (!lastTypingCommand->willAddTypingToOpenCommand( source, InputEvent::InputType::InsertText, newText)) return; + + // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets + // needs to be audited. See http://crbug.com/590369 for more details. + document.updateStyleAndLayoutIgnorePendingStylesheets(); + EditingState editingState; lastTypingCommand->insertText(newText, options & SelectInsertedText, &editingState); @@ -433,6 +454,11 @@ if (!lastTypingCommand->willAddTypingToOpenCommand( source, InputEvent::InputType::InsertLineBreak)) return false; + + // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets + // needs to be audited. See http://crbug.com/590369 for more details. + document.updateStyleAndLayoutIgnorePendingStylesheets(); + EditingState editingState; lastTypingCommand->insertLineBreak(&editingState); return !editingState.isAborted(); @@ -449,6 +475,11 @@ if (!lastTypingCommand->willAddTypingToOpenCommand( source, InputEvent::InputType::InsertParagraph)) return false; + + // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets + // needs to be audited. See http://crbug.com/590369 for more details. + document.updateStyleAndLayoutIgnorePendingStylesheets(); + EditingState editingState; lastTypingCommand->insertParagraphSeparatorInQuotedContent(&editingState); return !editingState.isAborted(); @@ -467,6 +498,11 @@ if (!lastTypingCommand->willAddTypingToOpenCommand( source, InputEvent::InputType::InsertParagraph)) return false; + + // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets + // needs to be audited. See http://crbug.com/590369 for more details. + document.updateStyleAndLayoutIgnorePendingStylesheets(); + EditingState editingState; lastTypingCommand->insertParagraphSeparator(&editingState); return !editingState.isAborted();
diff --git a/third_party/WebKit/Source/core/frame/DOMWindow.cpp b/third_party/WebKit/Source/core/frame/DOMWindow.cpp index b6f4c38..84c469ea 100644 --- a/third_party/WebKit/Source/core/frame/DOMWindow.cpp +++ b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
@@ -396,7 +396,7 @@ InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(context, "close", true); - page->chromeClient().closeWindowSoon(); + page->closeSoon(); // So as to make window.closed return the expected result // after window.close(), separately record the to-be-closed
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp index 9c5cfa2..c47c1e0 100644 --- a/third_party/WebKit/Source/core/frame/FrameView.cpp +++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -1323,6 +1323,11 @@ owner->setNeedsPaintPropertyUpdate(); } +void FrameView::setSubtreeNeedsPaintPropertyUpdate() { + setNeedsPaintPropertyUpdate(); + layoutView()->setSubtreeNeedsPaintPropertyUpdate(); +} + IntRect FrameView::computeVisibleArea() { // Return our clipping bounds in the root frame. IntRect us(frameRect());
diff --git a/third_party/WebKit/Source/core/frame/FrameView.h b/third_party/WebKit/Source/core/frame/FrameView.h index 4e0f8ed2..6f8b92d 100644 --- a/third_party/WebKit/Source/core/frame/FrameView.h +++ b/third_party/WebKit/Source/core/frame/FrameView.h
@@ -756,17 +756,9 @@ } bool needsPaintPropertyUpdate() const { return m_needsPaintPropertyUpdate; } - // Set when the whole frame subtree needs full paint invalidation and paint - // property update, e.g. when beginning or finishing printing. - void setShouldInvalidateAllPaintAndPaintProperties() { - m_shouldInvalidateAllPaintAndPaintProperties = true; - } - void clearShouldInvalidateAllPaintAndPaintProperties() { - m_shouldInvalidateAllPaintAndPaintProperties = true; - } - bool shouldInvalidateAllPaintAndPaintProperties() const { - return m_shouldInvalidateAllPaintAndPaintProperties; - } + // Set when the whole frame subtree needs full paint property update, + // e.g. when beginning or finishing printing. + void setSubtreeNeedsPaintPropertyUpdate(); // TODO(ojan): Merge this with IntersectionObserver once it lands. IntRect computeVisibleArea(); @@ -1147,7 +1139,6 @@ // Whether the paint properties need to be updated. For more details, see // FrameView::needsPaintPropertyUpdate(). bool m_needsPaintPropertyUpdate; - bool m_shouldInvalidateAllPaintAndPaintProperties; // This is set on the local root frame view only. DocumentLifecycle::LifecycleState m_currentUpdateLifecyclePhasesTargetState;
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp index d688177a..36b7171 100644 --- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp +++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -593,7 +593,7 @@ } if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) - view()->setShouldInvalidateAllPaintAndPaintProperties(); + view()->setSubtreeNeedsPaintPropertyUpdate(); if (!printing) document()->setPrinting(Document::NotPrinting);
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h index 590b0749..c5d7a1f 100644 --- a/third_party/WebKit/Source/core/frame/UseCounter.h +++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1400,6 +1400,7 @@ V8PeriodicWave_Constructor = 1721, V8StereoPannerNode_Constructor = 1722, V8WaveShaperNode_Constructor = 1723, + V8Headers_GetAll_Method = 1724, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp index 058f5aa..578582d2 100644 --- a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp +++ b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.cpp
@@ -433,6 +433,7 @@ // / "*" // host-char = ALPHA / DIGIT / "-" // +// static bool SourceListDirective::parseHost( const UChar* begin, const UChar* end, @@ -447,29 +448,34 @@ const UChar* position = begin; + // Parse "*" or [ "*." ]. if (skipExactly<UChar>(position, end, '*')) { hostWildcard = CSPSource::HasWildcard; - if (position == end) + if (position == end) { + // "*" return true; + } if (!skipExactly<UChar>(position, end, '.')) return false; } - const UChar* hostBegin = position; + // Parse 1*host-hcar. + if (!skipExactly<UChar, isHostCharacter>(position, end)) + return false; + skipWhile<UChar, isHostCharacter>(position, end); + + // Parse *( "." 1*host-char ). while (position < end) { + if (!skipExactly<UChar>(position, end, '.')) + return false; if (!skipExactly<UChar, isHostCharacter>(position, end)) return false; - skipWhile<UChar, isHostCharacter>(position, end); - - if (position < end && !skipExactly<UChar>(position, end, '.')) - return false; } - DCHECK(position == end); host = String(hostBegin, end - hostBegin); return true; }
diff --git a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h index c9a78360..425680c 100644 --- a/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h +++ b/third_party/WebKit/Source/core/frame/csp/SourceListDirective.h
@@ -59,6 +59,7 @@ FRIEND_TEST_ALL_PREFIXES(SourceListDirectiveTest, GetIntersectNonces); FRIEND_TEST_ALL_PREFIXES(SourceListDirectiveTest, GetIntersectHashes); FRIEND_TEST_ALL_PREFIXES(SourceListDirectiveTest, GetSources); + FRIEND_TEST_ALL_PREFIXES(SourceListDirectiveTest, ParseHost); FRIEND_TEST_ALL_PREFIXES(CSPDirectiveListTest, GetSourceVector); FRIEND_TEST_ALL_PREFIXES(CSPDirectiveListTest, OperativeDirectiveGivenType); @@ -71,10 +72,10 @@ CSPSource::WildcardDisposition&, CSPSource::WildcardDisposition&); bool parseScheme(const UChar* begin, const UChar* end, String& scheme); - bool parseHost(const UChar* begin, - const UChar* end, - String& host, - CSPSource::WildcardDisposition&); + static bool parseHost(const UChar* begin, + const UChar* end, + String& host, + CSPSource::WildcardDisposition&); bool parsePort(const UChar* begin, const UChar* end, int& port,
diff --git a/third_party/WebKit/Source/core/frame/csp/SourceListDirectiveTest.cpp b/third_party/WebKit/Source/core/frame/csp/SourceListDirectiveTest.cpp index 73697a5..c0cca7e 100644 --- a/third_party/WebKit/Source/core/frame/csp/SourceListDirectiveTest.cpp +++ b/third_party/WebKit/Source/core/frame/csp/SourceListDirectiveTest.cpp
@@ -1336,4 +1336,40 @@ } } +TEST_F(SourceListDirectiveTest, ParseHost) { + struct TestCase { + String sources; + bool expected; + } cases[] = { + // Wildcard. + {"*", true}, + {"*.", false}, + {"*.a", true}, + {"a.*.a", false}, + {"a.*", false}, + + // Dots. + {"a.b.c", true}, + {"a.b.", false}, + {".b.c", false}, + {"a..c", false}, + + // Valid/Invalid characters. + {"az09-", true}, + {"+", false}, + }; + + for (const auto& test : cases) { + String host; + CSPSource::WildcardDisposition disposition = CSPSource::NoWildcard; + Vector<UChar> characters; + test.sources.appendTo(characters); + const UChar* start = characters.data(); + const UChar* end = start + characters.size(); + EXPECT_EQ(test.expected, + SourceListDirective::parseHost(start, end, host, disposition)) + << "SourceListDirective::parseHost fail to parse: " << test.sources; + } +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp index b23ea4ea..2bf68aa 100644 --- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -115,7 +115,7 @@ DCHECK(ImageBuffer::canCreateImageBuffer(size)); sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(size.width(), size.height()); - return surface->makeImageSnapshot(); + return surface ? surface->makeImageSnapshot() : nullptr; } PassRefPtr<Image> createTransparentImage(const IntSize& size) {
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp b/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp index bb6717f..aaa9118 100644 --- a/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp +++ b/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp
@@ -103,6 +103,9 @@ if (isHTMLTemplateElement(*task.parent)) task.parent = toHTMLTemplateElement(task.parent.get())->content(); + // https://html.spec.whatwg.org/#insert-a-foreign-element + // 3.1, (3) Push (pop) an element queue + CEReactionsScope reactions; if (task.nextChild) task.parent->parserInsertBefore(task.child.get(), *task.nextChild); else
diff --git a/third_party/WebKit/Source/core/layout/FragmentainerIterator.cpp b/third_party/WebKit/Source/core/layout/FragmentainerIterator.cpp index 61a3c8e..50f2d001 100644 --- a/third_party/WebKit/Source/core/layout/FragmentainerIterator.cpp +++ b/third_party/WebKit/Source/core/layout/FragmentainerIterator.cpp
@@ -32,9 +32,7 @@ // Jump to the first interesting column set. m_currentColumnSet = flowThread.columnSetAtBlockOffset( m_logicalTopInFlowThread, LayoutBox::AssociateWithLatterPage); - if (!m_currentColumnSet || - m_currentColumnSet->logicalTopInFlowThread() >= - m_logicalBottomInFlowThread) { + if (!m_currentColumnSet) { setAtEnd(); return; }
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp index 66b2663d..1c8e33b 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -1693,22 +1693,37 @@ screenArea); } -void LayoutBox::frameRectChanged() { +void LayoutBox::locationChanged() { + // The location may change because of layout of other objects. Should check + // this object for paint invalidation. + if (!needsLayout()) + setMayNeedPaintInvalidation(); +} + +void LayoutBox::sizeChanged() { + // The size may change because of layout of other objects. Should check this + // object for paint invalidation. + if (!needsLayout()) + setMayNeedPaintInvalidation(); + if (node() && node()->isElementNode()) { Element& element = toElement(*node()); element.setNeedsResizeObserverUpdate(); } - // The frame rect may change because of layout of other objects. - // Should check this object for paint invalidation. - if (!needsLayout()) - setMayNeedPaintInvalidation(); - // The overflow clip paint property depends on the border box rect through - // overflowClipRect(). The border box rect's size equals the frame rect's - // size, so we trigger a paint property update when the framerect changes. - if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() && - shouldClipOverflow()) - setNeedsPaintPropertyUpdate(); + if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) { + if (shouldClipOverflow()) { + // The overflow clip paint property depends on the border box rect through + // overflowClipRect(). The border box rect's size equals the frame rect's + // size so we trigger a paint property update when the frame rect changes. + setNeedsPaintPropertyUpdate(); + } else if (styleRef().hasTransform()) { + // The transform paint property's origin depends on the frame rect because + // transform-origin can be sized with lengths relative to the frame rect. + // See: PaintPropertyTreeBuilder::updateTransform(...). + setNeedsPaintPropertyUpdate(); + } + } } bool LayoutBox::intersectsVisibleViewport() const {
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.h b/third_party/WebKit/Source/core/layout/LayoutBox.h index 795abf62..f8a4088 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.h +++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -212,25 +212,25 @@ if (x == m_frameRect.x()) return; m_frameRect.setX(x); - frameRectChanged(); + locationChanged(); } void setY(LayoutUnit y) { if (y == m_frameRect.y()) return; m_frameRect.setY(y); - frameRectChanged(); + locationChanged(); } void setWidth(LayoutUnit width) { if (width == m_frameRect.width()) return; m_frameRect.setWidth(width); - frameRectChanged(); + sizeChanged(); } void setHeight(LayoutUnit height) { if (height == m_frameRect.height()) return; m_frameRect.setHeight(height); - frameRectChanged(); + sizeChanged(); } LayoutUnit logicalLeft() const { @@ -324,7 +324,7 @@ if (location == m_frameRect.location()) return; m_frameRect.setLocation(location); - frameRectChanged(); + locationChanged(); } // The ancestor box that this object's location and physicalLocation are @@ -342,13 +342,13 @@ if (size == m_frameRect.size()) return; m_frameRect.setSize(size); - frameRectChanged(); + sizeChanged(); } void move(LayoutUnit dx, LayoutUnit dy) { if (!dx && !dy) return; m_frameRect.move(dx, dy); - frameRectChanged(); + locationChanged(); } // This function is in the container's coordinate system, meaning @@ -356,10 +356,8 @@ // inline-start/block-start margins. LayoutRect frameRect() const { return m_frameRect; } void setFrameRect(const LayoutRect& rect) { - if (rect == m_frameRect) - return; - m_frameRect = rect; - frameRectChanged(); + setLocation(rect.location()); + setSize(rect.size()); } // Note that those functions have their origin at this box's CSS border box. @@ -1495,7 +1493,8 @@ bool isBox() const = delete; // This will catch anyone doing an unnecessary check. - void frameRectChanged(); + void locationChanged(); + void sizeChanged(); virtual bool isInSelfHitTestingPhase(HitTestAction hitTestAction) const { return hitTestAction == HitTestForeground;
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.cpp b/third_party/WebKit/Source/core/layout/LayoutView.cpp index 5beaba00..6333e60 100644 --- a/third_party/WebKit/Source/core/layout/LayoutView.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutView.cpp
@@ -430,8 +430,10 @@ } void LayoutView::setShouldDoFullPaintInvalidationForViewAndAllDescendants() { - DCHECK(!RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()); - setShouldDoFullPaintInvalidationForViewAndAllDescendantsInternal(this); + if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) + setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); + else + setShouldDoFullPaintInvalidationForViewAndAllDescendantsInternal(this); } void LayoutView::invalidatePaintForViewAndCompositedLayers() {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc index c4a4410b..a0be580 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -181,7 +181,8 @@ NGConstraintSpace* constraint_space, NGBreakToken* break_token) : NGLayoutAlgorithm(kBlockLayoutAlgorithm), - state_(kStateInit), + layout_state_(kStateInit), + compute_minmax_state_(kStateInit), style_(style), first_child_(first_child), constraint_space_(constraint_space), @@ -190,20 +191,68 @@ DCHECK(style_); } +NGLayoutAlgorithm::MinAndMaxState +NGBlockLayoutAlgorithm::ComputeMinAndMaxContentSizes( + MinAndMaxContentSizes* sizes) { + switch (compute_minmax_state_) { + case kStateInit: + pending_minmax_sizes_.min_content = pending_minmax_sizes_.max_content = + LayoutUnit(); + // Size-contained elements don't consider their contents for intrinsic + // sizing. + if (style_->containsSize()) + return kSuccess; + current_minmax_child_ = first_child_; + compute_minmax_state_ = kStateChildLayout; + case kStateChildLayout: + // TODO: handle floats & orthogonal children + if (current_minmax_child_) { + Optional<MinAndMaxContentSizes> child_minmax; + if (NeedMinAndMaxContentSizesForContentContribution( + *current_minmax_child_->Style())) { + child_minmax = MinAndMaxContentSizes(); + if (!current_minmax_child_->ComputeMinAndMaxContentSizes( + &*child_minmax)) + return kPending; + } + MinAndMaxContentSizes child_sizes = ComputeMinAndMaxContentContribution( + *current_minmax_child_->Style(), child_minmax); + pending_minmax_sizes_.min_content = std::max( + pending_minmax_sizes_.min_content, child_sizes.min_content); + pending_minmax_sizes_.max_content = std::max( + pending_minmax_sizes_.max_content, child_sizes.max_content); + + current_minmax_child_ = current_minmax_child_->NextSibling(); + if (current_minmax_child_) + return kPending; + } + + *sizes = pending_minmax_sizes_; + sizes->max_content = std::max(sizes->min_content, sizes->max_content); + compute_minmax_state_ = kStateInit; + return kSuccess; + default: + NOTREACHED(); + return kSuccess; + }; +} + NGLayoutStatus NGBlockLayoutAlgorithm::Layout( NGPhysicalFragmentBase* child_fragment, NGPhysicalFragmentBase** fragment_out, NGLayoutAlgorithm** algorithm_out) { - switch (state_) { + switch (layout_state_) { case kStateInit: { + WTF::Optional<MinAndMaxContentSizes> sizes; + if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) { + sizes = MinAndMaxContentSizes(); + if (ComputeMinAndMaxContentSizes(&*sizes) == kPending) + return kNotFinished; + } + border_and_padding_ = ComputeBorders(Style()) + ComputePadding(ConstraintSpace(), Style()); - WTF::Optional<MinAndMaxContentSizes> sizes; - if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) { - // TODOO(layout-ng): Implement - sizes = MinAndMaxContentSizes(); - } LayoutUnit inline_size = ComputeInlineSizeForFragment(ConstraintSpace(), Style(), sizes); LayoutUnit adjusted_inline_size = @@ -238,7 +287,7 @@ builder_->SetInlineSize(inline_size).SetBlockSize(block_size); current_child_ = first_child_; - state_ = kStatePrepareForChildLayout; + layout_state_ = kStatePrepareForChildLayout; return kNotFinished; } case kStatePrepareForChildLayout: { @@ -255,11 +304,11 @@ space_for_current_child_ = CreateConstraintSpaceForCurrentChild(); *algorithm_out = NGLayoutInputNode::AlgorithmForInputNode( current_child_, space_for_current_child_); - state_ = kStateChildLayout; + layout_state_ = kStateChildLayout; return kChildAlgorithmRequired; } - state_ = kStateFinalize; + layout_state_ = kStateFinalize; return kNotFinished; } case kStateChildLayout: { @@ -274,7 +323,7 @@ ConstraintSpace().WritingMode(), ConstraintSpace().Direction(), toNGPhysicalFragment(child_fragment))); current_child_ = current_child_->NextSibling(); - state_ = kStatePrepareForChildLayout; + layout_state_ = kStatePrepareForChildLayout; return kNotFinished; } case kStateFinalize: { @@ -288,7 +337,7 @@ .SetInlineOverflow(max_inline_size_) .SetBlockOverflow(content_size_); *fragment_out = builder_->ToFragment(); - state_ = kStateInit; + layout_state_ = kStateInit; return kNewFragment; } }; @@ -446,9 +495,8 @@ // inline axis) // We have to keep this commented out for now until we correctly compute // min/max content sizes in Layout(). - // bool shrink_to_fit = CurrentChildStyle().display() == EDisplay::InlineBlock - // || CurrentChildStyle().isFloating(); - bool shrink_to_fit = false; + bool shrink_to_fit = CurrentChildStyle().display() == EDisplay::InlineBlock || + CurrentChildStyle().isFloating(); DCHECK(current_child_); space_builder_ ->SetIsNewFormattingContext( @@ -474,6 +522,7 @@ visitor->trace(space_builder_); visitor->trace(space_for_current_child_); visitor->trace(current_child_); + visitor->trace(current_minmax_child_); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h index c9c4a70..9ea2ca16 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
@@ -36,6 +36,7 @@ NGConstraintSpace* space, NGBreakToken* break_token = nullptr); + MinAndMaxState ComputeMinAndMaxContentSizes(MinAndMaxContentSizes*) override; NGLayoutStatus Layout(NGPhysicalFragmentBase*, NGPhysicalFragmentBase**, NGLayoutAlgorithm**) override; @@ -98,13 +99,14 @@ const ComputedStyle& Style() const { return *style_; } - enum State { + enum LayoutState { kStateInit, kStatePrepareForChildLayout, kStateChildLayout, kStateFinalize }; - State state_; + LayoutState layout_state_; + LayoutState compute_minmax_state_; RefPtr<const ComputedStyle> style_; @@ -115,6 +117,8 @@ Member<NGConstraintSpaceBuilder> space_builder_; Member<NGConstraintSpace> space_for_current_child_; Member<NGBlockNode> current_child_; + Member<NGBlockNode> current_minmax_child_; + MinAndMaxContentSizes pending_minmax_sizes_; NGBoxStrut border_and_padding_; LayoutUnit content_size_;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc index df0aee4d..ec08a06 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -20,12 +20,14 @@ NGConstraintSpace* ConstructConstraintSpace(NGWritingMode writing_mode, TextDirection direction, - NGLogicalSize size) { + NGLogicalSize size, + bool shrink_to_fit = false) { return NGConstraintSpaceBuilder(writing_mode) .SetAvailableSize(size) .SetPercentageResolutionSize(size) .SetTextDirection(direction) .SetWritingMode(writing_mode) + .SetIsShrinkToFit(shrink_to_fit) .ToConstraintSpace(); } @@ -49,6 +51,25 @@ return toNGPhysicalFragment(fragment); } + MinAndMaxContentSizes RunComputeMinAndMax(NGBlockNode* first_child) { + // The constraint space is not used for min/max computation, but we need + // it to create the algorithm. + NGConstraintSpace* space = + ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::Ltr, + NGLogicalSize(LayoutUnit(), LayoutUnit())); + NGBlockLayoutAlgorithm algorithm(style_.get(), first_child, space); + MinAndMaxContentSizes sizes; + NGLayoutAlgorithm::MinAndMaxState state; + while ((state = algorithm.ComputeMinAndMaxContentSizes(&sizes)) != + NGLayoutAlgorithm::kSuccess) { + EXPECT_NE(NGLayoutAlgorithm::kNotImplemented, state); + // shouldn't happen but let's avoid an infinite loop + if (state == NGLayoutAlgorithm::kNotImplemented) + break; + } + return sizes; + } + RefPtr<ComputedStyle> style_; }; @@ -779,5 +800,52 @@ EXPECT_EQ(kDiv1Size + kDiv2Size, child3->TopOffset()); } +// Verifies that we compute the right min and max-content size. +TEST_F(NGBlockLayoutAlgorithmTest, ComputeMinMaxContent) { + const int kWidth = 50; + const int kWidthChild1 = 20; + const int kWidthChild2 = 30; + + // This should have no impact on the min/max content size. + style_->setWidth(Length(kWidth, Fixed)); + + RefPtr<ComputedStyle> first_style = ComputedStyle::create(); + first_style->setWidth(Length(kWidthChild1, Fixed)); + NGBlockNode* first_child = new NGBlockNode(first_style.get()); + + RefPtr<ComputedStyle> second_style = ComputedStyle::create(); + second_style->setWidth(Length(kWidthChild2, Fixed)); + NGBlockNode* second_child = new NGBlockNode(second_style.get()); + + first_child->SetNextSibling(second_child); + + MinAndMaxContentSizes sizes = RunComputeMinAndMax(first_child); + EXPECT_EQ(kWidthChild2, sizes.min_content); + EXPECT_EQ(kWidthChild2, sizes.max_content); +} + +// Tests that we correctly handle shrink-to-fit +TEST_F(NGBlockLayoutAlgorithmTest, ShrinkToFit) { + const int kWidthChild1 = 20; + const int kWidthChild2 = 30; + + RefPtr<ComputedStyle> first_style = ComputedStyle::create(); + first_style->setWidth(Length(kWidthChild1, Fixed)); + NGBlockNode* first_child = new NGBlockNode(first_style.get()); + + RefPtr<ComputedStyle> second_style = ComputedStyle::create(); + second_style->setWidth(Length(kWidthChild2, Fixed)); + NGBlockNode* second_child = new NGBlockNode(second_style.get()); + + first_child->SetNextSibling(second_child); + + auto* space = ConstructConstraintSpace( + kHorizontalTopBottom, TextDirection::Ltr, + NGLogicalSize(LayoutUnit(100), NGSizeIndefinite), true); + NGPhysicalFragmentBase* frag = RunBlockLayoutAlgorithm(space, first_child); + + EXPECT_EQ(LayoutUnit(30), frag->Width()); +} + } // namespace } // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/resource/FontResource.cpp b/third_party/WebKit/Source/core/loader/resource/FontResource.cpp index c895500..8a32534 100644 --- a/third_party/WebKit/Source/core/loader/resource/FontResource.cpp +++ b/third_party/WebKit/Source/core/loader/resource/FontResource.cpp
@@ -150,9 +150,11 @@ float size, bool bold, bool italic, - FontOrientation orientation) { + FontOrientation orientation, + FontVariationSettings* fontVariationSettings) { DCHECK(m_fontData); - return m_fontData->fontPlatformData(size, bold, italic, orientation); + return m_fontData->fontPlatformData(size, bold, italic, orientation, + fontVariationSettings); } void FontResource::willReloadAfterDiskCacheMiss() {
diff --git a/third_party/WebKit/Source/core/loader/resource/FontResource.h b/third_party/WebKit/Source/core/loader/resource/FontResource.h index e1da2335..e7cfae3 100644 --- a/third_party/WebKit/Source/core/loader/resource/FontResource.h +++ b/third_party/WebKit/Source/core/loader/resource/FontResource.h
@@ -42,6 +42,7 @@ class FontPlatformData; class FontCustomPlatformData; class FontResourceClient; +class FontVariationSettings; class CORE_EXPORT FontResource final : public Resource { public: @@ -66,7 +67,8 @@ float size, bool bold, bool italic, - FontOrientation = FontOrientation::Horizontal); + FontOrientation = FontOrientation::Horizontal, + FontVariationSettings* = nullptr); // Returns true if the loading priority of the remote font resource can be // lowered. The loading priority of the font can be lowered only if the
diff --git a/third_party/WebKit/Source/core/loader/resource/FontResourceTest.cpp b/third_party/WebKit/Source/core/loader/resource/FontResourceTest.cpp index b30d380..988beed 100644 --- a/third_party/WebKit/Source/core/loader/resource/FontResourceTest.cpp +++ b/third_party/WebKit/Source/core/loader/resource/FontResourceTest.cpp
@@ -23,7 +23,11 @@ namespace blink { -class FontResourceTest : public ::testing::Test {}; +class FontResourceTest : public ::testing::Test { + void TearDown() override { + Platform::current()->getURLLoaderMockFactory()->unregisterAllURLs(); + } +}; // Tests if ResourceFetcher works fine with FontResource that requires defered // loading supports. @@ -138,7 +142,6 @@ EXPECT_TRUE(client3->fontLoadLongLimitExceededCalled()); Platform::current()->getURLLoaderMockFactory()->serveAsynchronousRequests(); - Platform::current()->getURLLoaderMockFactory()->unregisterURL(url); memoryCache()->remove(resource); }
diff --git a/third_party/WebKit/Source/core/page/FrameTree.cpp b/third_party/WebKit/Source/core/page/FrameTree.cpp index 6ec8771..85bcb34 100644 --- a/third_party/WebKit/Source/core/page/FrameTree.cpp +++ b/third_party/WebKit/Source/core/page/FrameTree.cpp
@@ -430,7 +430,7 @@ // Search the entire tree of each of the other pages in this namespace. // FIXME: Is random order OK? for (const Page* otherPage : Page::ordinaryPages()) { - if (otherPage == page) + if (otherPage == page || otherPage->isClosing()) continue; for (Frame* frame = otherPage->mainFrame(); frame; frame = frame->tree().traverseNext()) {
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp index 6a9bc39..13190a75 100644 --- a/third_party/WebKit/Source/core/page/Page.cpp +++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -63,13 +63,13 @@ // Set of all live pages; includes internal Page objects that are // not observable from scripts. static Page::PageSet& allPages() { - DEFINE_STATIC_LOCAL(Page::PageSet, allPages, ()); - return allPages; + DEFINE_STATIC_LOCAL(Page::PageSet, pages, ()); + return pages; } Page::PageSet& Page::ordinaryPages() { - DEFINE_STATIC_LOCAL(Page::PageSet, ordinaryPages, ()); - return ordinaryPages; + DEFINE_STATIC_LOCAL(Page::PageSet, pages, ()); + return pages; } void Page::networkStateChanged(bool online) { @@ -150,6 +150,17 @@ ASSERT(!m_mainFrame); } +void Page::closeSoon() { + // Make sure this Page can no longer be found by JS. + m_isClosing = true; + + // TODO(dcheng): Try to remove this in a followup, it's not obviously needed. + if (m_mainFrame->isLocalFrame()) + toLocalFrame(m_mainFrame)->loader().stopAllLoaders(); + + chromeClient().closeWindowSoon(); +} + ViewportDescription Page::viewportDescription() const { return mainFrame() && mainFrame()->isLocalFrame() && deprecatedLocalMainFrame()->document() @@ -504,10 +515,6 @@ m_scrollingCoordinator->willCloseLayerTreeView(layerTreeView); } -void Page::willBeClosed() { - ordinaryPages().remove(this); -} - void Page::willBeDestroyed() { Frame* mainFrame = m_mainFrame;
diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h index fe469260..918050f7 100644 --- a/third_party/WebKit/Source/core/page/Page.h +++ b/third_party/WebKit/Source/core/page/Page.h
@@ -60,6 +60,7 @@ class FrameHost; class PluginData; class PointerLockController; +class ScopedPageSuspender; class ScrollingCoordinator; class Settings; class SpellCheckerClient; @@ -104,7 +105,8 @@ ~Page() override; - void willBeClosed(); + void closeSoon(); + bool isClosing() const { return m_isClosing; } using PageSet = PersistentHeapHashSet<WeakMember<Page>>; @@ -186,10 +188,13 @@ return m_tabKeyCyclesThroughElements; } - // DefersLoading is used to delay loads during modal dialogs. - // Modal dialogs are supposed to freeze all background processes - // in the page, including prevent additional loads from staring/continuing. - void setSuspended(bool); + // Suspension is used to implement the "Optionally, pause while waiting for + // the user to acknowledge the message" step of simple dialog processing: + // https://html.spec.whatwg.org/multipage/webappapis.html#simple-dialogs + // + // Per https://html.spec.whatwg.org/multipage/webappapis.html#pause, no loads + // are allowed to start/continue in this state, and all background processing + // is also suspended. bool suspended() const { return m_suspended; } void setPageScaleFactor(float); @@ -227,6 +232,8 @@ void willBeDestroyed(); private: + friend class ScopedPageSuspender; + explicit Page(PageClients&); void initGroup(); @@ -234,6 +241,9 @@ // SettingsDelegate overrides. void settingsChanged(SettingsDelegate::ChangeType) override; + // ScopedPageSuspender helpers. + void setSuspended(bool); + Member<PageAnimator> m_animator; const Member<AutoscrollController> m_autoscrollController; Member<ChromeClient> m_chromeClient; @@ -269,6 +279,12 @@ HostsUsingFeatures m_hostsUsingFeatures; bool m_openedByDOM; + // Set to true when window.close() has been called and the Page will be + // destroyed. The browsing contexts in this page should no longer be + // discoverable via JS. + // TODO(dcheng): Try to remove |DOMWindow::m_windowIsClosing| in favor of + // this. However, this depends on resolving https://crbug.com/674641 + bool m_isClosing; bool m_tabKeyCyclesThroughElements; bool m_suspended;
diff --git a/third_party/WebKit/Source/core/page/ScopedPageSuspender.cpp b/third_party/WebKit/Source/core/page/ScopedPageSuspender.cpp index f03dd243..f169e32 100644 --- a/third_party/WebKit/Source/core/page/ScopedPageSuspender.cpp +++ b/third_party/WebKit/Source/core/page/ScopedPageSuspender.cpp
@@ -35,15 +35,6 @@ unsigned s_suspensionCount = 0; -void setSuspended(bool isSuspended) { - // Make a copy of the collection. Undeferring loads can cause script to run, - // which would mutate ordinaryPages() in the middle of iteration. - HeapVector<Member<Page>> pages; - copyToVector(Page::ordinaryPages(), pages); - for (const auto& page : pages) - page->setSuspended(isSuspended); -} - } // namespace ScopedPageSuspender::ScopedPageSuspender() { @@ -62,6 +53,16 @@ Platform::current()->currentThread()->scheduler()->resumeTimerQueue(); } +void ScopedPageSuspender::setSuspended(bool isSuspended) { + // Make a copy of the collection. Undeferring loads can cause script to run, + // which would mutate ordinaryPages() in the middle of iteration. + HeapVector<Member<Page>> pages; + copyToVector(Page::ordinaryPages(), pages); + + for (const auto& page : pages) + page->setSuspended(isSuspended); +} + bool ScopedPageSuspender::isActive() { return s_suspensionCount > 0; }
diff --git a/third_party/WebKit/Source/core/page/ScopedPageSuspender.h b/third_party/WebKit/Source/core/page/ScopedPageSuspender.h index 5b30bff..2889f8a 100644 --- a/third_party/WebKit/Source/core/page/ScopedPageSuspender.h +++ b/third_party/WebKit/Source/core/page/ScopedPageSuspender.h
@@ -26,6 +26,8 @@ namespace blink { +class Page; + class CORE_EXPORT ScopedPageSuspender final { WTF_MAKE_NONCOPYABLE(ScopedPageSuspender); USING_FAST_MALLOC(ScopedPageSuspender); @@ -34,6 +36,10 @@ explicit ScopedPageSuspender(); ~ScopedPageSuspender(); + private: + friend class Page; + + static void setSuspended(bool); static bool isActive(); };
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp index 32c4b10..47ef71c7 100644 --- a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp +++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
@@ -313,10 +313,7 @@ context.forcedSubtreeInvalidationFlags &= PaintInvalidatorContext:: ForcedSubtreeFullInvalidationForStackedContents; } else { - // For SPv1, subtree flags don't cross paint invalidation container - // boundary except for ForcedWholeTreeFullInvalidation. - context.forcedSubtreeInvalidationFlags &= - PaintInvalidatorContext::ForcedWholeTreeFullInvalidation; + context.forcedSubtreeInvalidationFlags = 0; } }
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.h b/third_party/WebKit/Source/core/paint/PaintInvalidator.h index 873b09b..e93ec3c 100644 --- a/third_party/WebKit/Source/core/paint/PaintInvalidator.h +++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.h
@@ -49,11 +49,6 @@ // TODO(crbug.com/637313): This is temporary before we support filters in // paint property tree. ForcedSubtreeSlowPathRect = 1 << 5, - // For SPv1, all of the above flags don't cross paint invalidation container - // boundary. ForceWholeTreeFullInvalidation does. - // TODO(wangxianzhu): Combine this with ForcedSubtreeFullInvalidation when - // removing non-SPv2 code. - ForcedWholeTreeFullInvalidation = 1 << 6, }; unsigned forcedSubtreeInvalidationFlags = 0;
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp index 8abb550..d566bce6 100644 --- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp +++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -268,15 +268,6 @@ } } -static FloatPoint3D transformOrigin(const LayoutBox& box) { - const ComputedStyle& style = box.styleRef(); - FloatSize borderBoxSize(box.size()); - return FloatPoint3D( - floatValueForLength(style.transformOriginX(), borderBoxSize.width()), - floatValueForLength(style.transformOriginY(), borderBoxSize.height()), - style.transformOriginZ()); -} - // SVG does not use the general transform update of |updateTransform|, instead // creating a transform node for SVG-specific transforms without 3D. void PaintPropertyTreeBuilder::updateTransformForNonRootSVG( @@ -330,6 +321,18 @@ return compositingReasons; } +static FloatPoint3D transformOrigin(const LayoutBox& box) { + const ComputedStyle& style = box.styleRef(); + // Transform origin has no effect without a transform or motion path. + if (!style.hasTransform()) + return FloatPoint3D(); + FloatSize borderBoxSize(box.size()); + return FloatPoint3D( + floatValueForLength(style.transformOriginX(), borderBoxSize.width()), + floatValueForLength(style.transformOriginY(), borderBoxSize.height()), + style.transformOriginZ()); +} + void PaintPropertyTreeBuilder::updateTransform( const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { @@ -350,10 +353,10 @@ // descendants. if (object.isBox() && (style.hasTransform() || style.preserves3D() || compositingReasons != CompositingReasonNone)) { + auto& box = toLayoutBox(object); TransformationMatrix matrix; style.applyTransform( - matrix, toLayoutBox(object).size(), - ComputedStyle::ExcludeTransformOrigin, + matrix, box.size(), ComputedStyle::ExcludeTransformOrigin, ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties); @@ -367,8 +370,7 @@ auto& properties = object.getMutableForPainting().ensurePaintProperties(); context.forceSubtreeUpdate |= properties.updateTransform( - context.current.transform, matrix, - transformOrigin(toLayoutBox(object)), + context.current.transform, matrix, transformOrigin(box), context.current.shouldFlattenInheritedTransform, renderingContextID, compositingReasons); } else {
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp index e4d02a0..969f3fa 100644 --- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp +++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -465,8 +465,10 @@ transform->layoutObject()->paintProperties(); EXPECT_EQ(TransformationMatrix(), transformProperties->transform()->matrix()); - EXPECT_EQ(FloatPoint3D(200, 150, 0), - transformProperties->transform()->origin()); + EXPECT_EQ(TransformationMatrix().translate(0, 0), + transformProperties->transform()->matrix()); + // The value is zero without a transform property that needs transform-offset. + EXPECT_EQ(FloatPoint3D(0, 0, 0), transformProperties->transform()->origin()); EXPECT_EQ(nullptr, transformProperties->paintOffsetTranslation()); EXPECT_TRUE(transformProperties->transform()->hasDirectCompositingReasons()); @@ -3085,4 +3087,73 @@ EXPECT_EQ(filterProperties->effect(), childPaintState.effect()); } +TEST_P(PaintPropertyTreeBuilderTest, TransformOriginWithAndWithoutTransform) { + setBodyInnerHTML( + "<style>" + " body { margin: 0 }" + " div {" + " width: 400px;" + " height: 100px;" + " }" + " #transform {" + " transform: translate(100px, 200px);" + " transform-origin: 75% 75% 0;" + " }" + " #willChange {" + " will-change: opacity;" + " transform-origin: 75% 75% 0;" + " }" + "</style>" + "<div id='transform'></div>" + "<div id='willChange'></div>"); + + auto* transform = document().getElementById("transform")->layoutObject(); + EXPECT_EQ(TransformationMatrix().translate3d(100, 200, 0), + transform->paintProperties()->transform()->matrix()); + EXPECT_EQ(FloatPoint3D(300, 75, 0), + transform->paintProperties()->transform()->origin()); + + auto* willChange = document().getElementById("willChange")->layoutObject(); + EXPECT_EQ(TransformationMatrix().translate3d(0, 0, 0), + willChange->paintProperties()->transform()->matrix()); + EXPECT_EQ(FloatPoint3D(0, 0, 0), + willChange->paintProperties()->transform()->origin()); +} + +TEST_P(PaintPropertyTreeBuilderTest, TransformOriginWithAndWithoutMotionPath) { + setBodyInnerHTML( + "<style>" + " body { margin: 0 }" + " div {" + " width: 100px;" + " height: 100px;" + " }" + " #motionPath {" + " position: absolute;" + " motion-path: path('M0 0 L 200 400');" + " motion-offset: 50%;" + " motion-rotation: 0deg;" + " transform-origin: 50% 50% 0;" + " }" + " #willChange {" + " will-change: opacity;" + " transform-origin: 50% 50% 0;" + " }" + "</style>" + "<div id='motionPath'></div>" + "<div id='willChange'></div>"); + + auto* motionPath = document().getElementById("motionPath")->layoutObject(); + EXPECT_EQ(TransformationMatrix().translate3d(50, 150, 0), + motionPath->paintProperties()->transform()->matrix()); + EXPECT_EQ(FloatPoint3D(50, 50, 0), + motionPath->paintProperties()->transform()->origin()); + + auto* willChange = document().getElementById("willChange")->layoutObject(); + EXPECT_EQ(TransformationMatrix().translate3d(0, 0, 0), + willChange->paintProperties()->transform()->matrix()); + EXPECT_EQ(FloatPoint3D(0, 0, 0), + willChange->paintProperties()->transform()->origin()); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp index f22a67d..7d80302 100644 --- a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp +++ b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
@@ -53,14 +53,6 @@ } PrePaintTreeWalkContext context(parentContext); - - if (frameView.shouldInvalidateAllPaintAndPaintProperties()) { - context.treeBuilderContext.forceSubtreeUpdate = true; - context.paintInvalidatorContext.forcedSubtreeInvalidationFlags |= - PaintInvalidatorContext::ForcedWholeTreeFullInvalidation; - frameView.clearShouldInvalidateAllPaintAndPaintProperties(); - } - m_propertyTreeBuilder.updateProperties(frameView, context.treeBuilderContext); m_paintInvalidator.invalidatePaintIfNeeded(frameView, context.paintInvalidatorContext);
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp index ee09e1a..4494a854 100644 --- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp +++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -2089,7 +2089,8 @@ visitedLink ? visitedLinkCaretColor() : caretColor(); // TODO(rego): We may want to adjust the caret color if it's the same than // the background to ensure good visibility and contrast. - result = autoColor.toStyleColor(); + result = autoColor.isAutoColor() ? StyleColor::currentColor() + : autoColor.toStyleColor(); break; } case CSSPropertyColor:
diff --git a/third_party/WebKit/Source/core/svg/SVGTransform.idl b/third_party/WebKit/Source/core/svg/SVGTransform.idl index 874f04b..9353ece 100644 --- a/third_party/WebKit/Source/core/svg/SVGTransform.idl +++ b/third_party/WebKit/Source/core/svg/SVGTransform.idl
@@ -35,10 +35,11 @@ const unsigned short SVG_TRANSFORM_SKEWY = 6; [ImplementedAs=transformType] readonly attribute unsigned short type; - // TODO(foolip): SVGMatrix should be DOMMatrixReadOnly. + // TODO(foolip): SVGMatrix should be DOMMatrix. readonly attribute SVGMatrix matrix; readonly attribute float angle; + // TODO(foolip): SVGMatrix should be DOMMatrixReadOnly. [RaisesException] void setMatrix(SVGMatrix matrix); [RaisesException] void setTranslate(float tx, float ty); [RaisesException] void setScale(float sx, float sy);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js index 9f70886..66c7252 100644 --- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js +++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
@@ -298,11 +298,13 @@ this._dataProvider = new Timeline.TimelineFlameChartDataProvider(this._model, frameModel, irModel, filters); var mainViewGroupExpansionSetting = Common.settings.createSetting('timelineFlamechartMainViewGroupExpansion', {}); this._mainView = new UI.FlameChart(this._dataProvider, this, mainViewGroupExpansionSetting); + this._mainView.alwaysShowVerticalScroll(); var networkViewGroupExpansionSetting = Common.settings.createSetting('timelineFlamechartNetworkViewGroupExpansion', {}); this._networkDataProvider = new Timeline.TimelineFlameChartNetworkDataProvider(this._model); this._networkView = new UI.FlameChart(this._networkDataProvider, this, networkViewGroupExpansionSetting); + this._networkView.alwaysShowVerticalScroll(); networkViewGroupExpansionSetting.addChangeListener(this.resizeToPreferredHeights.bind(this)); this._splitWidget.setMainWidget(this._mainView);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js index 790bef84..3ae124b 100644 --- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js +++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
@@ -556,8 +556,7 @@ for (var i = 0; i < this._overviewControls.length; ++i) this._overviewControls[i].timelineStarted(); - if (userInitiated) - Host.userMetrics.actionTaken(Host.UserMetrics.Action.TimelineStarted); + Host.userMetrics.actionTaken(Host.UserMetrics.Action.TimelineStarted); this._setUIControlsEnabled(false); this._hideLandingPage(); }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui_lazy/ChartViewport.js b/third_party/WebKit/Source/devtools/front_end/ui_lazy/ChartViewport.js index 10413d1..32ee51c 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui_lazy/ChartViewport.js +++ b/third_party/WebKit/Source/devtools/front_end/ui_lazy/ChartViewport.js
@@ -19,6 +19,7 @@ this.viewportElement, this._startRangeSelection.bind(this), this._rangeSelectionDragging.bind(this), this._endRangeSelection.bind(this), 'text', null); + this._alwaysShowVerticalScroll = false; this._vScrollElement = this.contentElement.createChild('div', 'flame-chart-v-scroll'); this._vScrollContent = this._vScrollElement.createChild('div'); this._vScrollElement.addEventListener('scroll', this._onScroll.bind(this), false); @@ -29,6 +30,11 @@ this.reset(); } + alwaysShowVerticalScroll() { + this._alwaysShowVerticalScroll = true; + this._vScrollElement.classList.add('always-show-scrollbar'); + } + /** * @return {boolean} */ @@ -48,7 +54,7 @@ * @private */ _updateScrollBar() { - var showScroll = this._totalHeight > this._offsetHeight; + const showScroll = this._alwaysShowVerticalScroll || this._totalHeight > this._offsetHeight; if (this._vScrollElement.classList.contains('hidden') !== showScroll) return; this._vScrollElement.classList.toggle('hidden', !showScroll);
diff --git a/third_party/WebKit/Source/devtools/front_end/ui_lazy/flameChart.css b/third_party/WebKit/Source/devtools/front_end/ui_lazy/flameChart.css index 0cd1ac1..4cebc9b 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui_lazy/flameChart.css +++ b/third_party/WebKit/Source/devtools/front_end/ui_lazy/flameChart.css
@@ -38,6 +38,10 @@ padding-left: 1px; } +.flame-chart-v-scroll.always-show-scrollbar { + overflow-y: scroll; +} + :host-context(.platform-mac) .flame-chart-v-scroll { right: 2px; top: 3px;
diff --git a/third_party/WebKit/Source/modules/fetch/Headers.idl b/third_party/WebKit/Source/modules/fetch/Headers.idl index 1e9c064..6b2b91af 100644 --- a/third_party/WebKit/Source/modules/fetch/Headers.idl +++ b/third_party/WebKit/Source/modules/fetch/Headers.idl
@@ -16,7 +16,7 @@ [RaisesException] void append(ByteString name, ByteString value); [ImplementedAs=remove, RaisesException] void delete(ByteString key); [RaisesException] ByteString? get(ByteString key); - [RaisesException] sequence<ByteString> getAll(ByteString name); + [RaisesException, Measure] sequence<ByteString> getAll(ByteString name); [RaisesException] boolean has(ByteString key); [RaisesException] void set(ByteString key, ByteString value); iterable<ByteString, ByteString>;
diff --git a/third_party/WebKit/Source/modules/filesystem/FileWriter.cpp b/third_party/WebKit/Source/modules/filesystem/FileWriter.cpp index e20faa99..ad60c05 100644 --- a/third_party/WebKit/Source/modules/filesystem/FileWriter.cpp +++ b/third_party/WebKit/Source/modules/filesystem/FileWriter.cpp
@@ -86,6 +86,8 @@ } void FileWriter::write(Blob* data, ExceptionState& exceptionState) { + if (!getExecutionContext()) + return; ASSERT(data); ASSERT(writer()); ASSERT(m_truncateLength == -1); @@ -115,6 +117,8 @@ } void FileWriter::seek(long long position, ExceptionState& exceptionState) { + if (!getExecutionContext()) + return; ASSERT(writer()); if (m_readyState == kWriting) { setError(FileError::kInvalidStateErr, exceptionState); @@ -127,6 +131,8 @@ } void FileWriter::truncate(long long position, ExceptionState& exceptionState) { + if (!getExecutionContext()) + return; ASSERT(writer()); ASSERT(m_truncateLength == -1); if (m_readyState == kWriting || position < 0) { @@ -154,6 +160,8 @@ } void FileWriter::abort(ExceptionState& exceptionState) { + if (!getExecutionContext()) + return; ASSERT(writer()); if (m_readyState != kWriting) return;
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.cpp b/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.cpp index fadbc47..9c31574 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.cpp +++ b/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.cpp
@@ -117,8 +117,17 @@ else if (initializer.source().isMessagePort()) m_sourceAsMessagePort = initializer.source().getAsMessagePort(); } - if (initializer.hasPorts()) - m_ports = new MessagePortArray(initializer.ports()); + if (initializer.hasPorts()) { + // TODO(sof): remove the extra same-heap checks once crbug.com/655926 + // has been resolved. + const MessagePortArray& ports = initializer.ports(); + m_ports = new MessagePortArray; + m_ports->reserveInitialCapacity(ports.size()); + for (const auto& port : ports) { + CHECK(ThreadState::current()->isOnThreadHeap(port.get())); + m_ports->append(port); + } + } } ExtendableMessageEvent::ExtendableMessageEvent( @@ -135,6 +144,14 @@ m_ports(ports) { if (m_serializedData) m_serializedData->registerMemoryAllocatedWithCurrentScriptContext(); + + // TODO(sof): remove the same-heap verification once crbug.com/655926 has + // been resolved. + if (m_ports) { + for (const auto& port : *m_ports) { + CHECK(ThreadState::current()->isOnThreadHeap(port.get())); + } + } } } // namespace blink
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.h b/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.h index a3a05a1..1086077 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.h +++ b/third_party/WebKit/Source/modules/serviceworkers/ExtendableMessageEvent.h
@@ -71,7 +71,8 @@ Member<ServiceWorkerClient> m_sourceAsClient; Member<ServiceWorker> m_sourceAsServiceWorker; Member<MessagePort> m_sourceAsMessagePort; - Member<MessagePortArray> m_ports; + // TODO(sof): remove once crbug.com/655926 has been addressed. + SameThreadCheckedMember<MessagePortArray> m_ports; }; } // namespace blink
diff --git a/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.h b/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.h index cf6f254..b2a15213 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.h +++ b/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.h
@@ -74,7 +74,8 @@ private: Member<RespondWithObserver> m_observer; - Member<Request> m_request; + // TODO(sof): remove once crbug.com/655926 has been diagnosed. + SameThreadCheckedMember<Request> m_request; Member<PreloadResponseProperty> m_preloadResponseProperty; String m_clientId; bool m_isReload;
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp index 89d723d..c1b5b5f 100644 --- a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp +++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp
@@ -37,7 +37,7 @@ } SpeechSynthesis::SpeechSynthesis(ExecutionContext* context) - : m_executionContext(context), + : ContextClient(context), m_platformSpeechSynthesizer(PlatformSpeechSynthesizer::create(this)), m_isPaused(false) {} @@ -48,7 +48,7 @@ void SpeechSynthesis::voicesDidChange() { m_voiceList.clear(); - if (!m_executionContext->isContextDestroyed()) + if (getExecutionContext()) dispatchEvent(Event::create(EventTypeNames::voiceschanged)); } @@ -126,12 +126,13 @@ SpeechSynthesisUtterance* utterance, unsigned long charIndex, const String& name) { - if (!m_executionContext->isContextDestroyed()) { - double elapsedTimeMillis = - (monotonicallyIncreasingTime() - utterance->startTime()) * 1000.0; - utterance->dispatchEvent(SpeechSynthesisEvent::create( - type, utterance, charIndex, elapsedTimeMillis, name)); - } + if (!getExecutionContext()) + return; + + double elapsedTimeMillis = + (monotonicallyIncreasingTime() - utterance->startTime()) * 1000.0; + utterance->dispatchEvent(SpeechSynthesisEvent::create( + type, utterance, charIndex, elapsedTimeMillis, name)); } void SpeechSynthesis::handleSpeakingCompleted( @@ -234,11 +235,11 @@ } DEFINE_TRACE(SpeechSynthesis) { - visitor->trace(m_executionContext); visitor->trace(m_platformSpeechSynthesizer); visitor->trace(m_voiceList); visitor->trace(m_utteranceQueue); PlatformSpeechSynthesizerClient::trace(visitor); + ContextClient::trace(visitor); EventTargetWithInlineData::trace(visitor); }
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h index 6363225..ce8e09f 100644 --- a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h +++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h
@@ -26,6 +26,7 @@ #ifndef SpeechSynthesis_h #define SpeechSynthesis_h +#include "core/dom/ContextLifecycleObserver.h" #include "modules/EventTargetModules.h" #include "modules/ModulesExport.h" #include "modules/speech/SpeechSynthesisUtterance.h" @@ -40,6 +41,7 @@ class MODULES_EXPORT SpeechSynthesis final : public EventTargetWithInlineData, + public ContextClient, public PlatformSpeechSynthesizerClient { DEFINE_WRAPPERTYPEINFO(); USING_GARBAGE_COLLECTED_MIXIN(SpeechSynthesis); @@ -64,7 +66,7 @@ DEFINE_ATTRIBUTE_EVENT_LISTENER(voiceschanged); ExecutionContext* getExecutionContext() const override { - return m_executionContext; + return ContextClient::getExecutionContext(); } DECLARE_VIRTUAL_TRACE(); @@ -93,7 +95,6 @@ // Returns the utterance at the front of the queue. SpeechSynthesisUtterance* currentSpeechUtterance() const; - Member<ExecutionContext> m_executionContext; Member<PlatformSpeechSynthesizer> m_platformSpeechSynthesizer; HeapVector<Member<SpeechSynthesisVoice>> m_voiceList; HeapDeque<Member<SpeechSynthesisUtterance>> m_utteranceQueue;
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp index 68a8339..f7c3d31 100644 --- a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp +++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp
@@ -37,17 +37,13 @@ SpeechSynthesisUtterance::SpeechSynthesisUtterance(ExecutionContext* context, const String& text) - : m_executionContext(context), + : ContextClient(context), m_platformUtterance(PlatformSpeechSynthesisUtterance::create(this)) { m_platformUtterance->setText(text); } SpeechSynthesisUtterance::~SpeechSynthesisUtterance() {} -ExecutionContext* SpeechSynthesisUtterance::getExecutionContext() const { - return m_executionContext; -} - const AtomicString& SpeechSynthesisUtterance::interfaceName() const { return EventTargetNames::SpeechSynthesisUtterance; } @@ -67,9 +63,9 @@ } DEFINE_TRACE(SpeechSynthesisUtterance) { - visitor->trace(m_executionContext); visitor->trace(m_platformUtterance); visitor->trace(m_voice); + ContextClient::trace(visitor); EventTargetWithInlineData::trace(visitor); }
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h index bad1e60..6cb1f62 100644 --- a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h +++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h
@@ -26,6 +26,7 @@ #ifndef SpeechSynthesisUtterance_h #define SpeechSynthesisUtterance_h +#include "core/dom/ContextLifecycleObserver.h" #include "modules/EventTargetModules.h" #include "modules/speech/SpeechSynthesisVoice.h" #include "platform/heap/Handle.h" @@ -35,6 +36,7 @@ class SpeechSynthesisUtterance final : public EventTargetWithInlineData, + public ContextClient, public PlatformSpeechSynthesisUtteranceClient { DEFINE_WRAPPERTYPEINFO(); USING_GARBAGE_COLLECTED_MIXIN(SpeechSynthesisUtterance); @@ -75,7 +77,9 @@ DEFINE_ATTRIBUTE_EVENT_LISTENER(mark); DEFINE_ATTRIBUTE_EVENT_LISTENER(boundary); - ExecutionContext* getExecutionContext() const override; + ExecutionContext* getExecutionContext() const override { + return ContextClient::getExecutionContext(); + } PlatformSpeechSynthesisUtterance* platformUtterance() const { return m_platformUtterance; @@ -89,7 +93,6 @@ // EventTarget const AtomicString& interfaceName() const override; - Member<ExecutionContext> m_executionContext; Member<PlatformSpeechSynthesisUtterance> m_platformUtterance; Member<SpeechSynthesisVoice> m_voice; };
diff --git a/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn b/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn index b70bc67..efda79be 100644 --- a/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn +++ b/third_party/WebKit/Source/modules/time_zone_monitor/BUILD.gn
@@ -13,6 +13,9 @@ deps = [ "//device/time_zone_monitor/public/interfaces:interfaces_blink", "//mojo/public/cpp/bindings", + "//services/device/public/interfaces:interfaces_blink", + "//services/service_manager/public/interfaces:interfaces_blink", + "//third_party/WebKit/Source/platform", "//third_party/icu:icui18n", ] }
diff --git a/third_party/WebKit/Source/modules/time_zone_monitor/DEPS b/third_party/WebKit/Source/modules/time_zone_monitor/DEPS index 91badb2..212c63f5 100644 --- a/third_party/WebKit/Source/modules/time_zone_monitor/DEPS +++ b/third_party/WebKit/Source/modules/time_zone_monitor/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+device/time_zone_monitor/public/interfaces/time_zone_monitor.mojom-blink.h", "+mojo/public/cpp/bindings/binding.h", + "+services/device/public/interfaces/constants.mojom-blink.h", "+third_party/icu/source/i18n/unicode/timezone.h", ]
diff --git a/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.cpp b/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.cpp index 18e2ff1..a7c0ab4 100644 --- a/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.cpp +++ b/third_party/WebKit/Source/modules/time_zone_monitor/TimeZoneMonitorClient.cpp
@@ -10,8 +10,9 @@ #include "core/dom/ExecutionContextTask.h" #include "core/workers/WorkerBackingThread.h" #include "core/workers/WorkerThread.h" -#include "public/platform/InterfaceProvider.h" +#include "platform/ServiceConnector.h" #include "public/platform/Platform.h" +#include "services/device/public/interfaces/constants.mojom-blink.h" #include "third_party/icu/source/i18n/unicode/timezone.h" #include <v8.h> @@ -36,8 +37,8 @@ DEFINE_STATIC_LOCAL(TimeZoneMonitorClient, instance, ()); device::mojom::blink::TimeZoneMonitorPtr monitor; - Platform::current()->interfaceProvider()->getInterface( - mojo::MakeRequest(&monitor)); + ServiceConnector::instance().connectToInterface( + device::mojom::blink::kServiceName, mojo::MakeRequest(&monitor)); monitor->AddClient(instance.m_binding.CreateInterfacePtrAndBind()); }
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp b/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp index 189aaa8..182465b 100644 --- a/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp +++ b/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp
@@ -730,7 +730,7 @@ for (; writeIndex < fillToFrame; ++writeIndex) values[writeIndex] = value; } else { - float numSampleFrames = deltaTime * sampleRate; + double numSampleFrames = deltaTime * sampleRate; // The value goes exponentially from value1 to value2 in a duration of // deltaTime seconds according to //
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn index d45fa60..a974f2ab 100644 --- a/third_party/WebKit/Source/platform/BUILD.gn +++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -333,6 +333,7 @@ "SecureTextInput.cpp", "SecureTextInput.h", "SerializedResource.h", + "ServiceConnector.h", "SharedBuffer.cpp", "SharedBuffer.h", "SharedBufferChunkReader.cpp", @@ -672,6 +673,7 @@ "fonts/mac/FontFamilyMatcherMac.h", "fonts/mac/FontFamilyMatcherMac.mm", "fonts/mac/FontPlatformDataMac.mm", + "fonts/opentype/FontSettings.cpp", "fonts/opentype/FontSettings.h", "fonts/opentype/OpenTypeCapsSupport.cpp", "fonts/opentype/OpenTypeCapsSupport.h", @@ -1442,6 +1444,7 @@ "//gpu/command_buffer/common:common", "//media", "//net", + "//services/service_manager/public/interfaces:interfaces_blink", "//skia", "//third_party:jpeg", "//third_party/WebKit/Source/wtf", @@ -1706,6 +1709,7 @@ "fonts/UnicodeRangeSetTest.cpp", "fonts/android/FontCacheAndroidTest.cpp", "fonts/mac/FontFamilyMatcherMacTest.mm", + "fonts/opentype/FontSettingsTest.cpp", "fonts/opentype/OpenTypeVerticalDataTest.cpp", "fonts/shaping/CachingWordShaperTest.cpp", "fonts/shaping/HarfBuzzShaperTest.cpp", @@ -1793,6 +1797,7 @@ "scheduler/renderer/web_view_scheduler_impl_unittest.cc", "scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc", "scroll/ScrollableAreaTest.cpp", + "scroll/ScrollbarThemeOverlayTest.cpp", "testing/ArenaTestHelpers.h", "testing/TreeTestHelpers.cpp", "testing/TreeTestHelpers.h",
diff --git a/third_party/WebKit/Source/platform/DEPS b/third_party/WebKit/Source/platform/DEPS index c15cda8..0595f59 100644 --- a/third_party/WebKit/Source/platform/DEPS +++ b/third_party/WebKit/Source/platform/DEPS
@@ -33,6 +33,7 @@ "+mozilla", "+platform", "+public/platform", + "+services/service_manager/public/interfaces", "+skia/ext", "+third_party/ced/src/compact_enc_det/compact_enc_det.h", "+third_party/khronos",
diff --git a/third_party/WebKit/Source/platform/ServiceConnector.h b/third_party/WebKit/Source/platform/ServiceConnector.h new file mode 100644 index 0000000..6d6b56b --- /dev/null +++ b/third_party/WebKit/Source/platform/ServiceConnector.h
@@ -0,0 +1,60 @@ +// Copyright 2016 The Chromium 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 ServiceConnector_h +#define ServiceConnector_h + +#include "mojo/public/cpp/bindings/binding.h" +#include "public/platform/Platform.h" +#include "services/service_manager/public/interfaces/connector.mojom-blink.h" +#include "wtf/StdLibExtras.h" + +namespace blink { + +// ServiceConnector supports connecting to Mojo services by name. +class ServiceConnector { + public: + static ServiceConnector& instance() { + DEFINE_STATIC_LOCAL(ServiceConnector, instance, ()); + return instance; + } + + template <typename Interface> + void connectToInterface(const char* serviceName, + mojo::InterfaceRequest<Interface> request) { + if (!m_connector.is_bound()) { + Platform::current()->bindServiceConnector( + mojo::MakeRequest(&m_connector).PassMessagePipe()); + } + + if (m_connector.encountered_error()) + return; + + service_manager::mojom::blink::IdentityPtr remoteIdentity( + service_manager::mojom::blink::Identity::New()); + remoteIdentity->name = serviceName; + remoteIdentity->user_id = service_manager::mojom::blink::kInheritUserID; + remoteIdentity->instance = ""; + + service_manager::mojom::blink::InterfaceProviderPtr remoteInterfaces; + m_connector->Connect(std::move(remoteIdentity), + MakeRequest(&remoteInterfaces), nullptr, + base::Bind(&ServiceConnector::onConnectionCompleted)); + + remoteInterfaces->GetInterface(Interface::Name_, request.PassMessagePipe()); + } + + private: + ServiceConnector() {} + + static void onConnectionCompleted( + service_manager::mojom::ConnectResult result, + const WTF::String& targetUserID) {} + + service_manager::mojom::blink::ConnectorPtr m_connector; +}; + +} // namespace blink + +#endif // ServiceConnector_h
diff --git a/third_party/WebKit/Source/platform/exported/Platform.cpp b/third_party/WebKit/Source/platform/exported/Platform.cpp index 70fee4a..2f69919a 100644 --- a/third_party/WebKit/Source/platform/exported/Platform.cpp +++ b/third_party/WebKit/Source/platform/exported/Platform.cpp
@@ -151,4 +151,7 @@ return InterfaceProvider::getEmptyInterfaceProvider(); } +void Platform::bindServiceConnector( + mojo::ScopedMessagePipeHandle remoteHandle) {} + } // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/FontCacheKey.h b/third_party/WebKit/Source/platform/fonts/FontCacheKey.h index 89c1232..ae1d8d2 100644 --- a/third_party/WebKit/Source/platform/fonts/FontCacheKey.h +++ b/third_party/WebKit/Source/platform/fonts/FontCacheKey.h
@@ -32,6 +32,7 @@ #define FontCacheKey_h #include "FontFaceCreationParams.h" +#include "platform/fonts/opentype/FontSettings.h" #include "wtf/Allocator.h" #include "wtf/HashMap.h" #include "wtf/HashTableDeletedValueType.h" @@ -51,21 +52,27 @@ FontCacheKey() : m_creationParams(), m_fontSize(0), m_options(0) {} FontCacheKey(FontFaceCreationParams creationParams, float fontSize, - unsigned options) + unsigned options, + PassRefPtr<FontVariationSettings> variationSettings) : m_creationParams(creationParams), m_fontSize(fontSize * s_fontSizePrecisionMultiplier), - m_options(options) {} + m_options(options), + m_variationSettings(variationSettings) {} + FontCacheKey(WTF::HashTableDeletedValueType) : m_fontSize(hashTableDeletedSize()) {} unsigned hash() const { - unsigned hashCodes[3] = {m_creationParams.hash(), m_fontSize, m_options}; + unsigned hashCodes[4] = { + m_creationParams.hash(), m_fontSize, m_options, + m_variationSettings ? m_variationSettings->hash() : 0}; return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes); } bool operator==(const FontCacheKey& other) const { return m_creationParams == other.m_creationParams && - m_fontSize == other.m_fontSize && m_options == other.m_options; + m_fontSize == other.m_fontSize && m_options == other.m_options && + m_variationSettings == other.m_variationSettings; } bool isHashTableDeletedValue() const { @@ -84,6 +91,7 @@ FontFaceCreationParams m_creationParams; unsigned m_fontSize; unsigned m_options; + RefPtr<FontVariationSettings> m_variationSettings; }; struct FontCacheKeyHash {
diff --git a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp index 67a56fe7..7b25a9a 100644 --- a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp +++ b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.cpp
@@ -37,6 +37,7 @@ #include "platform/fonts/FontCache.h" #include "platform/fonts/FontPlatformData.h" #include "platform/fonts/WebFontDecoder.h" +#include "platform/fonts/opentype/FontSettings.h" #include "third_party/skia/include/core/SkStream.h" #include "third_party/skia/include/core/SkTypeface.h" #include "wtf/PtrUtil.h" @@ -46,7 +47,7 @@ FontCustomPlatformData::FontCustomPlatformData(sk_sp<SkTypeface> typeface, size_t dataSize) - : m_typeface(typeface), m_dataSize(dataSize) {} + : m_baseTypeface(typeface), m_dataSize(dataSize) {} FontCustomPlatformData::~FontCustomPlatformData() {} @@ -54,10 +55,47 @@ float size, bool bold, bool italic, - FontOrientation orientation) { - ASSERT(m_typeface); - return FontPlatformData(m_typeface, "", size, bold && !m_typeface->isBold(), - italic && !m_typeface->isItalic(), orientation); + FontOrientation orientation, + const FontVariationSettings* variationSettings) { + DCHECK(m_baseTypeface); + + sk_sp<SkTypeface> returnTypeface = m_baseTypeface; + + // Maximum axis count is maximum value for the OpenType USHORT, which is a + // 16bit unsigned. https://www.microsoft.com/typography/otspec/fvar.htm + // Variation settings coming from CSS can have duplicate assignments and the + // list can be longer than UINT16_MAX, but ignoring this for now, going with a + // reasonable upper limit and leaving the deduplication for TODO(drott), + // crbug.com/674878 second duplicate value should supersede first.. + if (variationSettings && variationSettings->size() < UINT16_MAX) { + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + Vector<SkFontMgr::FontParameters::Axis, 0> axes; + axes.reserveCapacity(variationSettings->size()); + for (size_t i = 0; i < variationSettings->size(); ++i) { + SkFontMgr::FontParameters::Axis axis = { + atomicStringToFourByteTag(variationSettings->at(i).tag()), + SkFloatToScalar(variationSettings->at(i).value())}; + axes.append(axis); + } + + sk_sp<SkTypeface> skVariationFont(fm->createFromStream( + m_baseTypeface->openStream(nullptr)->duplicate(), + SkFontMgr::FontParameters().setAxes(axes.data(), axes.size()))); + + if (skVariationFont) { + returnTypeface = skVariationFont; + } else { + SkString familyName; + m_baseTypeface->getFamilyName(&familyName); + // TODO: Surface this as a console message? + LOG(ERROR) << "Unable for apply variation axis properties for font: " + << familyName.c_str(); + } + } + + return FontPlatformData(returnTypeface, "", size, + bold && !m_baseTypeface->isBold(), + italic && !m_baseTypeface->isItalic(), orientation); } std::unique_ptr<FontCustomPlatformData> FontCustomPlatformData::create(
diff --git a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h index 055ded0..80eb0cf 100644 --- a/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h +++ b/third_party/WebKit/Source/platform/fonts/FontCustomPlatformData.h
@@ -47,6 +47,7 @@ class FontPlatformData; class SharedBuffer; +class FontVariationSettings; class PLATFORM_EXPORT FontCustomPlatformData { USING_FAST_MALLOC(FontCustomPlatformData); @@ -62,14 +63,15 @@ float size, bool bold, bool italic, - FontOrientation = FontOrientation::Horizontal); + FontOrientation = FontOrientation::Horizontal, + const FontVariationSettings* = nullptr); size_t dataSize() const { return m_dataSize; } static bool supportsFormat(const String&); private: FontCustomPlatformData(sk_sp<SkTypeface>, size_t dataSize); - sk_sp<SkTypeface> m_typeface; + sk_sp<SkTypeface> m_baseTypeface; size_t m_dataSize; };
diff --git a/third_party/WebKit/Source/platform/fonts/FontDescription.cpp b/third_party/WebKit/Source/platform/fonts/FontDescription.cpp index f0012be..b8c4da4 100644 --- a/third_party/WebKit/Source/platform/fonts/FontDescription.cpp +++ b/third_party/WebKit/Source/platform/fonts/FontDescription.cpp
@@ -224,7 +224,8 @@ static_cast<unsigned>(m_fields.m_subpixelTextPosition); // bit 1 return FontCacheKey(creationParams, effectiveFontSize(), - options | fontTraits.bitfield() << 8); + options | fontTraits.bitfield() << 8, + m_variationSettings); } void FontDescription::setDefaultTypesettingFeatures( @@ -311,16 +312,9 @@ addToHash(hash, settings->at(i).value()); } } - const FontVariationSettings* varSettings = variationSettings(); - if (varSettings) { - unsigned numFeatures = varSettings->size(); - for (unsigned i = 0; i < numFeatures; ++i) { - const AtomicString& tag = varSettings->at(i).tag(); - for (unsigned j = 0; j < tag.length(); j++) - stringHasher.addCharacter(tag[j]); - addToHash(hash, varSettings->at(i).value()); - } - } + + if (variationSettings()) + addToHash(hash, variationSettings()->hash()); if (m_locale) { const AtomicString& locale = m_locale->localeString();
diff --git a/third_party/WebKit/Source/platform/fonts/FontPlatformData.h b/third_party/WebKit/Source/platform/fonts/FontPlatformData.h index 862af84..e2725206 100644 --- a/third_party/WebKit/Source/platform/fonts/FontPlatformData.h +++ b/third_party/WebKit/Source/platform/fonts/FontPlatformData.h
@@ -74,6 +74,7 @@ class Font; class HarfBuzzFace; +class FontVariationSettings; class PLATFORM_EXPORT FontPlatformData { USING_FAST_MALLOC(FontPlatformData); @@ -95,9 +96,10 @@ #if OS(MACOSX) FontPlatformData(NSFont*, float size, - bool syntheticBold = false, - bool syntheticItalic = false, - FontOrientation = FontOrientation::Horizontal); + bool syntheticBold, + bool syntheticItalic, + FontOrientation, + FontVariationSettings*); #endif FontPlatformData(sk_sp<SkTypeface>, const char* name,
diff --git a/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp b/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp index 41dc288..73a0cc3 100644 --- a/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp +++ b/third_party/WebKit/Source/platform/fonts/WebFontDecoder.cpp
@@ -99,6 +99,17 @@ const uint32_t gdefTag = OTS_TAG('G', 'D', 'E', 'F'); const uint32_t gposTag = OTS_TAG('G', 'P', 'O', 'S'); const uint32_t gsubTag = OTS_TAG('G', 'S', 'U', 'B'); + + // Font Variations related tables + // See "Variation Tables" in Terminology section of + // https://www.microsoft.com/typography/otspec/otvaroverview.htm + const uint32_t avarTag = OTS_TAG('a', 'v', 'a', 'r'); + const uint32_t cvarTag = OTS_TAG('c', 'v', 'a', 'r'); + const uint32_t fvarTag = OTS_TAG('f', 'v', 'a', 'r'); + const uint32_t gvarTag = OTS_TAG('g', 'v', 'a', 'r'); + const uint32_t hvarTag = OTS_TAG('H', 'V', 'A', 'R'); + const uint32_t mvarTag = OTS_TAG('M', 'V', 'A', 'R'); + const uint32_t vvarTag = OTS_TAG('V', 'V', 'A', 'R'); #endif switch (tag) { @@ -110,6 +121,13 @@ case cpalTag: #if HB_VERSION_ATLEAST(1, 0, 0) // Let HarfBuzz handle how to deal with broken tables. + case avarTag: + case cvarTag: + case fvarTag: + case gvarTag: + case hvarTag: + case mvarTag: + case vvarTag: case gdefTag: case gposTag: case gsubTag:
diff --git a/third_party/WebKit/Source/platform/fonts/linux/FontPlatformDataLinux.cpp b/third_party/WebKit/Source/platform/fonts/linux/FontPlatformDataLinux.cpp index d6b97cd..fcc5e56 100644 --- a/third_party/WebKit/Source/platform/fonts/linux/FontPlatformDataLinux.cpp +++ b/third_party/WebKit/Source/platform/fonts/linux/FontPlatformDataLinux.cpp
@@ -45,6 +45,12 @@ paint->setTypeface(m_typeface); paint->setFakeBoldText(m_syntheticBold); paint->setTextSkewX(m_syntheticItalic ? -SK_Scalar1 / 4 : 0); + + // TODO(drott): Due to Skia bug 5917 + // https://bugs.chromium.org/p/skia/issues/detail?id=5917 correct advance + // width scaling with FreeType for font sizes under 257px currently only works + // with: + // paint->setHinting(SkPaint::kNo_Hinting); } } // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm index 51655f7b..469f2d48 100644 --- a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm +++ b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
@@ -216,7 +216,8 @@ substituteFont, platformData.size(), syntheticBold, (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait), - platformData.orientation()); + platformData.orientation(), + nullptr); // No variation paramaters in fallback. return fontDataFromFontPlatformData(&alternateFont, DoNotRetain); } @@ -281,9 +282,10 @@ // font loading failing. Out-of-process loading occurs for registered fonts // stored in non-system locations. When loading fails, we do not want to use // the returned FontPlatformData since it will not have a valid SkTypeface. - std::unique_ptr<FontPlatformData> platformData = WTF::wrapUnique( - new FontPlatformData(platformFont, size, syntheticBold, syntheticItalic, - fontDescription.orientation())); + std::unique_ptr<FontPlatformData> platformData = + WTF::makeUnique<FontPlatformData>( + platformFont, size, syntheticBold, syntheticItalic, + fontDescription.orientation(), fontDescription.variationSettings()); if (!platformData->typeface()) { return nullptr; }
diff --git a/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm b/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm index 27a754dd..d09bc6d 100644 --- a/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm +++ b/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm
@@ -25,6 +25,7 @@ #import "platform/LayoutTestSupport.h" #import "platform/fonts/Font.h" +#import "platform/fonts/opentype/FontSettings.h" #import "platform/fonts/shaping/HarfBuzzFace.h" #import "platform/graphics/skia/SkiaUtils.h" #import "public/platform/Platform.h" @@ -163,7 +164,8 @@ float size, bool syntheticBold, bool syntheticItalic, - FontOrientation orientation) + FontOrientation orientation, + FontVariationSettings* variationSettings) : m_textSize(size), m_syntheticBold(syntheticBold), m_syntheticItalic(syntheticItalic), @@ -178,6 +180,21 @@ // and ~/Library Fonts, see crbug.com/72727 or crbug.com/108645. m_typeface = loadFromBrowserProcess(nsFont, size); } + + if (variationSettings && variationSettings->size() < UINT16_MAX) { + SkFontMgr::FontParameters::Axis axes[variationSettings->size()]; + for (size_t i = 0; i < variationSettings->size(); ++i) { + AtomicString featureTag = variationSettings->at(i).tag(); + axes[i] = {atomicStringToFourByteTag(featureTag), + SkFloatToScalar(variationSettings->at(i).value())}; + } + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + // TODO crbug.com/670246: Refactor this to a future Skia API that acccepts + // axis parameters on system fonts directly. + m_typeface = sk_sp<SkTypeface>(fm->createFromStream( + m_typeface->openStream(nullptr)->duplicate(), + SkFontMgr::FontParameters().setAxes(axes, variationSettings->size()))); + } } } // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.cpp b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.cpp new file mode 100644 index 0000000..6759f231 --- /dev/null +++ b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.cpp
@@ -0,0 +1,42 @@ +// Copyright 2016 The Chromium 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 "platform/fonts/opentype/FontSettings.h" + +#include "wtf/HashFunctions.h" +#include "wtf/StringHasher.h" +#include "wtf/text/AtomicStringHash.h" +#include "wtf/text/StringHash.h" + +namespace blink { + +uint32_t atomicStringToFourByteTag(AtomicString tag) { + DCHECK_EQ(tag.length(), 4u); + return (((tag[0]) << 24) | ((tag[1]) << 16) | ((tag[2]) << 8) | (tag[3])); +} + +static inline void addToHash(unsigned& hash, unsigned key) { + hash = ((hash << 5) + hash) + key; // Djb2 +}; + +static inline void addFloatToHash(unsigned& hash, float value) { + addToHash(hash, WTF::FloatHash<float>::hash(value)); +}; + +unsigned FontVariationSettings::hash() const { + unsigned computedHash = size() ? 5381 : 0; + unsigned numFeatures = size(); + for (unsigned i = 0; i < numFeatures; ++i) { + StringHasher stringHasher; + const AtomicString& tag = at(i).tag(); + for (unsigned j = 0; j < tag.length(); j++) { + stringHasher.addCharacter(tag[j]); + } + addToHash(computedHash, stringHasher.hash()); + addFloatToHash(computedHash, at(i).value()); + } + return computedHash; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h index d57d2eec..a8b7e48ea 100644 --- a/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h +++ b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h
@@ -15,6 +15,8 @@ namespace blink { +uint32_t atomicStringToFourByteTag(AtomicString tag); + template <typename T> class FontTagValuePair { DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); @@ -51,7 +53,7 @@ FontSettings(){}; private: - Vector<T> m_list; + Vector<T, 0> m_list; }; using FontFeature = FontTagValuePair<int>; @@ -81,6 +83,8 @@ return adoptRef(new FontVariationSettings()); } + unsigned hash() const; + private: FontVariationSettings() = default; };
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/FontSettingsTest.cpp b/third_party/WebKit/Source/platform/fonts/opentype/FontSettingsTest.cpp new file mode 100644 index 0000000..1914118 --- /dev/null +++ b/third_party/WebKit/Source/platform/fonts/opentype/FontSettingsTest.cpp
@@ -0,0 +1,44 @@ +// Copyright 2016 The Chromium 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 "platform/fonts/opentype/FontSettings.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "wtf/PassRefPtr.h" + +namespace blink { + +PassRefPtr<FontVariationSettings> makeFontVariationSettings( + std::initializer_list<FontVariationAxis> variationAxes) { + RefPtr<FontVariationSettings> variationSettings = + FontVariationSettings::create(); + + for (auto axis = variationAxes.begin(); axis != variationAxes.end(); ++axis) { + variationSettings->append(*axis); + } + return variationSettings; +} + +TEST(FontSettingsTest, HashTest) { + RefPtr<FontVariationSettings> oneAxisA = + makeFontVariationSettings({FontVariationAxis{"a ", 0}}); + RefPtr<FontVariationSettings> oneAxisB = + makeFontVariationSettings({FontVariationAxis{"b ", 0}}); + RefPtr<FontVariationSettings> twoAxes = makeFontVariationSettings( + {FontVariationAxis{"a ", 0}, FontVariationAxis{"b ", 0}}); + RefPtr<FontVariationSettings> twoAxesDifferentValue = + makeFontVariationSettings( + {FontVariationAxis{"a ", 0}, FontVariationAxis{"b ", 1}}); + + RefPtr<FontVariationSettings> emptyVariationSettings = + FontVariationSettings::create(); + + CHECK_NE(oneAxisA->hash(), oneAxisB->hash()); + CHECK_NE(oneAxisA->hash(), twoAxes->hash()); + CHECK_NE(oneAxisA->hash(), twoAxesDifferentValue->hash()); + CHECK_NE(emptyVariationSettings->hash(), oneAxisA->hash()); + CHECK_EQ(emptyVariationSettings->hash(), 0u); +}; + +} // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp index 1e5daf32..a364f53 100644 --- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp +++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzFace.cpp
@@ -386,6 +386,9 @@ m_harfBuzzFontData->m_paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); m_harfBuzzFontData->m_rangeSet = rangeSet; m_harfBuzzFontData->updateSimpleFontData(m_platformData); + + // TODO crbug.com/674879 - Connect variation axis parameters to future + // HarfBuzz API here. ASSERT(m_harfBuzzFontData->m_simpleFontData); int scale = SkiaScalarToHarfBuzzPosition(m_platformData->size()); hb_font_set_scale(m_unscaledFont, scale, scale);
diff --git a/third_party/WebKit/Source/platform/heap/GarbageCollected.h b/third_party/WebKit/Source/platform/heap/GarbageCollected.h index e814c8c0..58c9313c 100644 --- a/third_party/WebKit/Source/platform/heap/GarbageCollected.h +++ b/third_party/WebKit/Source/platform/heap/GarbageCollected.h
@@ -14,10 +14,8 @@ template <typename T> class GarbageCollected; -class HeapObjectHeader; class InlinedGlobalMarkingVisitor; class TraceWrapperBase; -class WrapperVisitor; // GC_PLUGIN_IGNORE is used to make the plugin ignore a particular class or // field when checking for proper usage. When using GC_PLUGIN_IGNORE @@ -77,11 +75,7 @@ virtual void trace(Visitor*) {} virtual void adjustAndMark(InlinedGlobalMarkingVisitor) const = 0; virtual void trace(InlinedGlobalMarkingVisitor); - virtual void adjustAndMarkAndTraceWrapper(const WrapperVisitor*) const = 0; - virtual void adjustAndMarkWrapperNoTracing(const WrapperVisitor*) const = 0; - virtual void adjustAndTraceMarkedWrapper(const WrapperVisitor*) const = 0; virtual bool isHeapObjectAlive() const = 0; - virtual HeapObjectHeader* adjustAndGetHeapObjectHeader() const = 0; }; #define DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(VISITOR, TYPE) \ @@ -104,47 +98,6 @@ \ private: -#define DEFINE_GARBAGE_COLLECTED_MIXIN_WRAPPER_METHODS(TYPE) \ - private: \ - typedef WTF::IsSubclassOfTemplate<typename std::remove_const<TYPE>::type, \ - blink::GarbageCollected> \ - IsSubclassOfGarbageCollected; \ - \ - public: \ - void adjustAndMarkAndTraceWrapper(const WrapperVisitor* visitor) \ - const override { \ - static_assert( \ - IsSubclassOfGarbageCollected::value, \ - "only garbage collected objects can have garbage collected mixins"); \ - return AdjustAndMarkTrait<TYPE>::markAndTraceWrapper( \ - visitor, static_cast<const TYPE*>(this)); \ - } \ - void adjustAndMarkWrapperNoTracing(const WrapperVisitor* visitor) \ - const override { \ - static_assert( \ - IsSubclassOfGarbageCollected::value, \ - "only garbage collected objects can have garbage collected mixins"); \ - return AdjustAndMarkTrait<TYPE>::markWrapperNoTracing( \ - visitor, static_cast<const TYPE*>(this)); \ - } \ - void adjustAndTraceMarkedWrapper(const WrapperVisitor* visitor) \ - const override { \ - static_assert( \ - IsSubclassOfGarbageCollected::value, \ - "only garbage collected objects can have garbage collected mixins"); \ - AdjustAndMarkTrait<TYPE>::traceMarkedWrapper( \ - visitor, static_cast<const TYPE*>(this)); \ - } \ - HeapObjectHeader* adjustAndGetHeapObjectHeader() const override { \ - static_assert( \ - IsSubclassOfGarbageCollected::value, \ - "only garbage collected objects can have garbage collected mixins"); \ - return AdjustAndMarkTrait<TYPE>::heapObjectHeader( \ - static_cast<const TYPE*>(this)); \ - } \ - \ - private: - // A C++ object's vptr will be initialized to its leftmost base's vtable after // the constructors of all its subclasses have run, so if a subclass constructor // tries to access any of the vtbl entries of its leftmost base prematurely, @@ -204,7 +157,6 @@ DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::Visitor*, TYPE) \ DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::InlinedGlobalMarkingVisitor, \ TYPE) \ - DEFINE_GARBAGE_COLLECTED_MIXIN_WRAPPER_METHODS(TYPE) \ DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE) \ public: \ bool isHeapObjectAlive() const override { \
diff --git a/third_party/WebKit/Source/platform/heap/Member.h b/third_party/WebKit/Source/platform/heap/Member.h index 1a6502d..db16863 100644 --- a/third_party/WebKit/Source/platform/heap/Member.h +++ b/third_party/WebKit/Source/platform/heap/Member.h
@@ -136,12 +136,11 @@ // constructor wasn't called. if (m_creationThreadState) { // Member should point to objects that belong in the same ThreadHeap. - DCHECK_EQ(&ThreadState::fromObject(m_raw)->heap(), - &m_creationThreadState->heap()); + DCHECK(m_creationThreadState->isOnThreadHeap(m_raw)); // Member should point to objects that belong in the same ThreadHeap. DCHECK_EQ(¤t->heap(), &m_creationThreadState->heap()); } else { - DCHECK_EQ(&ThreadState::fromObject(m_raw)->heap(), ¤t->heap()); + DCHECK(current->isOnThreadHeap(m_raw)); } } @@ -361,12 +360,11 @@ // constructor wasn't called. if (m_creationThreadState) { // Member should point to objects that belong in the same ThreadHeap. - CHECK_EQ(&ThreadState::fromObject(this->m_raw)->heap(), - &m_creationThreadState->heap()); + CHECK(m_creationThreadState->isOnThreadHeap(this->m_raw)); // Member should point to objects that belong in the same ThreadHeap. CHECK_EQ(¤t->heap(), &m_creationThreadState->heap()); } else { - CHECK_EQ(&ThreadState::fromObject(this->m_raw)->heap(), ¤t->heap()); + CHECK(current->isOnThreadHeap(this->m_raw)); } }
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.h b/third_party/WebKit/Source/platform/heap/ThreadState.h index a8c771b8..284a3de 100644 --- a/third_party/WebKit/Source/platform/heap/ThreadState.h +++ b/third_party/WebKit/Source/platform/heap/ThreadState.h
@@ -573,6 +573,15 @@ static const char* gcReasonString(BlinkGC::GCReason); + // Returns |true| if |object| resides on this thread's heap. + // It is well-defined to call this method on any heap allocated + // reference, provided its associated heap hasn't been detached + // and shut down. Its behavior is undefined for any other pointer + // value. + bool isOnThreadHeap(const void* object) const { + return &fromObject(object)->heap() == &heap(); + } + private: template <typename T> friend class PrefinalizerRegistration;
diff --git a/third_party/WebKit/Source/platform/heap/TraceTraits.h b/third_party/WebKit/Source/platform/heap/TraceTraits.h index 01bfdd9..cf0baa0 100644 --- a/third_party/WebKit/Source/platform/heap/TraceTraits.h +++ b/third_party/WebKit/Source/platform/heap/TraceTraits.h
@@ -47,28 +47,6 @@ STATIC_ONLY(AdjustAndMarkTrait); public: - static void markAndTraceWrapper(const WrapperVisitor* visitor, const T* t) { - if (visitor->markWrapperHeader(heapObjectHeader(t))) { - visitor->markWrappersInAllWorlds(t); - visitor->dispatchTraceWrappers(t); - } - } - static void markWrapperNoTracing(const WrapperVisitor* visitor, const T* t) { - DCHECK(!heapObjectHeader(t)->isWrapperHeaderMarked()); - visitor->markWrapperHeader(heapObjectHeader(t)); - } - static void traceMarkedWrapper(const WrapperVisitor* visitor, const T* t) { - DCHECK(heapObjectHeader(t)->isWrapperHeaderMarked()); - // The term *mark* is misleading here as we effectively trace through the - // API boundary, i.e., tell V8 that an object is alive. Actual marking - // will be done in V8. - visitor->markWrappersInAllWorlds(t); - visitor->dispatchTraceWrappers(t); - } - static HeapObjectHeader* heapObjectHeader(const T* t) { - return HeapObjectHeader::fromPayload(t); - } - template <typename VisitorDispatcher> static void mark(VisitorDispatcher visitor, const T* t) { #if ENABLE(ASSERT) @@ -111,19 +89,6 @@ STATIC_ONLY(AdjustAndMarkTrait); public: - static void markAndTraceWrapper(const WrapperVisitor* visitor, const T* t) { - t->adjustAndMarkAndTraceWrapper(visitor); - } - static void markWrapperNoTracing(const WrapperVisitor* visitor, const T* t) { - t->adjustAndMarkWrapperNoTracing(visitor); - } - static void traceMarkedWrapper(const WrapperVisitor* visitor, const T* t) { - t->adjustAndTraceMarkedWrapper(visitor); - } - static HeapObjectHeader* heapObjectHeader(const T* t) { - return t->adjustAndGetHeapObjectHeader(); - } - template <typename VisitorDispatcher> static void mark(VisitorDispatcher visitor, const T* self) { if (!self) @@ -223,41 +188,28 @@ static void trace(Visitor*, void* self); static void trace(InlinedGlobalMarkingVisitor, void* self); - static void markAndTraceWrapper(const WrapperVisitor* visitor, - const void* t) { - static_assert(CanTraceWrappers<T>::value, - "T should be able to trace wrappers. See " - "dispatchTraceWrappers in WrapperVisitor.h"); - AdjustAndMarkTrait<T>::markAndTraceWrapper(visitor, - reinterpret_cast<const T*>(t)); - } - static void markWrapperNoTracing(const WrapperVisitor* visitor, - const void* t) { - static_assert(CanTraceWrappers<T>::value, - "T should be able to trace wrappers. See " - "dispatchTraceWrappers in WrapperVisitor.h"); - AdjustAndMarkTrait<T>::markWrapperNoTracing(visitor, - reinterpret_cast<const T*>(t)); - } - static void traceMarkedWrapper(const WrapperVisitor* visitor, const void* t) { - static_assert(CanTraceWrappers<T>::value, - "T should be able to trace wrappers. See " - "dispatchTraceWrappers in WrapperVisitor.h"); - AdjustAndMarkTrait<T>::traceMarkedWrapper(visitor, - reinterpret_cast<const T*>(t)); - } - static HeapObjectHeader* heapObjectHeader(const void* t) { - static_assert(CanTraceWrappers<T>::value, - "T should be able to trace wrappers. See " - "dispatchTraceWrappers in WrapperVisitor.h"); - return AdjustAndMarkTrait<T>::heapObjectHeader( - reinterpret_cast<const T*>(t)); - } + static void markAndTraceWrapper(const WrapperVisitor*, const void*); + static void markWrapperNoTracing(const WrapperVisitor*, const void*); + static void traceMarkedWrapper(const WrapperVisitor*, const void*); + static HeapObjectHeader* heapObjectHeader(const void*); template <typename VisitorDispatcher> static void mark(VisitorDispatcher visitor, const T* t) { AdjustAndMarkTrait<T>::mark(visitor, t); } + + private: + static const T* ToWrapperTracingType(const void* t) { + static_assert(CanTraceWrappers<T>::value, + "T should be able to trace wrappers. See " + "dispatchTraceWrappers in WrapperVisitor.h"); + static_assert(!NeedsAdjustAndMark<T>::value, + "wrapper tracing is not supported within mixins"); +#if DCHECK_IS_ON() + DCHECK(HeapObjectHeader::fromPayload(t)->checkHeader()); +#endif + return reinterpret_cast<const T*>(t); + } }; template <typename T> @@ -281,6 +233,41 @@ static_cast<T*>(self)->trace(visitor); } +template <typename T> +void TraceTrait<T>::markAndTraceWrapper(const WrapperVisitor* visitor, + const void* t) { + const T* traceable = ToWrapperTracingType(t); + if (visitor->markWrapperHeader(heapObjectHeader(traceable))) { + visitor->markWrappersInAllWorlds(traceable); + visitor->dispatchTraceWrappers(traceable); + } +} + +template <typename T> +void TraceTrait<T>::markWrapperNoTracing(const WrapperVisitor* visitor, + const void* t) { + const T* traceable = ToWrapperTracingType(t); + DCHECK(!heapObjectHeader(traceable)->isWrapperHeaderMarked()); + visitor->markWrapperHeader(heapObjectHeader(traceable)); +} + +template <typename T> +void TraceTrait<T>::traceMarkedWrapper(const WrapperVisitor* visitor, + const void* t) { + const T* traceable = ToWrapperTracingType(t); + DCHECK(heapObjectHeader(t)->isWrapperHeaderMarked()); + // The term *mark* is misleading here as we effectively trace through the + // API boundary, i.e., tell V8 that an object is alive. Actual marking + // will be done in V8. + visitor->markWrappersInAllWorlds(traceable); + visitor->dispatchTraceWrappers(traceable); +} + +template <typename T> +HeapObjectHeader* TraceTrait<T>::heapObjectHeader(const void* t) { + return HeapObjectHeader::fromPayload(ToWrapperTracingType(t)); +} + template <typename T, typename Traits> struct TraceTrait<HeapVectorBacking<T, Traits>> { STATIC_ONLY(TraceTrait);
diff --git a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h index 6315aad..a4fc977 100644 --- a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h +++ b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
@@ -154,14 +154,6 @@ #undef DECLARE_DISPATCH_TRACE_WRAPPERS - void dispatchTraceWrappers(const void*) const { - // This call should never be reached as we access all tracing through - // TraceTraits, which will check that we can dispatch at compile time. - // This handler is merely here to make adjustAndMarkWrapper compile - // for GarbageCollectedMixin objects. - NOTREACHED(); - } - virtual bool markWrapperHeader(HeapObjectHeader*) const = 0; virtual void markWrappersInAllWorlds(const ScriptWrappable*) const = 0;
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc index fda7d476..30442f5 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc +++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
@@ -105,11 +105,13 @@ } void WillBeginFrameIdleTask(RendererScheduler* scheduler, + uint64_t sequence_number, base::SimpleTestTickClock* clock, base::TimeTicks deadline) { scheduler->WillBeginFrame(cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); + BEGINFRAME_FROM_HERE, 0, sequence_number, clock->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000), + cc::BeginFrameArgs::NORMAL)); } void UpdateClockToDeadlineIdleTestTask(base::SimpleTestTickClock* clock, @@ -314,8 +316,9 @@ void DoMainFrame() { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = false; scheduler_->WillBeginFrame(begin_frame_args); scheduler_->DidCommitFrameToCompositor(); @@ -323,8 +326,9 @@ void DoMainFrameOnCriticalPath() { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); } @@ -667,6 +671,7 @@ scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner_; bool simulate_timer_task_ran_; bool simulate_compositor_task_ran_; + uint64_t next_begin_frame_number_ = cc::BeginFrameArgs::kStartingFrameNumber; DISALLOW_COPY_AND_ASSIGN(RendererSchedulerImplTest); }; @@ -715,8 +720,9 @@ EXPECT_EQ(0, run_count); // Shouldn't run yet as no WillBeginFrame. scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000), + cc::BeginFrameArgs::NORMAL)); RunUntilIdle(); EXPECT_EQ(0, run_count); // Shouldn't run as no DidCommitFrameToCompositor. @@ -726,8 +732,9 @@ EXPECT_EQ(0, run_count); // We missed the deadline. scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000), + cc::BeginFrameArgs::NORMAL)); clock_->Advance(base::TimeDelta::FromMilliseconds(800)); scheduler_->DidCommitFrameToCompositor(); RunUntilIdle(); @@ -787,16 +794,18 @@ // Trigger the beginning of an idle period for 1000ms. scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000), + cc::BeginFrameArgs::NORMAL)); DoMainFrame(); // End the idle period early (after 500ms), and send a WillBeginFrame which // specifies that the next idle period should end 1000ms from now. clock_->Advance(base::TimeDelta::FromMilliseconds(500)); scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000), + cc::BeginFrameArgs::NORMAL)); RunUntilIdle(); EXPECT_EQ(0, run_count); // Not currently in an idle period. @@ -2079,8 +2088,9 @@ base::RetainedRef(idle_task_runner_), &run_count, clock_.get(), idle_task_runtime, &actual_deadlines)); idle_task_runner_->PostIdleTask( - FROM_HERE, base::Bind(&WillBeginFrameIdleTask, - base::Unretained(scheduler_.get()), clock_.get())); + FROM_HERE, + base::Bind(&WillBeginFrameIdleTask, base::Unretained(scheduler_.get()), + next_begin_frame_number_++, clock_.get())); RunUntilIdle(); EXPECT_EQ(4, run_count); } @@ -2159,8 +2169,9 @@ // Next long idle period will be for the maximum time, so // CanExceedIdleDeadlineIfRequired should return true. scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000), + cc::BeginFrameArgs::NORMAL)); EXPECT_FALSE(scheduler_->CanExceedIdleDeadlineIfRequired()); } @@ -2371,8 +2382,9 @@ ASSERT_FALSE(scheduler_->BeginMainFrameOnCriticalPath()); cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000), + cc::BeginFrameArgs::NORMAL); scheduler_->WillBeginFrame(begin_frame_args); ASSERT_TRUE(scheduler_->BeginMainFrameOnCriticalPath()); @@ -2748,8 +2760,9 @@ simulate_timer_task_ran_ = false; cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = false; scheduler_->WillBeginFrame(begin_frame_args); @@ -2786,8 +2799,9 @@ simulate_timer_task_ran_ = false; cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = false; scheduler_->WillBeginFrame(begin_frame_args); scheduler_->DidAnimateForInputOnCompositorThread(); @@ -2827,8 +2841,9 @@ simulate_timer_task_ran_ = false; cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = false; scheduler_->WillBeginFrame(begin_frame_args); @@ -2891,8 +2906,9 @@ SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START, blink::WebInputEvent::GestureScrollUpdate); cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = false; scheduler_->WillBeginFrame(begin_frame_args); @@ -2914,8 +2930,9 @@ RendererSchedulerImplTest, EstimateLongestJankFreeTaskDuration_UseCase_MAIN_THREAD_CUSTOM_INPUT_HANDLING) { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = false; scheduler_->WillBeginFrame(begin_frame_args); @@ -2938,8 +2955,9 @@ SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START); cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); @@ -3049,8 +3067,9 @@ SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START); cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); @@ -3094,8 +3113,9 @@ for (int i = 0; i < 1000; i++) { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); scheduler_->DidHandleInputEventOnCompositorThread( @@ -3157,8 +3177,9 @@ bool suspended = false; for (int i = 0; i < 1000; i++) { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); scheduler_->DidHandleInputEventOnCompositorThread( @@ -3214,8 +3235,9 @@ for (int i = 0; i < 1000; i++) { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); scheduler_->DidHandleInputEventOnCompositorThread( @@ -3253,8 +3275,9 @@ RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); @@ -3314,8 +3337,9 @@ for (int i = 0; i < 100; i++) { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); scheduler_->DidHandleInputEventOnCompositorThread( @@ -3355,8 +3379,9 @@ for (int i = 0; i < 100; i++) { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); scheduler_->DidHandleInputEventOnCompositorThread( @@ -3398,8 +3423,9 @@ for (int i = 0; i < 100; i++) { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); scheduler_->DidHandleInputEventOnCompositorThread( @@ -3522,8 +3548,9 @@ for (int i = 0; i < 1000; i++) { cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), - base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_->NowTicks(), + base::TimeTicks(), base::TimeDelta::FromMilliseconds(16), + cc::BeginFrameArgs::NORMAL); begin_frame_args.on_critical_path = true; scheduler_->WillBeginFrame(begin_frame_args); scheduler_->DidHandleInputEventOnCompositorThread(
diff --git a/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp b/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp index a246461..1c616d32 100644 --- a/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp +++ b/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
@@ -531,7 +531,9 @@ return; m_enabled = e; theme().updateEnabledState(*this); - setNeedsPaintInvalidation(AllParts); + + ScrollbarPart invalidParts = theme().invalidateOnEnabledChange(); + setNeedsPaintInvalidation(invalidParts); } int Scrollbar::scrollbarThickness() const {
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarTestSuite.h b/third_party/WebKit/Source/platform/scroll/ScrollbarTestSuite.h index a4789a58..6bb469f 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollbarTestSuite.h +++ b/third_party/WebKit/Source/platform/scroll/ScrollbarTestSuite.h
@@ -34,14 +34,14 @@ MOCK_CONST_METHOD1(scrollSize, int(ScrollbarOrientation)); MOCK_CONST_METHOD0(isScrollCornerVisible, bool()); MOCK_CONST_METHOD0(scrollCornerRect, IntRect()); - MOCK_CONST_METHOD0(horizontalScrollbar, Scrollbar*()); - MOCK_CONST_METHOD0(verticalScrollbar, Scrollbar*()); MOCK_CONST_METHOD0(enclosingScrollableArea, ScrollableArea*()); MOCK_CONST_METHOD1(visibleContentRect, IntRect(IncludeScrollbarsInRect)); MOCK_CONST_METHOD0(contentsSize, IntSize()); MOCK_CONST_METHOD0(scrollableAreaBoundingBox, IntRect()); MOCK_CONST_METHOD0(layerForHorizontalScrollbar, GraphicsLayer*()); MOCK_CONST_METHOD0(layerForVerticalScrollbar, GraphicsLayer*()); + MOCK_CONST_METHOD0(horizontalScrollbar, Scrollbar*()); + MOCK_CONST_METHOD0(verticalScrollbar, Scrollbar*()); bool userInputScrollable(ScrollbarOrientation) const override { return true; } bool scrollbarsCanBeActive() const override { return true; } @@ -68,13 +68,16 @@ DEFINE_INLINE_VIRTUAL_TRACE() { ScrollableArea::trace(visitor); } + protected: + explicit MockScrollableArea() : m_maximumScrollOffset(ScrollOffset(0, 100)) {} + explicit MockScrollableArea(const ScrollOffset& offset) + : m_maximumScrollOffset(offset) {} + private: void setMaximumScrollOffset(const ScrollOffset& maximumScrollOffset) { m_maximumScrollOffset = maximumScrollOffset; } - explicit MockScrollableArea() : m_maximumScrollOffset(ScrollOffset(0, 100)) {} - ScrollOffset m_scrollOffset; ScrollOffset m_maximumScrollOffset; };
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h b/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h index 9036cd2..948af56 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h +++ b/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h
@@ -94,6 +94,10 @@ return AllParts; } + // Returns parts of the scrollbar which must be repainted following a change + // in enabled state. + virtual ScrollbarPart invalidateOnEnabledChange() const { return AllParts; } + virtual void paintScrollCorner(GraphicsContext&, const DisplayItemClient&, const IntRect& cornerRect);
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp index c2ef26e9..14878b96 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp +++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp
@@ -59,6 +59,21 @@ m_allowHitTest(allowHitTest), m_useSolidColor(false) {} +bool ScrollbarThemeOverlay::shouldRepaintAllPartsOnInvalidation() const { + return false; +} + +ScrollbarPart ScrollbarThemeOverlay::invalidateOnThumbPositionChange( + const ScrollbarThemeClient&, + float oldPosition, + float newPosition) const { + return NoPart; +} + +ScrollbarPart ScrollbarThemeOverlay::invalidateOnEnabledChange() const { + return NoPart; +} + int ScrollbarThemeOverlay::scrollbarThickness( ScrollbarControlSize controlSize) { return m_thumbThickness + m_scrollbarMargin;
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h index d746291..0772e21 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h +++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h
@@ -46,6 +46,15 @@ Color); ~ScrollbarThemeOverlay() override {} + bool shouldRepaintAllPartsOnInvalidation() const override; + + ScrollbarPart invalidateOnThumbPositionChange( + const ScrollbarThemeClient&, + float oldPosition, + float newPosition) const override; + + ScrollbarPart invalidateOnEnabledChange() const override; + int scrollbarThickness(ScrollbarControlSize) override; int scrollbarMargin() const override; bool usesOverlayScrollbars() const override;
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp new file mode 100644 index 0000000..028ed37e --- /dev/null +++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp
@@ -0,0 +1,127 @@ +// Copyright 2016 The Chromium 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 "platform/scroll/ScrollbarThemeOverlay.h" + +#include "platform/scroll/ScrollbarTestSuite.h" + +namespace blink { + +using testing::NiceMock; +using testing::Return; + +class ScrollbarThemeOverlayTest : public ScrollbarTestSuite {}; + +TEST_F(ScrollbarThemeOverlayTest, PaintInvalidation) { + NiceMock<MockScrollableArea>* mockScrollableArea = + new NiceMock<MockScrollableArea>(ScrollOffset(100, 100)); + ScrollbarThemeOverlay theme(14, 0, ScrollbarThemeOverlay::AllowHitTest); + + Scrollbar* verticalScrollbar = Scrollbar::createForTesting( + mockScrollableArea, VerticalScrollbar, RegularScrollbar, &theme); + Scrollbar* horizontalScrollbar = Scrollbar::createForTesting( + mockScrollableArea, HorizontalScrollbar, RegularScrollbar, &theme); + ON_CALL(*mockScrollableArea, verticalScrollbar()) + .WillByDefault(Return(verticalScrollbar)); + ON_CALL(*mockScrollableArea, horizontalScrollbar()) + .WillByDefault(Return(horizontalScrollbar)); + + IntRect verticalRect(1010, 0, 14, 768); + IntRect horizontalRect(0, 754, 1024, 14); + verticalScrollbar->setFrameRect(verticalRect); + horizontalScrollbar->setFrameRect(horizontalRect); + + ASSERT_EQ(verticalScrollbar, mockScrollableArea->verticalScrollbar()); + ASSERT_EQ(horizontalScrollbar, mockScrollableArea->horizontalScrollbar()); + + verticalScrollbar->clearTrackNeedsRepaint(); + verticalScrollbar->clearThumbNeedsRepaint(); + horizontalScrollbar->clearTrackNeedsRepaint(); + horizontalScrollbar->clearThumbNeedsRepaint(); + mockScrollableArea->clearNeedsPaintInvalidationForScrollControls(); + + ASSERT_FALSE(verticalScrollbar->thumbNeedsRepaint()); + ASSERT_FALSE(verticalScrollbar->trackNeedsRepaint()); + ASSERT_FALSE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation()); + ASSERT_FALSE(horizontalScrollbar->thumbNeedsRepaint()); + ASSERT_FALSE(horizontalScrollbar->trackNeedsRepaint()); + ASSERT_FALSE(mockScrollableArea->horizontalScrollbarNeedsPaintInvalidation()); + + // Changing the scroll offset shouldn't invalide the thumb nor track, but it + // should cause a "general" invalidation for non-composited scrollbars. + // Ensure the horizontal scrollbar is unaffected. + mockScrollableArea->updateScrollOffset(ScrollOffset(0, 5), UserScroll); + verticalScrollbar->offsetDidChange(); + horizontalScrollbar->offsetDidChange(); + EXPECT_FALSE(verticalScrollbar->thumbNeedsRepaint()); + EXPECT_FALSE(verticalScrollbar->trackNeedsRepaint()); + EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation()); + EXPECT_FALSE(horizontalScrollbar->thumbNeedsRepaint()); + EXPECT_FALSE(horizontalScrollbar->trackNeedsRepaint()); + EXPECT_FALSE(mockScrollableArea->horizontalScrollbarNeedsPaintInvalidation()); + + // Try the horizontal scrollbar. + mockScrollableArea->clearNeedsPaintInvalidationForScrollControls(); + mockScrollableArea->updateScrollOffset(ScrollOffset(5, 5), UserScroll); + horizontalScrollbar->offsetDidChange(); + verticalScrollbar->offsetDidChange(); + EXPECT_FALSE(verticalScrollbar->thumbNeedsRepaint()); + EXPECT_FALSE(verticalScrollbar->trackNeedsRepaint()); + EXPECT_FALSE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation()); + EXPECT_FALSE(horizontalScrollbar->thumbNeedsRepaint()); + EXPECT_FALSE(horizontalScrollbar->trackNeedsRepaint()); + EXPECT_TRUE(mockScrollableArea->horizontalScrollbarNeedsPaintInvalidation()); + + mockScrollableArea->clearNeedsPaintInvalidationForScrollControls(); + + // Move the mouse over the vertical scrollbar's thumb. Ensure the thumb is + // invalidated as its state is changed to hover. + verticalScrollbar->setHoveredPart(ThumbPart); + EXPECT_TRUE(verticalScrollbar->thumbNeedsRepaint()); + EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation()); + + verticalScrollbar->clearThumbNeedsRepaint(); + mockScrollableArea->clearNeedsPaintInvalidationForScrollControls(); + + // Pressing down should also cause an invalidation. + verticalScrollbar->setPressedPart(ThumbPart); + EXPECT_TRUE(verticalScrollbar->thumbNeedsRepaint()); + EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation()); + + verticalScrollbar->clearThumbNeedsRepaint(); + mockScrollableArea->clearNeedsPaintInvalidationForScrollControls(); + + // Release should cause invalidation. + verticalScrollbar->setPressedPart(NoPart); + EXPECT_TRUE(verticalScrollbar->thumbNeedsRepaint()); + EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation()); + + verticalScrollbar->clearThumbNeedsRepaint(); + mockScrollableArea->clearNeedsPaintInvalidationForScrollControls(); + + // Move off should cause invalidation + verticalScrollbar->setHoveredPart(NoPart); + EXPECT_TRUE(verticalScrollbar->thumbNeedsRepaint()); + EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation()); + + verticalScrollbar->clearThumbNeedsRepaint(); + mockScrollableArea->clearNeedsPaintInvalidationForScrollControls(); + + // Disabling the scrollbar is used to hide it so it should cause invalidation + // but only in the general sense since the compositor will have hidden it + // without a repaint. + verticalScrollbar->setEnabled(false); + EXPECT_FALSE(verticalScrollbar->thumbNeedsRepaint()); + EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation()); + + mockScrollableArea->clearNeedsPaintInvalidationForScrollControls(); + + verticalScrollbar->setEnabled(true); + EXPECT_FALSE(verticalScrollbar->thumbNeedsRepaint()); + EXPECT_TRUE(mockScrollableArea->verticalScrollbarNeedsPaintInvalidation()); + + ThreadState::current()->collectAllGarbage(); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h b/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h index c4bd3ec..70d07b7f 100644 --- a/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h +++ b/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h
@@ -27,11 +27,8 @@ class WebURLLoaderTestDelegate; // A factory that creates WebURLLoaderMock to simulate resource loading in -// tests. -// You register files for specific URLs, the content of the file is then served -// when these URLs are loaded. -// In order to serve the asynchronous requests, you need to invoke -// ServeAsynchronousRequest. +// tests. Since there are restriction and rules to follow, please read comments +// in WebURLLoaderMockFactory carefully to use this class correctly. class WebURLLoaderMockFactoryImpl : public WebURLLoaderMockFactory { public: WebURLLoaderMockFactoryImpl();
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.cpp b/third_party/WebKit/Source/web/ChromeClientImpl.cpp index 09ea9e4..75a82ab8 100644 --- a/third_party/WebKit/Source/web/ChromeClientImpl.cpp +++ b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
@@ -477,12 +477,6 @@ } void ChromeClientImpl::closeWindowSoon() { - // Make sure this Page can no longer be found by JS. - m_webView->page()->willBeClosed(); - - // Make sure that all loading is stopped. Ensures that JS stops executing! - m_webView->mainFrame()->stopLoading(); - if (m_webView->client()) m_webView->client()->closeWidgetSoon(); }
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp b/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp index 7449b666..00a0c734 100644 --- a/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp +++ b/third_party/WebKit/Source/web/WebFrameWidgetBase.cpp
@@ -75,6 +75,10 @@ void WebFrameWidgetBase::dragTargetDragLeave() { DCHECK(m_currentDragData); + if (ignoreInputEvents()) { + cancelDrag(); + return; + } DragData dragData(m_currentDragData.get(), IntPoint(), IntPoint(), static_cast<DragOperation>(m_operationsAllowed)); @@ -109,12 +113,13 @@ return; } - m_currentDragData->setModifiers(modifiers); - DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint, - static_cast<DragOperation>(m_operationsAllowed)); + if (!ignoreInputEvents()) { + m_currentDragData->setModifiers(modifiers); + DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint, + static_cast<DragOperation>(m_operationsAllowed)); - page()->dragController().performDrag(&dragData, *toCoreFrame(localRoot())); - + page()->dragController().performDrag(&dragData, *toCoreFrame(localRoot())); + } m_dragOperation = WebDragOperationNone; m_currentDragData = nullptr; } @@ -122,6 +127,10 @@ void WebFrameWidgetBase::dragSourceEndedAt(const WebPoint& pointInViewport, const WebPoint& screenPoint, WebDragOperation operation) { + if (ignoreInputEvents()) { + cancelDrag(); + return; + } WebPoint pointInRootFrame( page()->frameHost().visualViewport().viewportToRootFrame( pointInViewport)); @@ -135,12 +144,16 @@ } void WebFrameWidgetBase::dragSourceSystemDragEnded() { - // It's possible for us to get this callback while not doing a drag if it's - // from a previous page that got unloaded. - if (m_doingDragAndDrop) { - page()->dragController().dragEnded(); - m_doingDragAndDrop = false; - } + cancelDrag(); +} + +void WebFrameWidgetBase::cancelDrag() { + // It's possible for us this to be callback while we're not doing a drag if + // it's from a previous page that got unloaded. + if (!m_doingDragAndDrop) + return; + page()->dragController().dragEnded(); + m_doingDragAndDrop = false; } void WebFrameWidgetBase::startDragging(WebReferrerPolicy policy, @@ -158,6 +171,10 @@ DragAction dragAction, int modifiers) { DCHECK(m_currentDragData); + if (ignoreInputEvents()) { + cancelDrag(); + return WebDragOperationNone; + } WebPoint pointInRootFrame(viewportToRootFrame(pointInViewport));
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetBase.h b/third_party/WebKit/Source/web/WebFrameWidgetBase.h index 67d3dd7a5..f424460 100644 --- a/third_party/WebKit/Source/web/WebFrameWidgetBase.h +++ b/third_party/WebKit/Source/web/WebFrameWidgetBase.h
@@ -108,6 +108,8 @@ WebDragOperation m_dragOperation = WebDragOperationNone; private: + void cancelDrag(); + static bool s_ignoreInputEvents; };
diff --git a/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp b/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp index 034bde6..4ec3622 100644 --- a/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp +++ b/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp
@@ -30,6 +30,7 @@ #include "core/loader/FrameLoadRequest.h" #include "core/page/Page.h" +#include "core/page/ScopedPageSuspender.h" #include "public/platform/WebInputEvent.h" #include "public/web/WebFrameClient.h" #include "public/web/WebLocalFrame.h" @@ -268,14 +269,13 @@ }; TEST_F(CreateWindowTest, CreateWindowFromSuspendedPage) { - m_webView->page()->setSuspended(true); + ScopedPageSuspender suspender; LocalFrame* frame = toWebLocalFrameImpl(m_mainFrame)->frame(); FrameLoadRequest request(frame->document()); WindowFeatures features; EXPECT_EQ(nullptr, m_chromeClientImpl->createWindow(frame, request, features, NavigationPolicyNewForegroundTab)); - m_webView->page()->setSuspended(false); } } // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp index 257c108f..0840d8c1 100644 --- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp +++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -4119,6 +4119,29 @@ EXPECT_FALSE(webView->page()->suspended()); } +TEST_P(WebViewTest, ClosingPageIsSuspended) { + WebViewImpl* webView = m_webViewHelper.initialize(); + Page* page = m_webViewHelper.webView()->page(); + EXPECT_FALSE(page->suspended()); + + webView->setOpenedByDOM(); + + LocalFrame* mainFrame = toLocalFrame(page->mainFrame()); + EXPECT_FALSE(mainFrame->domWindow()->closed()); + + mainFrame->domWindow()->close(nullptr); + // The window should be marked closed... + EXPECT_TRUE(mainFrame->domWindow()->closed()); + // EXPECT_TRUE(page->isClosing()); + // ...but not yet detached. + EXPECT_TRUE(mainFrame->host()); + + { + ScopedPageSuspender suspender; + EXPECT_TRUE(page->suspended()); + } +} + TEST_P(WebViewTest, ForceAndResetViewport) { URLTestHelpers::registerMockedURLFromBaseURL( WebString::fromUTF8(m_baseURL.c_str()),
diff --git a/third_party/WebKit/public/platform/DEPS b/third_party/WebKit/public/platform/DEPS index eeefc68..76b11842 100644 --- a/third_party/WebKit/public/platform/DEPS +++ b/third_party/WebKit/public/platform/DEPS
@@ -19,6 +19,7 @@ "+platform/weborigin", "+public/platform", "-public/web", + "+services/service_manager/public/interfaces", "+third_party/skia", "+url", "-web",
diff --git a/third_party/WebKit/public/platform/Platform.h b/third_party/WebKit/public/platform/Platform.h index 0aae60e68..fce5664 100644 --- a/third_party/WebKit/public/platform/Platform.h +++ b/third_party/WebKit/public/platform/Platform.h
@@ -56,6 +56,7 @@ #include "base/metrics/user_metrics_action.h" #include "cc/resources/shared_bitmap.h" #include "cc/surfaces/frame_sink_id.h" +#include "mojo/public/cpp/system/message_pipe.h" namespace gpu { class GpuMemoryBufferManager; @@ -590,6 +591,15 @@ virtual InterfaceProvider* interfaceProvider(); + // Sets up a connection to the ServiceManager by binding |remoteHandle| to a + // remote implementation of + // //service_manager/public/interfaces/connector.mojom. Using this connection + // the caller can then request connections to other services. + // NOTE: This handle is not strongly typed because neither the Blink nor + // Chromium types generated from connector.mojom should leak across the + // Blink-Chromium boundary. + virtual void bindServiceConnector(mojo::ScopedMessagePipeHandle remoteHandle); + // Platform events ----------------------------------------------------- // Device Orientation, Device Motion, Device Light, Battery, Gamepad.
diff --git a/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h b/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h index 1e17c33..b3cc9b16 100644 --- a/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h +++ b/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h
@@ -32,11 +32,15 @@ // Registers a response and the file to be served when the specified URL // is loaded. If no file is specified then the response content will be empty. + // unregisterURL() should be called for each test entry before registering + // another response for the same URL from another test. virtual void registerURL(const WebURL&, const WebURLResponse&, const WebString& filePath) = 0; // Registers an error to be served when the specified URL is requested. + // unregisterURL() should be called for each test entry before registering + // another response for the same URL from another test. virtual void registerErrorURL(const WebURL&, const WebURLResponse&, const WebURLError&) = 0;
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath index 28d95f64..a34a9e6 100644 --- a/tools/android/eclipse/.classpath +++ b/tools/android/eclipse/.classpath
@@ -68,6 +68,7 @@ <classpathentry kind="src" path="components/sync/android/java/src"/> <classpathentry kind="src" path="components/sync/android/javatests/src"/> <classpathentry kind="src" path="components/test/android/browsertests_apk/src"/> + <classpathentry kind="src" path="components/url_formatter/android/java/src"/> <classpathentry kind="src" path="components/variations/android/java/src"/> <classpathentry kind="src" path="components/web_contents_delegate_android/android/java/src"/> <classpathentry kind="src" path="content/public/android/java/src"/> @@ -98,6 +99,7 @@ <classpathentry kind="src" path="remoting/android/apk/src"/> <classpathentry kind="src" path="remoting/android/java/src"/> <classpathentry kind="src" path="remoting/android/javatests/src"/> + <classpathentry kind="src" path="services/service_manager/public/java/src"/> <classpathentry kind="src" path="testing/android/appurify_support/java/src"/> <classpathentry kind="src" path="testing/android/broker/java/src"/> <classpathentry kind="src" path="testing/android/driver/java/src"/> @@ -254,6 +256,7 @@ <classpathentry kind="lib" path="out/Debug/lib.java/components/invalidation/impl/proto_java.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/components/navigation_interception/android/navigation_interception_java.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/components/policy/android/policy_java.jar"/> + <classpathentry kind="lib" path="out/Debug/lib.java/components/payments/payment_request_java.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/components/precache/android/precache_java.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/components/safe_json/android/safe_json_java.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/components/service_tab_launcher/service_tab_launcher_java.jar"/> @@ -274,6 +277,7 @@ <classpathentry kind="lib" path="out/Debug/lib.java/mojo/public/java/system.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/net/android/net_java.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/printing/printing_java.jar"/> + <classpathentry kind="lib" path="out/Debug/lib.java/services/service_manager/public/interfaces/interfaces_java.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/sync/android/sync_java.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/sync/test_support_proto_java.jar"/> <classpathentry kind="lib" path="out/Debug/lib.java/third_party/android_data_chart/android_data_chart_java.jar"/>
diff --git a/tools/chrome_proxy/webdriver/common.py b/tools/chrome_proxy/webdriver/common.py index 381ba75ba..695bb563 100644 --- a/tools/chrome_proxy/webdriver/common.py +++ b/tools/chrome_proxy/webdriver/common.py
@@ -527,15 +527,22 @@ http_response.response_headers['via']) @staticmethod - def RunAllTests(): + def RunAllTests(run_all_tests=False): """A simple helper method to run all tests using unittest.main(). + + Args: + run_all_tests: If True, all tests in the directory will be run, Otherwise + only the tests in the file given on the command line will be run. """ flags = ParseFlags() logger = GetLogger() logger.debug('Command line args: %s', str(sys.argv)) logger.info('sys.argv parsed to %s', str(flags)) - # The unittest library uses sys.argv itself and is easily confused by our - # command line options. Pass it a simpler argv instead, while working in the - # unittest command line args functionality. - unittest.main(argv=[sys.argv[0]], verbosity=2, failfast=flags.failfast, - catchbreak=flags.catch, buffer=(not flags.disable_buffer)) + if flags.catch: + unittest.installHandler() + pattern = '*.py' if run_all_tests else os.path.basename(sys.argv[0]) + loader = unittest.TestLoader() + tests = loader.discover(os.path.dirname(__file__), pattern=pattern) + testRunner = unittest.runner.TextTestRunner(verbosity=2, + failfast=flags.failfast, buffer=(not flags.disable_buffer)) + testRunner.run(tests)
diff --git a/services/device/public/cpp/BUILD.gn b/tools/chrome_proxy/webdriver/run_all_tests.py similarity index 61% copy from services/device/public/cpp/BUILD.gn copy to tools/chrome_proxy/webdriver/run_all_tests.py index f341a18b..604b4764 100644 --- a/services/device/public/cpp/BUILD.gn +++ b/tools/chrome_proxy/webdriver/run_all_tests.py
@@ -2,9 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -source_set("cpp") { - sources = [ - "constants.cc", - "constants.h", - ] -} +import common + +if __name__ == "__main__": + common.IntegrationTest.RunAllTests(run_all_tests=True)
diff --git a/tools/clang-format-js b/tools/clang-format-js new file mode 100755 index 0000000..d09925d --- /dev/null +++ b/tools/clang-format-js
@@ -0,0 +1,30 @@ +#!/bin/bash +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [[ -z "${@}" ]]; then + echo >&2 "Usage: `basename $0` <paths_to_clang_format...>"; + exit 1; +fi + +which clang-format >/dev/null 2>&1; +if [[ $? -ne 0 ]]; then + echo >&2 "Sorry, but you need \`clang-format\` on your \$PATH to run this script"; + exit 1; +fi + +for arg in "${@}"; do + dir=`readlink -f "${arg}"` + while dir=`dirname ${dir}`; do + if [[ -f "${dir}/.clang-format" ]]; then + echo "Using style from: ${dir}/.clang-format"; + break; + elif [[ "${dir}" == "/" ]]; then + echo >&2 "No .clang-format file found. Make one at or above ${arg}"; + exit 1; + fi + done +done + +find "${@}" -type f -iname '*.js' | xargs clang-format -i -style=file;
diff --git a/tools/clang/rewrite_to_chrome_style/CMakeLists.txt b/tools/clang/rewrite_to_chrome_style/CMakeLists.txt index 8fa96ef..1935e239 100644 --- a/tools/clang/rewrite_to_chrome_style/CMakeLists.txt +++ b/tools/clang/rewrite_to_chrome_style/CMakeLists.txt
@@ -7,6 +7,7 @@ ) add_llvm_executable(rewrite_to_chrome_style + EditTracker.cpp RewriteToChromeStyle.cpp )
diff --git a/tools/clang/rewrite_to_chrome_style/EditTracker.cpp b/tools/clang/rewrite_to_chrome_style/EditTracker.cpp new file mode 100644 index 0000000..cd8228e --- /dev/null +++ b/tools/clang/rewrite_to_chrome_style/EditTracker.cpp
@@ -0,0 +1,41 @@ +// Copyright 2016 The Chromium 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 "EditTracker.h" + +#include <assert.h> +#include <stdio.h> +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +void EditTracker::Add(const clang::SourceManager& source_manager, + clang::SourceLocation location, + llvm::StringRef original_text, + llvm::StringRef new_text) { + llvm::StringRef filename; + for (int i = 0; i < 10; i++) { + filename = source_manager.getFilename(location); + if (!filename.empty() || !location.isMacroID()) + break; + // Otherwise, no filename and the SourceLocation is a macro ID. Look one + // level up the stack... + location = source_manager.getImmediateMacroCallerLoc(location); + } + assert(!filename.empty() && "Can't track edit with no filename!"); + auto result = tracked_edits_.try_emplace(original_text); + if (result.second) { + result.first->getValue().new_text = new_text; + } + result.first->getValue().filenames.try_emplace(filename); +} + +void EditTracker::SerializeTo(llvm::StringRef tag, + llvm::raw_ostream& output) const { + for (const auto& edit : tracked_edits_) { + for (const auto& filename : edit.getValue().filenames) { + output << filename.getKey() << ":" << tag << ":" << edit.getKey() << ":" + << edit.getValue().new_text << "\n"; + } + } +}
diff --git a/tools/clang/rewrite_to_chrome_style/EditTracker.h b/tools/clang/rewrite_to_chrome_style/EditTracker.h new file mode 100644 index 0000000..ef5e301 --- /dev/null +++ b/tools/clang/rewrite_to_chrome_style/EditTracker.h
@@ -0,0 +1,49 @@ +// Copyright 2016 The Chromium 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 TOOLS_CLANG_REWRITE_TO_CHROME_STYLE_EDIT_TRACKER_H_ +#define TOOLS_CLANG_REWRITE_TO_CHROME_STYLE_EDIT_TRACKER_H_ + +#include <map> + +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" + +namespace llvm { +class raw_ostream; +} // namespace llvm + +struct EditInfo { + std::string new_text; + llvm::StringSet<> filenames; +}; + +// Simple class that tracks the edits made by path. Used to dump the databaes +// used by the Blink rebase helper. +class EditTracker { + public: + EditTracker() = default; + + void Add(const clang::SourceManager& source_manager, + clang::SourceLocation location, + llvm::StringRef original_text, + llvm::StringRef new_text); + + // Serializes the tracked edits to |output|. Emits: + // <filename>:<tag>:<original text>:<new text> + // for each distinct filename for each tracked edit. + void SerializeTo(llvm::StringRef tag, llvm::raw_ostream& output) const; + + private: + EditTracker(const EditTracker&) = delete; + EditTracker& operator=(const EditTracker&) = delete; + + // The string key is the original text. + llvm::StringMap<EditInfo> tracked_edits_; +}; + +#endif // #define TOOLS_CLANG_REWRITE_TO_CHROME_STYLE_EDIT_TRACKER_H_
diff --git a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp index 3e18a280..3374ea29 100644 --- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp +++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
@@ -14,10 +14,9 @@ #include <assert.h> #include <algorithm> -#include <fstream> #include <memory> +#include <set> #include <string> -#include <unordered_map> #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -33,12 +32,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/TargetSelect.h" -#if defined(_WIN32) -#include <windows.h> -#else -#include <sys/file.h> -#include <unistd.h> -#endif +#include "EditTracker.h" using namespace clang::ast_matchers; using clang::tooling::CommonOptionsParser; @@ -694,19 +688,17 @@ clang::SourceLocation loc = TargetNodeTraits<TargetNode>::GetLoc(GetTargetNode(result)); + edit_tracker_.Add(*result.SourceManager, loc, old_name, new_name); + clang::CharSourceRange range = clang::CharSourceRange::getTokenRange(loc); replacements_->emplace(*result.SourceManager, range, new_name); - replacement_names_.emplace(old_name.str(), std::move(new_name)); } - const std::unordered_map<std::string, std::string>& replacement_names() - const { - return replacement_names_; - } + const EditTracker& edit_tracker() const { return edit_tracker_; } private: std::set<Replacement>* const replacements_; - std::unordered_map<std::string, std::string> replacement_names_; + EditTracker edit_tracker_; }; template <typename DeclNode, typename TargetNode> @@ -1287,38 +1279,15 @@ if (result != 0) return result; -#if defined(_WIN32) - HANDLE lockfd = CreateFile("rewrite-sym.lock", GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - OVERLAPPED overlapped = {}; - LockFileEx(lockfd, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &overlapped); -#else - int lockfd = open("rewrite-sym.lock", O_RDWR | O_CREAT, 0666); - while (flock(lockfd, LOCK_EX)) { // :D - } -#endif - - std::ofstream replacement_db_file("rewrite-sym.txt", - std::ios_base::out | std::ios_base::app); - for (const auto& p : field_decl_rewriter.replacement_names()) - replacement_db_file << "var:" << p.first << ":" << p.second << "\n"; - for (const auto& p : var_decl_rewriter.replacement_names()) - replacement_db_file << "var:" << p.first << ":" << p.second << "\n"; - for (const auto& p : enum_member_decl_rewriter.replacement_names()) - replacement_db_file << "enu:" << p.first << ":" << p.second << "\n"; - for (const auto& p : function_decl_rewriter.replacement_names()) - replacement_db_file << "fun:" << p.first << ":" << p.second << "\n"; - for (const auto& p : method_decl_rewriter.replacement_names()) - replacement_db_file << "fun:" << p.first << ":" << p.second << "\n"; - replacement_db_file.close(); - -#if defined(_WIN32) - UnlockFileEx(lockfd, 0, 1, 0, &overlapped); - CloseHandle(lockfd); -#else - flock(lockfd, LOCK_UN); - close(lockfd); -#endif + // Supplemental data for the Blink rename rebase helper. + // TODO(dcheng): There's a lot of match rewriters missing from this list. + llvm::outs() << "==== BEGIN TRACKED EDITS ====\n"; + field_decl_rewriter.edit_tracker().SerializeTo("var", llvm::outs()); + var_decl_rewriter.edit_tracker().SerializeTo("var", llvm::outs()); + enum_member_decl_rewriter.edit_tracker().SerializeTo("enu", llvm::outs()); + function_decl_rewriter.edit_tracker().SerializeTo("fun", llvm::outs()); + method_decl_rewriter.edit_tracker().SerializeTo("fun", llvm::outs()); + llvm::outs() << "==== END TRACKED EDITS ====\n"; // Serialization format is documented in tools/clang/scripts/run_tool.py llvm::outs() << "==== BEGIN EDITS ====\n";
diff --git a/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc b/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc index 327d3b1..ddc092e 100644 --- a/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc +++ b/tools/clang/rewrite_to_chrome_style/tests/methods-expected.cc
@@ -50,10 +50,10 @@ // These are special functions that we don't rename so that range-based // for loops and STL things work. - MyIterator begin() {} - my_iterator end() {} - my_iterator rbegin() {} - MyIterator rend() {} + MyIterator begin() { return {}; } + my_iterator end() { return {}; } + my_iterator rbegin() { return {}; } + MyIterator rend() { return {}; } // The trace() method is used by Oilpan, but we plan to tweak the Oilpan's // clang plugin, so that it recognizes the new method name. void Trace() {} @@ -65,8 +65,8 @@ class Other { // Static begin/end/trace don't count, and should be renamed. - static MyIterator Begin() {} - static my_iterator End() {} + static MyIterator Begin() { return {}; } + static my_iterator End() { return {}; } static void Trace() {} static void Lock() {} };
diff --git a/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc b/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc index afb5162..a5360ac 100644 --- a/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc +++ b/tools/clang/rewrite_to_chrome_style/tests/methods-original.cc
@@ -54,10 +54,10 @@ // These are special functions that we don't rename so that range-based // for loops and STL things work. - MyIterator begin() {} - my_iterator end() {} - my_iterator rbegin() {} - MyIterator rend() {} + MyIterator begin() { return {}; } + my_iterator end() { return {}; } + my_iterator rbegin() { return {}; } + MyIterator rend() { return {}; } // The trace() method is used by Oilpan, but we plan to tweak the Oilpan's // clang plugin, so that it recognizes the new method name. void trace() {} @@ -69,8 +69,8 @@ class Other { // Static begin/end/trace don't count, and should be renamed. - static MyIterator begin() {} - static my_iterator end() {} + static MyIterator begin() { return {}; } + static my_iterator end() { return {}; } static void trace() {} static void lock() {} };
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py index a8fe279..31c3aef 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py
@@ -34,7 +34,7 @@ CLANG_REVISION = 'HEAD' # This is incremented when pushing a new build of Clang at the same revision. -CLANG_SUB_REVISION=1 +CLANG_SUB_REVISION=2 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 58302431..425567b 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -1752,6 +1752,8 @@ }, 'ndk_next': { + # GCC is unsupported starting in r13. + 'mixins': ['clang'], 'gn_args': 'android_ndk_version="r13b" android_ndk_major_version=13', },
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 817f4bc..9ea96f3 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -28685,6 +28685,9 @@ <histogram name="Navigation.Start.RendererBrowserDifference.Negative" units="ms"> + <obsolete> + Data collected and posted on issue 675833. + </obsolete> <owner>csharrison@chromium.org</owner> <summary> The difference between Now() in the renderer and browser_navigation_start @@ -28698,6 +28701,9 @@ <histogram name="Navigation.Start.RendererBrowserDifference.Positive" units="ms"> + <obsolete> + Data collected and posted on issue 675833. + </obsolete> <owner>csharrison@chromium.org</owner> <summary> The difference between Now() in the renderer and browser_navigation_start @@ -49602,6 +49608,9 @@ </histogram> <histogram name="Prerender.PerceivedPLTMatchedComplete" units="ms"> + <obsolete> + Deprecated August 2016 + </obsolete> <owner>pasko@chromium.org</owner> <summary> Time from when a user navigates to a page to when it loads. Since the pages @@ -83710,6 +83719,9 @@ label="Enables users to set weak PINs for the lock screen PIN."/> <int value="357" label="Enables domain admins to sets a device policy wallpaper."/> + <int value="358" + label="Enable the creation of roaming copies for Chrome profiles."/> + <int value="359" label="Set the roaming profile directory."/> </enum> <enum name="EnterprisePolicyInvalidations" type="int"> @@ -88069,6 +88081,7 @@ <int value="1721" label="V8PeriodicWave_Constructor"/> <int value="1722" label="V8StereoPannerNode_Constructor"/> <int value="1723" label="V8WaveShaperNode_Constructor"/> + <int value="1724" label="V8Headers_GetAll_Method"/> </enum> <enum name="FetchRequestMode" type="int">
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml index 927580b..b842ac5 100644 --- a/tools/metrics/rappor/rappor.xml +++ b/tools/metrics/rappor/rappor.xml
@@ -910,16 +910,65 @@ </rappor-metric> <rappor-metric + name="Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin2" + type="UMA_RAPPOR_TYPE"> + <owner>nzolghadr@chromium.org</owner> + <owner>tdresser@chromium.org</owner> + <summary> + The eTLD+1 of the website visited, along with the time between initial + creation of a touch event and the start of the frame swap on the GPU service + caused by the generated ScrollUpdate gesture event if that ScrollUpdate is + the first such event in a given scroll gesture event sequence. If no swap + was induced by the event, no recording is made. + </summary> + <string-field name="Domain"> + <summary> + The domain+registry of the URL. + </summary> + </string-field> + <uint64-field name="Latency"> + <summary> + Latency in microseconds representing first + Touch.TimeToScrollUpdateSwapBegin. + </summary> + </uint64-field> +</rappor-metric> + +<rappor-metric + name="Event.Latency.ScrollBegin.Wheel.TimeToScrollUpdateSwapBegin2" + type="UMA_RAPPOR_TYPE"> + <owner>nzolghadr@chromium.org</owner> + <owner>tdresser@chromium.org</owner> + <summary> + The eTLD+1 of the website visited, along with the time between initial + creation of a wheel event and the start of the frame swap on the GPU service + caused by the generated ScrollUpdate gesture event if that ScrollUpdate is + the first such event in a given scroll gesture event sequence. If no swap + was induced by the event, no recording is made. + </summary> + <string-field name="Domain"> + <summary> + The domain+registry of the URL. + </summary> + </string-field> + <uint64-field name="Latency"> + <summary> + Latency in microseconds representing first + Wheel.TimeToScrollUpdateSwapBegin. + </summary> + </uint64-field> +</rappor-metric> + +<rappor-metric name="Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2" type="UMA_RAPPOR_TYPE"> <owner>nzolghadr@chromium.org</owner> <owner>tdresser@chromium.org</owner> <summary> - The eTLD+1 of the website visited, along with a the time between the initial + The eTLD+1 of the website visited, along with the time between the initial creation of a touch event and the start of the frame swap on the GPU service - caused by the generated ScrollUpdate gesture event if that ScrollUpdate is - the first such event in a given scroll gesture event sequence. If no swap - was induced by the event, no recording is made. + caused by the generated ScrollUpdate gesture event. If no swap was induced + by the event, no recording is made. </summary> <string-field name="Domain"> <summary>
diff --git a/tools/perf/benchmarks/speedometer.py b/tools/perf/benchmarks/speedometer.py index b6d82cf..282b747 100644 --- a/tools/perf/benchmarks/speedometer.py +++ b/tools/perf/benchmarks/speedometer.py
@@ -116,3 +116,13 @@ @classmethod def Name(cls): return 'speedometer-ignition' + + +class SpeedometerTurbo(Speedometer): + def SetExtraBrowserOptions(self, options): + super(SpeedometerTurbo, self).SetExtraBrowserOptions(options) + v8_helper.EnableTurbo(options) + + @classmethod + def Name(cls): + return 'speedometer-turbo'
diff --git a/tools/perf/benchmarks/v8_helper.py b/tools/perf/benchmarks/v8_helper.py index 19ecab3..6eb8d4d 100644 --- a/tools/perf/benchmarks/v8_helper.py +++ b/tools/perf/benchmarks/v8_helper.py
@@ -9,6 +9,11 @@ AppendJSFlags(options, '--ignition-staging') +def EnableTurbo(options): + AppendJSFlags(options, '--ignition-staging') + AppendJSFlags(options, '--turbo') + + def AppendJSFlags(options, js_flags): existing_js_flags = '' # There should be only one occurence of --js-flags in the browser flags. When
diff --git a/tools/perf/generate_perf_json.py b/tools/perf/generate_perf_json.py index 29783f0..4ccd4f4 100755 --- a/tools/perf/generate_perf_json.py +++ b/tools/perf/generate_perf_json.py
@@ -244,7 +244,9 @@ 'device_ids': [ 'build132-m1', 'build133-m1', 'build134-m1', 'build135-m1', 'build136-m1' - ] + ], + 'perf_tests': [ + ('media_perftests', 2)] } ]) waterfall = add_tester( @@ -259,7 +261,8 @@ ], 'perf_tests': [ ('load_library_perf_tests', 2), - ('performance_browser_tests', 2)] + ('performance_browser_tests', 2), + ('media_perftests', 3)] } ]) waterfall = add_tester( @@ -275,7 +278,8 @@ ], 'perf_tests': [ ('load_library_perf_tests', 2), - ('performance_browser_tests', 2)] + ('performance_browser_tests', 2), + ('media_perftests', 3)] } ]) waterfall = add_tester( @@ -308,7 +312,8 @@ 'perf_tests': [ ('angle_perftests', 2), ('load_library_perf_tests', 2), - ('performance_browser_tests', 2)] + ('performance_browser_tests', 2), + ('media_perftests', 3)] } ]) waterfall = add_tester( @@ -338,7 +343,8 @@ 'perf_tests': [ ('angle_perftests', 2), ('load_library_perf_tests', 2), - ('performance_browser_tests', 2)] + ('performance_browser_tests', 2), + ('media_perftests', 3)] } ]) @@ -352,7 +358,9 @@ 'device_ids': [ 'build102-b1', 'build103-b1', 'build104-b1', 'build105-b1', 'build106-b1' - ] + ], + 'perf_tests': [ + ('media_perftests', 2)] } ]) waterfall = add_tester( @@ -419,7 +427,8 @@ 'perf_tests': [ ('cc_perftests', 2), ('load_library_perf_tests', 2), - ('tracing_perftests', 2)] + ('tracing_perftests', 2), + ('media_perftests', 3)] } ])
diff --git a/tools/perf/page_sets/system_health/background_stories.py b/tools/perf/page_sets/system_health/background_stories.py index 31df314..a0eb93f 100644 --- a/tools/perf/page_sets/system_health/background_stories.py +++ b/tools/perf/page_sets/system_health/background_stories.py
@@ -78,7 +78,7 @@ SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY -@decorators.Disabled('android') # crbug.com.com/664505 +@decorators.Disabled('android') # crbug.com/676336 class BackgroundGmailMobileStory(LoadGmailMobileStory): NAME = 'background:tools:gmail' SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py index 04862001..a5c8cca 100644 --- a/tools/perf/page_sets/system_health/browsing_stories.py +++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -146,6 +146,7 @@ ITEM_SELECTOR = '.athing .title > a' +@decorators.Disabled('android') # crbug.com/676315 class NytimesMobileStory(_NewsBrowsingStory): """The third top website in http://www.alexa.com/topsites/category/News""" NAME = 'browse:news:nytimes' @@ -198,7 +199,7 @@ @decorators.Disabled('win', # crbug.com/662971 - 'mac') # crbug.com/664661, crbug.com/663025 + 'mac') # crbug.com/663025 class TwitterDesktopStory(_NewsBrowsingStory): NAME = 'browse:social:twitter' URL = 'https://www.twitter.com/nasa'
diff --git a/tools/perf/page_sets/system_health/loading_stories.py b/tools/perf/page_sets/system_health/loading_stories.py index eb0c54e..2b0af06 100644 --- a/tools/perf/page_sets/system_health/loading_stories.py +++ b/tools/perf/page_sets/system_health/loading_stories.py
@@ -309,7 +309,7 @@ 'document.getElementById("loading").style.display === "none"') -@decorators.Disabled('android') # crbug.com.com/664505 +@decorators.Disabled('android') # crbug.com/657433 class LoadGmailMobileStory(_LoadGmailBaseStory): SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
diff --git a/tools/perf/page_sets/system_health/long_running_stories.py b/tools/perf/page_sets/system_health/long_running_stories.py index 2eed0ef..916e75c0 100644 --- a/tools/perf/page_sets/system_health/long_running_stories.py +++ b/tools/perf/page_sets/system_health/long_running_stories.py
@@ -83,7 +83,7 @@ 'document.getElementById("loading").style.display === "none"') -@decorators.Disabled('android') # crbug.com.com/664505 +@decorators.Disabled('android') # crbug.com/657433 class LongRunningGmailMobileForegroundStory(_LongRunningGmailMobileBase): NAME = 'long_running:tools:gmail-foreground' @@ -92,8 +92,8 @@ NAME = 'long_running:tools:gmail-foreground' -@decorators.Disabled('android-webview' # Weview does not have tabs. - , 'android') # crbug.com/664505 +@decorators.Disabled('android-webview', # Weview does not have tabs. + 'android') # crbug.com/657433 class LongRunningGmailMobileBackgroundStory(_LongRunningGmailMobileBase): BACKGROUND = True NAME = 'long_running:tools:gmail-background'
diff --git a/tools/perf/page_sets/system_health/searching_stories.py b/tools/perf/page_sets/system_health/searching_stories.py index 898aacf0..329a0cb 100644 --- a/tools/perf/page_sets/system_health/searching_stories.py +++ b/tools/perf/page_sets/system_health/searching_stories.py
@@ -27,18 +27,7 @@ # Scroll to the Wikipedia result. action_runner.WaitForElement(selector=self._RESULT_SELECTOR) action_runner.Wait(1) - # TODO(petrcermak): Turn this into a proper Telemetry API (ScrollToElement). - # TODO(catapult:#3028): Fix interpolation of JavaScript values. - result_visible_expression = (''' - (function() { - var resultElem = document.querySelector(\'%s\'); - var boundingRect = resultElem.getBoundingClientRect(); - return boundingRect.bottom >= window.innerHeight - })()''' % self._RESULT_SELECTOR) - while action_runner.EvaluateJavaScript(result_visible_expression): - action_runner.RepeatableBrowserDrivenScroll(y_scroll_distance_ratio=0.75, - prevent_fling=False) - action_runner.Wait(0.2) + action_runner.ScrollPageToElement(selector=self._RESULT_SELECTOR) # Click on the Wikipedia result. action_runner.Wait(1)
diff --git a/ui/android/window_android.cc b/ui/android/window_android.cc index 79110f33..770fda4 100644 --- a/ui/android/window_android.cc +++ b/ui/android/window_android.cc
@@ -33,6 +33,7 @@ explicit WindowBeginFrameSource(WindowAndroid* window) : window_(window), observer_count_(0), + next_sequence_number_(cc::BeginFrameArgs::kStartingFrameNumber), in_on_vsync_(false) {} ~WindowBeginFrameSource() override {} @@ -43,14 +44,14 @@ size_t remaining_frames) override {} bool IsThrottled() const override { return true; } - void OnVSync(base::TimeTicks frame_time, - base::TimeDelta vsync_period); + void OnVSync(base::TimeTicks frame_time, base::TimeDelta vsync_period); private: WindowAndroid* const window_; base::ObserverList<cc::BeginFrameObserver> observers_; int observer_count_; cc::BeginFrameArgs last_begin_frame_args_; + uint64_t next_sequence_number_; bool in_on_vsync_; }; @@ -70,6 +71,9 @@ cc::BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); if (!last_args.IsValid() || last_args.frame_time < last_begin_frame_args_.frame_time) { + DCHECK(last_args.sequence_number < + last_begin_frame_args_.sequence_number || + last_args.source_id != last_begin_frame_args_.source_id); last_begin_frame_args_.type = cc::BeginFrameArgs::MISSED; // TODO(crbug.com/602485): A deadline doesn't make too much sense // for a missed BeginFrame (the intention rather is 'immediately'), @@ -98,10 +102,11 @@ base::TimeDelta vsync_period) { // frame time is in the past, so give the next vsync period as the deadline. base::TimeTicks deadline = frame_time + vsync_period; - last_begin_frame_args_ = - cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, - vsync_period, cc::BeginFrameArgs::NORMAL); + last_begin_frame_args_ = cc::BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_, frame_time, + deadline, vsync_period, cc::BeginFrameArgs::NORMAL); DCHECK(last_begin_frame_args_.IsValid()); + next_sequence_number_++; // We support adding/removing observers during observer iteration through // base::ObserverList. We also prevent observers that are added during
diff --git a/ui/aura/mus/property_converter.cc b/ui/aura/mus/property_converter.cc index 875ec3d..b02fca4 100644 --- a/ui/aura/mus/property_converter.cc +++ b/ui/aura/mus/property_converter.cc
@@ -32,6 +32,8 @@ // Add known aura properties with associated mus properties. RegisterProperty(client::kAlwaysOnTopKey, ui::mojom::WindowManager::kAlwaysOnTop_Property); + RegisterProperty(client::kAppIconKey, + ui::mojom::WindowManager::kAppIcon_Property); RegisterProperty(client::kAppIdKey, ui::mojom::WindowManager::kAppID_Property); RegisterProperty(client::kNameKey, ui::mojom::WindowManager::kName_Property); @@ -45,6 +47,8 @@ ui::mojom::WindowManager::kShowState_Property); RegisterProperty(client::kTitleKey, ui::mojom::WindowManager::kWindowTitle_Property); + RegisterProperty(client::kWindowIconKey, + ui::mojom::WindowManager::kWindowIcon_Property); } PropertyConverter::~PropertyConverter() {}
diff --git a/ui/base/clipboard/clipboard_mac.h b/ui/base/clipboard/clipboard_mac.h index 2e264cb..76701ff0 100644 --- a/ui/base/clipboard/clipboard_mac.h +++ b/ui/base/clipboard/clipboard_mac.h
@@ -21,6 +21,8 @@ private: FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageRetina); FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageNonRetina); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, EmptyImage); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, PDFImage); friend class Clipboard; ClipboardMac();
diff --git a/ui/base/clipboard/clipboard_mac.mm b/ui/base/clipboard/clipboard_mac.mm index fcc4a3a..ce4bada 100644 --- a/ui/base/clipboard/clipboard_mac.mm +++ b/ui/base/clipboard/clipboard_mac.mm
@@ -324,17 +324,27 @@ } @catch (id exception) { } - if (image.get()) { - if ([[image representations] count] == 1u) { - NSImageRep* rep = [[image representations] objectAtIndex:0]; + if (!image) + return SkBitmap(); + if ([[image representations] count] == 0u) + return SkBitmap(); + + // This logic prevents loss of pixels from retina images, where size != pixel + // size. In an ideal world, the concept of "retina-ness" would be plumbed all + // the way through to the web, but the clipboard API doesn't support the + // additional metainformation. + if ([[image representations] count] == 1u) { + NSImageRep* rep = [[image representations] objectAtIndex:0]; + NSInteger width = [rep pixelsWide]; + NSInteger height = [rep pixelsHigh]; + if (width != 0 && height != 0) { return skia::NSImageRepToSkBitmapWithColorSpace( - rep, NSMakeSize([rep pixelsWide], [rep pixelsHigh]), - /*is_opaque=*/false, base::mac::GetSystemColorSpace()); + rep, NSMakeSize(width, height), /*is_opaque=*/false, + base::mac::GetSystemColorSpace()); } - return skia::NSImageToSkBitmapWithColorSpace( - image.get(), /*is_opaque=*/false, base::mac::GetSystemColorSpace()); } - return SkBitmap(); + return skia::NSImageToSkBitmapWithColorSpace( + image.get(), /*is_opaque=*/false, base::mac::GetSystemColorSpace()); } SkBitmap ClipboardMac::ReadImage(ClipboardType type) const {
diff --git a/ui/base/clipboard/clipboard_mac_unittest.mm b/ui/base/clipboard/clipboard_mac_unittest.mm index 6a5577cf5..475db13 100644 --- a/ui/base/clipboard/clipboard_mac_unittest.mm +++ b/ui/base/clipboard/clipboard_mac_unittest.mm
@@ -4,6 +4,8 @@ #import "ui/base/clipboard/clipboard_mac.h" +#import <AppKit/AppKit.h> + #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsobject.h" #include "base/memory/free_deleter.h" @@ -13,6 +15,16 @@ #include "ui/base/clipboard/clipboard_types.h" #include "ui/base/clipboard/clipboard_util_mac.h" +@interface RedView : NSView +@end +@implementation RedView +- (void)drawRect:(NSRect)dirtyRect { + [[NSColor redColor] setFill]; + NSRectFill(dirtyRect); + [super drawRect:dirtyRect]; +} +@end + namespace ui { class ClipboardMacTest : public PlatformTest { @@ -78,4 +90,41 @@ EXPECT_EQ(height, bitmap.height()); } +TEST_F(ClipboardMacTest, EmptyImage) { + base::scoped_nsobject<NSImage> image([[NSImage alloc] init]); + scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard; + [pasteboard->get() writeObjects:@[ image.get() ]]; + + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); + ui::ClipboardMac* clipboard_mac = static_cast<ui::ClipboardMac*>(clipboard); + + SkBitmap bitmap = clipboard_mac->ReadImage(ui::CLIPBOARD_TYPE_COPY_PASTE, + pasteboard->get()); + EXPECT_EQ(0, bitmap.width()); + EXPECT_EQ(0, bitmap.height()); +} + +TEST_F(ClipboardMacTest, PDFImage) { + int32_t width = 99; + int32_t height = 101; + NSRect frame = NSMakeRect(0, 0, width, height); + + // This seems like a round-about way of getting a NSPDFImageRep to shove into + // an NSPasteboard. However, I haven't found any other way of generating a + // "PDF" image that makes NSPasteboard happy. + base::scoped_nsobject<NSView> v([[RedView alloc] initWithFrame:frame]); + NSData* data = [v dataWithPDFInsideRect:frame]; + + scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard; + [pasteboard->get() setData:data forType:NSPasteboardTypePDF]; + + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); + ui::ClipboardMac* clipboard_mac = static_cast<ui::ClipboardMac*>(clipboard); + + SkBitmap bitmap = clipboard_mac->ReadImage(ui::CLIPBOARD_TYPE_COPY_PASTE, + pasteboard->get()); + EXPECT_EQ(width, bitmap.width()); + EXPECT_EQ(height, bitmap.height()); +} + } // namespace ui
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn index 28e335d..abb3815 100644 --- a/ui/ozone/platform/drm/BUILD.gn +++ b/ui/ozone/platform/drm/BUILD.gn
@@ -6,7 +6,7 @@ import("//ui/ozone/ozone.gni") declare_args() { - use_drm_atomic = false + use_drm_atomic = true } visibility = [ "//ui/ozone/*" ]
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc index 0401223..6a3b1b6 100644 --- a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc +++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
@@ -290,15 +290,10 @@ overlay_validator_->TestPageFlip(overlay_params_, ui::OverlayPlaneList()); ui::OverlayPlaneList plane_list = overlay_validator_->PrepareBuffersForPageFlip(plane_list_); -#if defined(USE_DRM_ATOMIC) - EXPECT_EQ(DRM_FORMAT_UYVY, - plane_list.back().buffer->GetFramebufferPixelFormat()); -#else - // If Atomic support is disabled, ensure we choose DRM_FORMAT_XRGB8888 as the - // optimal format even if other packed formats are supported by Primary. + // TODO(dcastagna): If Atomic support is enabled, a packed format (UYVY) might + // be the optimal one and should be preferred. EXPECT_EQ(DRM_FORMAT_XRGB8888, plane_list.back().buffer->GetFramebufferPixelFormat()); -#endif } TEST_F(DrmOverlayValidatorTest, OverlayPreferredFormat) {
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc index 1b91c7ec..9b62a695 100644 --- a/ui/ozone/platform/drm/gpu/drm_thread.cc +++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -90,7 +90,8 @@ void DrmThread::Init() { bool use_atomic = false; #if defined(USE_DRM_ATOMIC) - use_atomic = true; + use_atomic = base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableDrmAtomic); #endif device_manager_.reset(
diff --git a/ui/ozone/public/ozone_switches.cc b/ui/ozone/public/ozone_switches.cc index 0a9b3ab32..f7a772a5 100644 --- a/ui/ozone/public/ozone_switches.cc +++ b/ui/ozone/public/ozone_switches.cc
@@ -12,4 +12,7 @@ // Specify location for image dumps. const char kOzoneDumpFile[] = "ozone-dump-file"; +// Try to enable drm atomic. This works only with drm platform. +const char kEnableDrmAtomic[] = "enable-drm-atomic"; + } // namespace switches
diff --git a/ui/ozone/public/ozone_switches.h b/ui/ozone/public/ozone_switches.h index b062e67..e53912bf 100644 --- a/ui/ozone/public/ozone_switches.h +++ b/ui/ozone/public/ozone_switches.h
@@ -14,6 +14,8 @@ OZONE_BASE_EXPORT extern const char kOzoneDumpFile[]; +OZONE_BASE_EXPORT extern const char kEnableDrmAtomic[]; + } // namespace switches #endif // UI_OZONE_PUBLIC_OZONE_SWITCHES_H_
diff --git a/ui/views/cocoa/bridged_content_view.mm b/ui/views/cocoa/bridged_content_view.mm index b5d67a1..15ac350 100644 --- a/ui/views/cocoa/bridged_content_view.mm +++ b/ui/views/cocoa/bridged_content_view.mm
@@ -1412,11 +1412,15 @@ composition.underlines.push_back(ui::CompositionUnderline( 0, [text length], SK_ColorBLACK, false, SK_ColorTRANSPARENT)); textInputClient_->SetCompositionText(composition); + keyDownEvent_ = nil; // Handled. } - (void)unmarkText { - if (textInputClient_) - textInputClient_->ConfirmCompositionText(); + if (!textInputClient_) + return; + + textInputClient_->ConfirmCompositionText(); + keyDownEvent_ = nil; // Handled. } - (NSArray*)validAttributesForMarkedText {
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc index b27b90e4..5a20610c 100644 --- a/ui/views/controls/textfield/textfield_unittest.cc +++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -654,31 +654,36 @@ EXPECT_TRUE(menu->IsEnabledAt(7 /* SELECT ALL */)); } - void PressLeftMouseButton(int extra_flags) { - ui::MouseEvent click(ui::ET_MOUSE_PRESSED, mouse_position_, mouse_position_, - ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON | extra_flags); - textfield_->OnMousePressed(click); + void PressMouseButton(ui::EventFlags mouse_button_flags, int extra_flags) { + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, mouse_position_, mouse_position_, + ui::EventTimeForNow(), mouse_button_flags, + mouse_button_flags | extra_flags); + textfield_->OnMousePressed(press); } - void PressLeftMouseButton() { - PressLeftMouseButton(0); - } - - void ReleaseLeftMouseButton() { + void ReleaseMouseButton(ui::EventFlags mouse_button_flags) { ui::MouseEvent release(ui::ET_MOUSE_RELEASED, mouse_position_, mouse_position_, ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + mouse_button_flags, mouse_button_flags); textfield_->OnMouseReleased(release); } - void ClickLeftMouseButton(int extra_flags) { + void PressLeftMouseButton(int extra_flags = 0) { + PressMouseButton(ui::EF_LEFT_MOUSE_BUTTON, extra_flags); + } + + void ReleaseLeftMouseButton() { + ReleaseMouseButton(ui::EF_LEFT_MOUSE_BUTTON); + } + + void ClickLeftMouseButton(int extra_flags = 0) { PressLeftMouseButton(extra_flags); ReleaseLeftMouseButton(); } - void ClickLeftMouseButton() { - ClickLeftMouseButton(0); + void ClickRightMouseButton() { + PressMouseButton(ui::EF_RIGHT_MOUSE_BUTTON, 0); + ReleaseMouseButton(ui::EF_RIGHT_MOUSE_BUTTON); } void DragMouseTo(const gfx::Point& where) { @@ -1453,6 +1458,27 @@ EXPECT_STR_EQ("hello", textfield_->GetSelectedText()); } +// Tests text selection behavior on a right click. +TEST_F(TextfieldTest, SelectWordOnRightClick) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("hello world")); + + // Verify right clicking within the selection does not alter the selection. + textfield_->SelectRange(gfx::Range(1, 5)); + EXPECT_STR_EQ("ello", textfield_->GetSelectedText()); + MoveMouseTo(gfx::Point(GetCursorPositionX(3), 0)); + ClickRightMouseButton(); + EXPECT_STR_EQ("ello", textfield_->GetSelectedText()); + + // Verify right clicking outside the selection, selects the word under the + // cursor on platforms where this is expected. + MoveMouseTo(gfx::Point(GetCursorPositionX(8), 0)); + const char* expected_right_click = + PlatformStyle::kSelectWordOnRightClick ? "world" : "ello"; + ClickRightMouseButton(); + EXPECT_STR_EQ(expected_right_click, textfield_->GetSelectedText()); +} + TEST_F(TextfieldTest, DragToSelect) { InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello world"));
diff --git a/ui/views/selection_controller.cc b/ui/views/selection_controller.cc index 38ba6ed..4692c9c 100644 --- a/ui/views/selection_controller.cc +++ b/ui/views/selection_controller.cc
@@ -56,10 +56,7 @@ break; case 1: // Select the word at the click location on a double click. - delegate_->OnBeforePointerAction(); - render_text->MoveCursorTo(event.location(), false); - render_text->SelectWord(); - delegate_->OnAfterPointerAction(false, true); + SelectWord(event.location()); double_click_word_ = render_text->selection(); break; case 2: @@ -73,6 +70,15 @@ } } + // TODO(crbug.com/676296): Right clicking an unfocused text view should select + // all its text on Mac. + const bool select_word_on_right_click = + event.IsOnlyRightMouseButton() && + PlatformStyle::kSelectWordOnRightClick && + !render_text->IsPointInSelection(event.location()); + if (select_word_on_right_click) + SelectWord(event.location()); + if (handles_selection_clipboard_ && event.IsOnlyMiddleMouseButton()) { if (render_text->IsPointInSelection(event.location())) { delegate_->OnBeforePointerAction(); @@ -171,6 +177,15 @@ } } +void SelectionController::SelectWord(const gfx::Point& point) { + gfx::RenderText* render_text = GetRenderText(); + DCHECK(render_text); + delegate_->OnBeforePointerAction(); + render_text->MoveCursorTo(point, false); + render_text->SelectWord(); + delegate_->OnAfterPointerAction(false, true); +} + gfx::RenderText* SelectionController::GetRenderText() { return delegate_->GetRenderTextForSelectionController(); }
diff --git a/ui/views/selection_controller.h b/ui/views/selection_controller.h index ed9ffab..8a410e9 100644 --- a/ui/views/selection_controller.h +++ b/ui/views/selection_controller.h
@@ -53,6 +53,9 @@ // Tracks the mouse clicks for single/double/triple clicks. void TrackMouseClicks(const ui::MouseEvent& event); + // Selects the word at the given |point|. + void SelectWord(const gfx::Point& point); + // Returns the associated render text instance via the |delegate_|. gfx::RenderText* GetRenderText();
diff --git a/ui/views/style/platform_style.cc b/ui/views/style/platform_style.cc index c7cbab1..dc20e0f8 100644 --- a/ui/views/style/platform_style.cc +++ b/ui/views/style/platform_style.cc
@@ -42,6 +42,7 @@ const bool PlatformStyle::kDefaultLabelButtonHasBoldFont = true; const bool PlatformStyle::kDialogDefaultButtonCanBeCancel = true; const bool PlatformStyle::kTextDragVerticallyDragsToEnd = false; +const bool PlatformStyle::kSelectWordOnRightClick = false; const CustomButton::NotifyAction PlatformStyle::kMenuNotifyActivationAction = CustomButton::NOTIFY_ON_RELEASE; const bool PlatformStyle::kTreeViewHasFocusRing = false;
diff --git a/ui/views/style/platform_style.h b/ui/views/style/platform_style.h index 181c180..dfa434f13 100644 --- a/ui/views/style/platform_style.h +++ b/ui/views/style/platform_style.h
@@ -44,6 +44,9 @@ // the left or right end of the text from the cursor, respectively. static const bool kTextDragVerticallyDragsToEnd; + // Whether right clicking on text, selects the word under cursor. + static const bool kSelectWordOnRightClick; + // The menu button's action to show the menu. static const CustomButton::NotifyAction kMenuNotifyActivationAction;
diff --git a/ui/views/style/platform_style_mac.mm b/ui/views/style/platform_style_mac.mm index 0b983bb..97caa954 100644 --- a/ui/views/style/platform_style_mac.mm +++ b/ui/views/style/platform_style_mac.mm
@@ -24,6 +24,7 @@ const bool PlatformStyle::kDefaultLabelButtonHasBoldFont = false; const bool PlatformStyle::kDialogDefaultButtonCanBeCancel = false; const bool PlatformStyle::kTextDragVerticallyDragsToEnd = true; +const bool PlatformStyle::kSelectWordOnRightClick = true; const bool PlatformStyle::kTreeViewHasFocusRing = true; const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = true; const bool PlatformStyle::kUseRipples = false;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc index 132cfa7..cfc4f0d 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -1027,12 +1027,15 @@ // and does not change. return; } - - SetUseNativeFrame(new_type == Widget::FRAME_TYPE_FORCE_NATIVE); - // Replace the frame and layout the contents. Even though we don't have a - // swapable glass frame like on Windows, we still replace the frame because - // the button assets don't update otherwise. - native_widget_delegate_->AsWidget()->non_client_view()->UpdateFrame(); + // Avoid mutating |View::children_| while possibly iterating over them. + // See View::PropagateNativeThemeChanged(). + // TODO(varkha, sadrul): Investigate removing this (and instead expecting the + // NonClientView::UpdateFrame() to update the frame-view when theme changes, + // like all other views). + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&DesktopWindowTreeHostX11::DelayedChangeFrameType, + weak_factory_.GetWeakPtr(), + new_type)); } void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) { @@ -2279,6 +2282,14 @@ delayed_resize_task_.Cancel(); } +void DesktopWindowTreeHostX11::DelayedChangeFrameType(Widget::FrameType type) { + SetUseNativeFrame(type == Widget::FRAME_TYPE_FORCE_NATIVE); + // Replace the frame and layout the contents. Even though we don't have a + // swappable glass frame like on Windows, we still replace the frame because + // the button assets don't update otherwise. + native_widget_delegate_->AsWidget()->non_client_view()->UpdateFrame(); +} + gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInPixels() const { std::vector<int> value; if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h index 7b78e7e..34a7e75 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
@@ -271,6 +271,7 @@ uint32_t DispatchEvent(const ui::PlatformEvent& event) override; void DelayedResize(const gfx::Size& size_in_pixels); + void DelayedChangeFrameType(Widget::FrameType new_type); gfx::Rect GetWorkAreaBoundsInPixels() const; gfx::Rect ToDIPRect(const gfx::Rect& rect_in_pixels) const;