diff --git a/DEPS b/DEPS index 85a9eae0..fa00e08 100644 --- a/DEPS +++ b/DEPS
@@ -40,7 +40,7 @@ # 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': '746cc0b92290a81a8756a3a75d486cf3c66fcaad', + 'skia_revision': 'efa9d34ccbdeb541a1fa77a678552df7a08531be', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # 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': '4fc181694898980a2b23778d242d6971765d7b37', + 'catapult_revision': '2c95066149e88b00bea89ac25599cbd8f931de0d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other.
diff --git a/base/BUILD.gn b/base/BUILD.gn index a038579d9..7e28bf8 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -2025,6 +2025,7 @@ "template_util_unittest.cc", "test/histogram_tester_unittest.cc", "test/scoped_mock_time_message_loop_task_runner_unittest.cc", + "test/scoped_task_scheduler_unittest.cc", "test/test_pending_task_unittest.cc", "test/test_reg_util_win_unittest.cc", "test/trace_event_analyzer_unittest.cc",
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc index ddbd63e5..30a8922 100644 --- a/base/allocator/partition_allocator/partition_alloc_unittest.cc +++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -11,6 +11,7 @@ #include <vector> #include "base/bits.h" +#include "base/sys_info.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -187,6 +188,10 @@ #endif } +bool IsLargeMemoryDevice() { + return base::SysInfo::AmountOfPhysicalMemory() >= 2LL * 1024 * 1024 * 1024; +} + class MockPartitionStatsDumper : public PartitionStatsDumper { public: MockPartitionStatsDumper() @@ -672,9 +677,13 @@ // Can we allocate a massive (512MB) size? // Allocate 512MB, but +1, to test for cookie writing alignment issues. - ptr = partitionAllocGeneric(genericAllocator.root(), 512 * 1024 * 1024 + 1, - typeName); - partitionFreeGeneric(genericAllocator.root(), ptr); + // Test this only if the device has enough memory or it might fail due + // to OOM. + if (IsLargeMemoryDevice()) { + ptr = partitionAllocGeneric(genericAllocator.root(), 512 * 1024 * 1024 + 1, + typeName); + partitionFreeGeneric(genericAllocator.root(), ptr); + } // Check a more reasonable, but still direct mapped, size. // Chop a system page and a byte off to test for rounding errors. @@ -745,15 +754,18 @@ partitionFreeGeneric(genericAllocator.root(), ptr); // Allocate something very large, and uneven. - requestedSize = 512 * 1024 * 1024 - 1; - predictedSize = - partitionAllocActualSize(genericAllocator.root(), requestedSize); - ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize, typeName); - EXPECT_TRUE(ptr); - actualSize = partitionAllocGetSize(ptr); - EXPECT_EQ(predictedSize, actualSize); - EXPECT_LT(requestedSize, actualSize); - partitionFreeGeneric(genericAllocator.root(), ptr); + if (IsLargeMemoryDevice()) { + requestedSize = 512 * 1024 * 1024 - 1; + predictedSize = + partitionAllocActualSize(genericAllocator.root(), requestedSize); + ptr = + partitionAllocGeneric(genericAllocator.root(), requestedSize, typeName); + EXPECT_TRUE(ptr); + actualSize = partitionAllocGetSize(ptr); + EXPECT_EQ(predictedSize, actualSize); + EXPECT_LT(requestedSize, actualSize); + partitionFreeGeneric(genericAllocator.root(), ptr); + } // Too large allocation. requestedSize = INT_MAX; @@ -1328,7 +1340,7 @@ #endif TEST(PartitionAllocTest, MAYBE_RepeatedReturnNullDirect) { // A direct-mapped allocation size. - DoReturnNullTest(256 * 1024 * 1024); + DoReturnNullTest(32 * 1024 * 1024); } #endif // !defined(ARCH_CPU_64_BITS) || defined(OS_POSIX)
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index 1581f6c..2212941d 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc
@@ -357,12 +357,20 @@ void MessageLoop::SetTaskRunner( scoped_refptr<SingleThreadTaskRunner> task_runner) { DCHECK_EQ(this, current()); + DCHECK(task_runner); DCHECK(task_runner->BelongsToCurrentThread()); DCHECK(!unbound_task_runner_); task_runner_ = std::move(task_runner); SetThreadTaskRunnerHandle(); } +void MessageLoop::ClearTaskRunnerForTesting() { + DCHECK_EQ(this, current()); + DCHECK(!unbound_task_runner_); + task_runner_ = nullptr; + thread_task_runner_handle_.reset(); +} + void MessageLoop::SetThreadTaskRunnerHandle() { DCHECK_EQ(this, current()); // Clear the previous thread task runner first, because only one can exist at
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h index ac7a303..91a7b1d3 100644 --- a/base/message_loop/message_loop.h +++ b/base/message_loop/message_loop.h
@@ -234,6 +234,10 @@ // thread to which the message loop is bound. void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner); + // Clears task_runner() and the ThreadTaskRunnerHandle for the target thread. + // Must be called on the thread to which the message loop is bound. + void ClearTaskRunnerForTesting(); + // Enables or disables the recursive task processing. This happens in the case // of recursive message loops. Some unwanted message loops may occur when // using common controls or printer functions. By default, recursive task
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h index 66b278f..99cbef4 100644 --- a/base/numerics/safe_conversions.h +++ b/base/numerics/safe_conversions.h
@@ -110,35 +110,35 @@ // that the specified numeric conversion will saturate by default rather than // overflow or underflow, and NaN assignment to an integral will return 0. // All boundary condition behaviors can be overriden with a custom handler. +template <template <typename> + class SaturationHandler = SaturatedCastDefaultHandler, + typename Dst, + typename Src> +constexpr Dst saturated_cast_impl(const Src value, + const RangeConstraint constraint) { + return constraint.IsValid() + ? static_cast<Dst>(value) + : (constraint.IsOverflow() + ? SaturationHandler<Dst>::HandleOverflow() + // Skip this check for integral Src, which cannot be NaN. + : (std::is_integral<Src>::value || constraint.IsUnderflow() + ? SaturationHandler<Dst>::HandleUnderflow() + : SaturationHandler<Dst>::HandleNaN())); +} + +// saturated_cast<> is analogous to static_cast<> for numeric types, except +// that the specified numeric conversion will saturate by default rather than +// overflow or underflow, and NaN assignment to an integral will return 0. +// All boundary condition behaviors can be overriden with a custom handler. template <typename Dst, template <typename> class SaturationHandler = SaturatedCastDefaultHandler, typename Src> constexpr Dst saturated_cast(Src value) { - static_assert( - SaturationHandler<Dst>::lowest() < SaturationHandler<Dst>::max(), ""); - // While this looks like a lot of code, it's all constexpr and all but - // one variable are compile-time constants (enforced by a static_assert). - // So, it should evaluate to the minimum number of comparisons required - // for the range check, which is 0-3, depending on the exact source and - // destination types, and whatever custom range is specified. using SrcType = typename UnderlyingType<Src>::type; - return IsGreaterOrEqual<SrcType, Dst>::Test( - value, NarrowingRange<Dst, SrcType, SaturationHandler>::lowest()) - ? (IsLessOrEqual<SrcType, Dst>::Test( - value, - NarrowingRange<Dst, SrcType, SaturationHandler>::max()) - ? static_cast<Dst>(value) - : SaturationHandler<Dst>::HandleOverflow()) - // This last branch is a little confusing. It's specifically to - // catch NaN when converting from float to integral. - : (std::is_integral<SrcType>::value || - std::is_floating_point<Dst>::value || - IsLessOrEqual<SrcType, Dst>::Test( - value, NarrowingRange<Dst, SrcType, - SaturationHandler>::max()) - ? SaturationHandler<Dst>::HandleUnderflow() - : SaturationHandler<Dst>::HandleNaN()); + return saturated_cast_impl<SaturationHandler, Dst>( + static_cast<SrcType>(value), + DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value)); } // strict_cast<> is analogous to static_cast<> for numeric types, except that
diff --git a/base/numerics/safe_conversions_impl.h b/base/numerics/safe_conversions_impl.h index 5ef3321..fd0064dca 100644 --- a/base/numerics/safe_conversions_impl.h +++ b/base/numerics/safe_conversions_impl.h
@@ -166,29 +166,38 @@ static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; }; -enum RangeConstraint { - RANGE_VALID = 0x0, // Value can be represented by the destination type. - RANGE_UNDERFLOW = 0x1, // Value would overflow. - RANGE_OVERFLOW = 0x2, // Value would underflow. +enum RangeConstraintEnum { + RANGE_VALID = 0x0, // Value can be represented by the destination type. + RANGE_OVERFLOW = 0x1, // Value would overflow. + RANGE_UNDERFLOW = 0x2, // Value would underflow. RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). }; -// Helper function for coercing an int back to a RangeContraint. -constexpr RangeConstraint GetRangeConstraint(int integer_range_constraint) { - // TODO(jschuh): Once we get full C++14 support we want this - // assert(integer_range_constraint >= RANGE_VALID && - // integer_range_constraint <= RANGE_INVALID) - return static_cast<RangeConstraint>(integer_range_constraint); -} +// This class wraps the range constraints as separate booleans so the compiler +// can identify constants and eliminate unused code paths. +class RangeConstraint { + public: + constexpr RangeConstraint(bool is_in_upper_bound, bool is_in_lower_bound) + : is_overflow_(!is_in_upper_bound), is_underflow_(!is_in_lower_bound) {} + constexpr RangeConstraint() : is_overflow_(0), is_underflow_(0) {} + constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } + constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } + constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } + constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } -// This function creates a RangeConstraint from an upper and lower bound -// check by taking advantage of the fact that only NaN can be out of range in -// both directions at once. -constexpr inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, - bool is_in_lower_bound) { - return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) | - (is_in_lower_bound ? 0 : RANGE_UNDERFLOW)); -} + // These are some wrappers to make the tests a bit cleaner. + constexpr operator RangeConstraintEnum() const { + return static_cast<RangeConstraintEnum>( + static_cast<int>(is_overflow_) | static_cast<int>(is_underflow_) << 1); + } + constexpr bool operator==(const RangeConstraintEnum rhs) const { + return rhs == static_cast<RangeConstraintEnum>(*this); + } + + private: + const bool is_overflow_; + const bool is_underflow_; +}; // The following helper template addresses a corner case in range checks for // conversion from a floating-point type to an integral type of smaller range @@ -211,11 +220,9 @@ // To fix this bug we manually truncate the maximum value when the destination // type is an integral of larger precision than the source floating-point type, // such that the resulting maximum is represented exactly as a floating point. -template <typename Dst, - typename Src, - template <typename> class Bounds = std::numeric_limits> +template <typename Dst, typename Src, template <typename> class Bounds> struct NarrowingRange { - using SrcLimits = typename std::numeric_limits<Src>; + using SrcLimits = std::numeric_limits<Src>; using DstLimits = typename std::numeric_limits<Dst>; // Computes the mask required to make an accurate comparison between types. @@ -253,6 +260,7 @@ template <typename Dst, typename Src, + template <typename> class Bounds, IntegerRepresentation DstSign = std::is_signed<Dst>::value ? INTEGER_REPRESENTATION_SIGNED : INTEGER_REPRESENTATION_UNSIGNED, @@ -267,83 +275,112 @@ // split it into checks based on signedness to avoid confusing casts and // compiler warnings on signed an unsigned comparisons. -// Dst range is statically determined to contain Src: Nothing to check. +// Same sign narrowing: The range is contained for normal limits. template <typename Dst, typename Src, + template <typename> class Bounds, IntegerRepresentation DstSign, IntegerRepresentation SrcSign> struct DstRangeRelationToSrcRangeImpl<Dst, Src, + Bounds, DstSign, SrcSign, NUMERIC_RANGE_CONTAINED> { - static constexpr RangeConstraint Check(Src value) { return RANGE_VALID; } + static constexpr RangeConstraint Check(Src value) { + using SrcLimits = std::numeric_limits<Src>; + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + return RangeConstraint( + static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || + static_cast<Dst>(value) <= DstLimits::max(), + static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || + static_cast<Dst>(value) >= DstLimits::lowest()); + } }; // Signed to signed narrowing: Both the upper and lower boundaries may be -// exceeded. -template <typename Dst, typename Src> +// exceeded for standard limits. +template <typename Dst, typename Src, template <typename> class Bounds> struct DstRangeRelationToSrcRangeImpl<Dst, Src, + Bounds, INTEGER_REPRESENTATION_SIGNED, INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> { static constexpr RangeConstraint Check(Src value) { - return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()), - (value >= NarrowingRange<Dst, Src>::lowest())); + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + return RangeConstraint(value <= DstLimits::max(), + value >= DstLimits::lowest()); } }; -// Unsigned to unsigned narrowing: Only the upper boundary can be exceeded. -template <typename Dst, typename Src> +// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for +// standard limits. +template <typename Dst, typename Src, template <typename> class Bounds> struct DstRangeRelationToSrcRangeImpl<Dst, Src, + Bounds, INTEGER_REPRESENTATION_UNSIGNED, INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> { static constexpr RangeConstraint Check(Src value) { - return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true); + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + return RangeConstraint( + value <= DstLimits::max(), + DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest()); } }; -// Unsigned to signed: The upper boundary may be exceeded. -template <typename Dst, typename Src> +// Unsigned to signed: Only the upper bound can be exceeded for standard limits. +template <typename Dst, typename Src, template <typename> class Bounds> struct DstRangeRelationToSrcRangeImpl<Dst, Src, + Bounds, INTEGER_REPRESENTATION_SIGNED, INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> { static constexpr RangeConstraint Check(Src value) { - return IntegerBitsPlusSign<Dst>::value > IntegerBitsPlusSign<Src>::value - ? RANGE_VALID - : GetRangeConstraint( - value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()), - true); + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + using Promotion = decltype(Src() + Dst()); + return RangeConstraint(static_cast<Promotion>(value) <= + static_cast<Promotion>(DstLimits::max()), + DstLimits::lowest() <= Dst(0) || + static_cast<Promotion>(value) >= + static_cast<Promotion>(DstLimits::lowest())); } }; // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, -// and any negative value exceeds the lower boundary. -template <typename Dst, typename Src> +// and any negative value exceeds the lower boundary for standard limits. +template <typename Dst, typename Src, template <typename> class Bounds> struct DstRangeRelationToSrcRangeImpl<Dst, Src, + Bounds, INTEGER_REPRESENTATION_UNSIGNED, INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> { static constexpr RangeConstraint Check(Src value) { - return (MaxExponent<Dst>::value >= MaxExponent<Src>::value) - ? GetRangeConstraint(true, value >= static_cast<Src>(0)) - : GetRangeConstraint( - value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()), - value >= static_cast<Src>(0)); + using SrcLimits = std::numeric_limits<Src>; + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + using Promotion = decltype(Src() + Dst()); + return RangeConstraint( + static_cast<Promotion>(SrcLimits::max()) <= + static_cast<Promotion>(DstLimits::max()) || + static_cast<Promotion>(value) <= + static_cast<Promotion>(DstLimits::max()), + value >= Src(0) && (DstLimits::lowest() == 0 || + static_cast<Dst>(value) >= DstLimits::lowest())); } }; -template <typename Dst, typename Src> +template <typename Dst, + template <typename> class Bounds = std::numeric_limits, + typename Src> constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) { static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); - return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value); + static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); + return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); } // Integer promotion templates used by the portable checked integer arithmetic.
diff --git a/base/numerics/safe_numerics_unittest.cc b/base/numerics/safe_numerics_unittest.cc index 0e3d999b..5304593 100644 --- a/base/numerics/safe_numerics_unittest.cc +++ b/base/numerics/safe_numerics_unittest.cc
@@ -671,7 +671,14 @@ TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max()); TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1)); + + // Additional saturation tests. + EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max())); + EXPECT_EQ(DstLimits::lowest(), saturated_cast<Dst>(SrcLimits::lowest())); + if (SrcLimits::is_iec559) { + EXPECT_EQ(Dst(0), saturated_cast<Dst>(SrcLimits::quiet_NaN())); + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1); TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity()); TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1); @@ -714,6 +721,10 @@ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest()); TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max()); TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + + // Additional saturation tests. + EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max())); + EXPECT_EQ(Dst(0), saturated_cast<Dst>(SrcLimits::lowest())); } };
diff --git a/base/task_scheduler/task_scheduler.h b/base/task_scheduler/task_scheduler.h index cbfaae9..d4084c6f 100644 --- a/base/task_scheduler/task_scheduler.h +++ b/base/task_scheduler/task_scheduler.h
@@ -82,10 +82,6 @@ // other threads during the call. Returns immediately when shutdown completes. virtual void FlushForTesting() = 0; - // Joins all threads of this scheduler. Tasks that are already running are - // allowed to complete their execution. This can only be called once. - virtual void JoinForTesting() = 0; - // CreateAndSetSimpleTaskScheduler(), CreateAndSetDefaultTaskScheduler(), and // SetInstance() register a TaskScheduler to handle tasks posted through the // post_task.h API for this process. The registered TaskScheduler will only be
diff --git a/base/task_scheduler/task_scheduler_impl.h b/base/task_scheduler/task_scheduler_impl.h index 9a161037..0e03d52 100644 --- a/base/task_scheduler/task_scheduler_impl.h +++ b/base/task_scheduler/task_scheduler_impl.h
@@ -62,7 +62,10 @@ std::vector<const HistogramBase*> GetHistograms() const override; void Shutdown() override; void FlushForTesting() override; - void JoinForTesting() override; + + // Joins all threads. Tasks that are already running are allowed to complete + // their execution. This can only be called once. + void JoinForTesting(); private: explicit TaskSchedulerImpl(const WorkerPoolIndexForTraitsCallback&
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc index 4c1ee8da..447b35a 100644 --- a/base/task_scheduler/task_tracker.cc +++ b/base/task_scheduler/task_tracker.cc
@@ -288,6 +288,14 @@ } void TaskTracker::SetHasShutdownStartedForTesting() { + AutoSchedulerLock auto_lock(shutdown_lock_); + + // Create a dummy |shutdown_event_| to satisfy TaskTracker's expectation of + // its existence during shutdown (e.g. in OnBlockingShutdownTasksComplete()). + shutdown_event_.reset( + new WaitableEvent(WaitableEvent::ResetPolicy::MANUAL, + WaitableEvent::InitialState::NOT_SIGNALED)); + state_->StartShutdown(); }
diff --git a/base/test/OWNERS b/base/test/OWNERS index 34780973..92ca839 100644 --- a/base/test/OWNERS +++ b/base/test/OWNERS
@@ -1,5 +1,7 @@ phajdan.jr@chromium.org +per-file scoped_task_scheduler*=file://base/task_scheduler/OWNERS + # For Android-specific changes: per-file *android*=file://base/test/android/OWNERS per-file BUILD.gn=file://base/test/android/OWNERS
diff --git a/base/test/scoped_task_scheduler.cc b/base/test/scoped_task_scheduler.cc index 2cf5b33..19ee891 100644 --- a/base/test/scoped_task_scheduler.cc +++ b/base/test/scoped_task_scheduler.cc
@@ -4,37 +4,250 @@ #include "base/test/scoped_task_scheduler.h" +#include <memory> +#include <utility> #include <vector> #include "base/bind.h" -#include "base/task_scheduler/scheduler_worker_pool_params.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram_base.h" +#include "base/run_loop.h" +#include "base/sequence_token.h" +#include "base/sequenced_task_runner.h" +#include "base/single_thread_task_runner.h" +#include "base/task_runner.h" +#include "base/task_scheduler/task.h" #include "base/task_scheduler/task_scheduler.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" +#include "base/task_scheduler/task_tracker.h" +#include "base/threading/thread_task_runner_handle.h" namespace base { namespace test { -ScopedTaskScheduler::ScopedTaskScheduler() { - DCHECK(!TaskScheduler::GetInstance()); +namespace { - // Create a TaskScheduler with a single thread to make tests deterministic. - constexpr int kMaxThreads = 1; - std::vector<SchedulerWorkerPoolParams> worker_pool_params_vector; - worker_pool_params_vector.emplace_back( - "Simple", ThreadPriority::NORMAL, - SchedulerWorkerPoolParams::StandbyThreadPolicy::LAZY, kMaxThreads, - TimeDelta::Max()); - TaskScheduler::CreateAndSetDefaultTaskScheduler( - worker_pool_params_vector, - Bind([](const TaskTraits&) -> size_t { return 0; })); +enum class ExecutionMode { PARALLEL, SEQUENCED, SINGLE_THREADED }; + +class TestTaskScheduler : public TaskScheduler { + public: + // |external_message_loop| is an externally provided MessageLoop on which to + // run tasks. A MessageLoop will be created by TestTaskScheduler if + // |external_message_loop| is nullptr. + explicit TestTaskScheduler(MessageLoop* external_message_loop); + ~TestTaskScheduler() override; + + // TaskScheduler: + void PostTaskWithTraits(const tracked_objects::Location& from_here, + const TaskTraits& traits, + const Closure& task) override; + scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits( + const TaskTraits& traits) override; + scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunnerWithTraits( + const TaskTraits& traits) override; + scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunnerWithTraits( + const TaskTraits& traits) override; + std::vector<const HistogramBase*> GetHistograms() const override; + void Shutdown() override; + void FlushForTesting() override; + + // Posts |task| to this TaskScheduler with |sequence_token|. Returns true on + // success. + bool PostTask(std::unique_ptr<internal::Task> task, + const SequenceToken& sequence_token); + + // Runs |task| with |sequence_token| using this TaskScheduler's TaskTracker. + void RunTask(std::unique_ptr<internal::Task> task, + const SequenceToken& sequence_token); + + // Returns true if this TaskScheduler runs its tasks on the current thread. + bool RunsTasksOnCurrentThread() const; + + private: + // |message_loop_owned_| will be non-null if this TestTaskScheduler owns the + // MessageLoop (wasn't provided an external one at construction). + // |message_loop_| will always be set and is used by this TestTaskScheduler to + // run tasks. + std::unique_ptr<MessageLoop> message_loop_owned_; + MessageLoop* message_loop_; + + // The SingleThreadTaskRunner associated with |message_loop_|. + const scoped_refptr<SingleThreadTaskRunner> message_loop_task_runner_ = + message_loop_->task_runner(); + + // Handles shutdown behaviors and sets up the environment to run a task. + internal::TaskTracker task_tracker_; + + DISALLOW_COPY_AND_ASSIGN(TestTaskScheduler); +}; + +class TestTaskSchedulerTaskRunner : public SingleThreadTaskRunner { + public: + TestTaskSchedulerTaskRunner(TestTaskScheduler* task_scheduler, + ExecutionMode execution_mode, + TaskTraits traits); + + // SingleThreadTaskRunner: + bool PostDelayedTask(const tracked_objects::Location& from_here, + const Closure& closure, + TimeDelta delay) override; + bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here, + const Closure& closure, + TimeDelta delay) override; + bool RunsTasksOnCurrentThread() const override; + + private: + ~TestTaskSchedulerTaskRunner() override; + + TestTaskScheduler* const task_scheduler_; + const ExecutionMode execution_mode_; + const SequenceToken sequence_token_; + const TaskTraits traits_; + + DISALLOW_COPY_AND_ASSIGN(TestTaskSchedulerTaskRunner); +}; + +TestTaskScheduler::TestTaskScheduler(MessageLoop* external_message_loop) + : message_loop_owned_(external_message_loop ? nullptr + : MakeUnique<MessageLoop>()), + message_loop_(message_loop_owned_ ? message_loop_owned_.get() + : external_message_loop) {} + +TestTaskScheduler::~TestTaskScheduler() { + // Prevent the RunUntilIdle() call below from running SKIP_ON_SHUTDOWN and + // CONTINUE_ON_SHUTDOWN tasks. + task_tracker_.SetHasShutdownStartedForTesting(); + + // Run pending BLOCK_SHUTDOWN tasks. + RunLoop().RunUntilIdle(); +} + +void TestTaskScheduler::PostTaskWithTraits( + const tracked_objects::Location& from_here, + const TaskTraits& traits, + const Closure& task) { + CreateTaskRunnerWithTraits(traits)->PostTask(from_here, task); +} + +scoped_refptr<TaskRunner> TestTaskScheduler::CreateTaskRunnerWithTraits( + const TaskTraits& traits) { + return make_scoped_refptr( + new TestTaskSchedulerTaskRunner(this, ExecutionMode::PARALLEL, traits)); +} + +scoped_refptr<SequencedTaskRunner> +TestTaskScheduler::CreateSequencedTaskRunnerWithTraits( + const TaskTraits& traits) { + return make_scoped_refptr( + new TestTaskSchedulerTaskRunner(this, ExecutionMode::SEQUENCED, traits)); +} + +scoped_refptr<SingleThreadTaskRunner> +TestTaskScheduler::CreateSingleThreadTaskRunnerWithTraits( + const TaskTraits& traits) { + return make_scoped_refptr(new TestTaskSchedulerTaskRunner( + this, ExecutionMode::SINGLE_THREADED, traits)); +} + +std::vector<const HistogramBase*> TestTaskScheduler::GetHistograms() const { + NOTREACHED(); + return std::vector<const HistogramBase*>(); +} + +void TestTaskScheduler::Shutdown() { + NOTREACHED(); +} + +void TestTaskScheduler::FlushForTesting() { + NOTREACHED(); +} + +bool TestTaskScheduler::PostTask(std::unique_ptr<internal::Task> task, + const SequenceToken& sequence_token) { + DCHECK(task); + if (!task_tracker_.WillPostTask(task.get())) + return false; + internal::Task* const task_ptr = task.get(); + return message_loop_task_runner_->PostDelayedTask( + task_ptr->posted_from, Bind(&TestTaskScheduler::RunTask, Unretained(this), + Passed(&task), sequence_token), + task_ptr->delay); +} + +void TestTaskScheduler::RunTask(std::unique_ptr<internal::Task> task, + const SequenceToken& sequence_token) { + // Clear the MessageLoop TaskRunner to allow TaskTracker to register its own + // Thread/SequencedTaskRunnerHandle as appropriate. + MessageLoop::current()->ClearTaskRunnerForTesting(); + + // Run the task. + task_tracker_.RunTask(std::move(task), sequence_token.IsValid() + ? sequence_token + : SequenceToken::Create()); + + // Restore the MessageLoop TaskRunner. + MessageLoop::current()->SetTaskRunner(message_loop_task_runner_); +} + +bool TestTaskScheduler::RunsTasksOnCurrentThread() const { + return message_loop_task_runner_->RunsTasksOnCurrentThread(); +} + +TestTaskSchedulerTaskRunner::TestTaskSchedulerTaskRunner( + TestTaskScheduler* task_scheduler, + ExecutionMode execution_mode, + TaskTraits traits) + : task_scheduler_(task_scheduler), + execution_mode_(execution_mode), + sequence_token_(execution_mode == ExecutionMode::PARALLEL + ? SequenceToken() + : SequenceToken::Create()), + traits_(traits) {} + +bool TestTaskSchedulerTaskRunner::PostDelayedTask( + const tracked_objects::Location& from_here, + const Closure& closure, + TimeDelta delay) { + auto task = MakeUnique<internal::Task>(from_here, closure, traits_, delay); + if (execution_mode_ == ExecutionMode::SEQUENCED) + task->sequenced_task_runner_ref = make_scoped_refptr(this); + else if (execution_mode_ == ExecutionMode::SINGLE_THREADED) + task->single_thread_task_runner_ref = make_scoped_refptr(this); + return task_scheduler_->PostTask(std::move(task), sequence_token_); +} + +bool TestTaskSchedulerTaskRunner::PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + const Closure& closure, + TimeDelta delay) { + // Tasks are never nested within the task scheduler. + return PostDelayedTask(from_here, closure, delay); +} + +bool TestTaskSchedulerTaskRunner::RunsTasksOnCurrentThread() const { + if (execution_mode_ == ExecutionMode::PARALLEL) + return task_scheduler_->RunsTasksOnCurrentThread(); + return sequence_token_ == SequenceToken::GetForCurrentThread(); +} + +TestTaskSchedulerTaskRunner::~TestTaskSchedulerTaskRunner() = default; + +} // namespace + +ScopedTaskScheduler::ScopedTaskScheduler() : ScopedTaskScheduler(nullptr) {} + +ScopedTaskScheduler::ScopedTaskScheduler(MessageLoop* external_message_loop) { + DCHECK(!TaskScheduler::GetInstance()); + TaskScheduler::SetInstance( + MakeUnique<TestTaskScheduler>(external_message_loop)); task_scheduler_ = TaskScheduler::GetInstance(); } ScopedTaskScheduler::~ScopedTaskScheduler() { + DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_EQ(task_scheduler_, TaskScheduler::GetInstance()); - TaskScheduler::GetInstance()->Shutdown(); - TaskScheduler::GetInstance()->JoinForTesting(); TaskScheduler::SetInstance(nullptr); }
diff --git a/base/test/scoped_task_scheduler.h b/base/test/scoped_task_scheduler.h index f2b252b60..bcb98c0 100644 --- a/base/test/scoped_task_scheduler.h +++ b/base/test/scoped_task_scheduler.h
@@ -6,31 +6,58 @@ #define BASE_TEST_SCOPED_TASK_SCHEDULER_H_ #include "base/macros.h" +#include "base/threading/thread_checker.h" namespace base { +class MessageLoop; class TaskScheduler; namespace test { -// Initializes a TaskScheduler and allows usage of the -// base/task_scheduler/post_task.h API within its scope. +// Allows usage of the base/task_scheduler/post_task.h API within its scope. +// +// To run pending tasks synchronously, call RunLoop::Run/RunUntilIdle() on the +// thread where the ScopedTaskScheduler lives. The destructor runs remaining +// BLOCK_SHUTDOWN tasks synchronously. +// +// Example usage: +// +// In this snippet, RunUntilIdle() returns after "A" is run. +// base::test::ScopedTaskScheduler scoped_task_scheduler; +// base::PostTask(FROM_HERE, base::Bind(&A)); +// base::RunLoop::RunUntilIdle(); // Returns after running A. +// +// In this snippet, run_loop.Run() returns after running "B" and +// "RunLoop::Quit". +// base::RunLoop run_loop; +// base::PostTask(FROM_HERE, base::Bind(&B)); +// base::PostTask(FROM_HERE, base::Bind(&RunLoop::Quit, &run_loop)); +// base::PostTask(FROM_HERE, base::Bind(&C)); +// base::PostTaskWithTraits( +// base::TaskTraits().WithShutdownBehavior( +// base::TaskShutdownBehavior::BLOCK_SHUTDOWN), +// base::Bind(&D)); +// run_loop.Run(); // Returns after running B and RunLoop::Quit. +// +// At this point, |scoped_task_scheduler| will be destroyed. The destructor +// runs "D" because it's BLOCK_SHUTDOWN. "C" is skipped. class ScopedTaskScheduler { public: - // Initializes a TaskScheduler with default arguments. + // Registers a TaskScheduler that instantiates a MessageLoop on the current + // thread and runs its tasks on it. ScopedTaskScheduler(); - // Waits until all TaskScheduler tasks blocking shutdown complete their - // execution (see TaskShutdownBehavior). Then, joins all TaskScheduler threads - // and deletes the TaskScheduler. - // - // Note that joining TaskScheduler threads may involve waiting for - // CONTINUE_ON_SHUTDOWN tasks to complete their execution. Normally, in - // production, the process exits without joining TaskScheduler threads. + // Registers a TaskScheduler that runs its tasks on |external_message_loop|. + // |external_message_loop| must be bound to the current thread. + explicit ScopedTaskScheduler(MessageLoop* external_message_loop); + + // Runs all pending BLOCK_SHUTDOWN tasks and unregisters the TaskScheduler. ~ScopedTaskScheduler(); private: const TaskScheduler* task_scheduler_ = nullptr; + ThreadChecker thread_checker_; DISALLOW_COPY_AND_ASSIGN(ScopedTaskScheduler); };
diff --git a/base/test/scoped_task_scheduler_unittest.cc b/base/test/scoped_task_scheduler_unittest.cc new file mode 100644 index 0000000..057dff7 --- /dev/null +++ b/base/test/scoped_task_scheduler_unittest.cc
@@ -0,0 +1,252 @@ +// 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 "base/test/scoped_task_scheduler.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/sequence_checker.h" +#include "base/task_scheduler/post_task.h" +#include "base/task_scheduler/test_utils.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/threading/thread_checker.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace test { + +TEST(ScopedTaskSchedulerTest, PostTask) { + ScopedTaskScheduler scoped_task_scheduler; + + bool first_task_ran = false; + bool second_task_ran = false; + + SequenceCheckerImpl sequence_checker; + ThreadCheckerImpl thread_checker; + + // Detach |sequence_checker| and |thread_checker|. Otherwise, they are bound + // to the current thread without a SequenceToken or TaskToken (i.e. + // CalledOnValidSequence/Thread() will always return true on the current + // thread, even when the SequenceToken or TaskToken changes). + sequence_checker.DetachFromSequence(); + thread_checker.DetachFromThread(); + + PostTask(FROM_HERE, + Bind( + [](SequenceCheckerImpl* sequence_checker, + ThreadCheckerImpl* thread_checker, bool* first_task_ran) { + EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); + EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); + EXPECT_TRUE(thread_checker->CalledOnValidThread()); + *first_task_ran = true; + }, + Unretained(&sequence_checker), Unretained(&thread_checker), + Unretained(&first_task_ran))); + + PostTask(FROM_HERE, + Bind( + [](SequenceCheckerImpl* sequence_checker, + ThreadCheckerImpl* thread_checker, bool* second_task_ran) { + EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); + EXPECT_FALSE(sequence_checker->CalledOnValidSequence()); + EXPECT_FALSE(thread_checker->CalledOnValidThread()); + *second_task_ran = true; + }, + Unretained(&sequence_checker), Unretained(&thread_checker), + Unretained(&second_task_ran))); + + RunLoop().RunUntilIdle(); + + EXPECT_TRUE(first_task_ran); + EXPECT_TRUE(second_task_ran); +} + +TEST(ScopedTaskSchedulerTest, CreateTaskRunnerAndPostTask) { + ScopedTaskScheduler scoped_task_scheduler; + auto task_runner = CreateTaskRunnerWithTraits(TaskTraits()); + + bool first_task_ran = false; + bool second_task_ran = false; + + SequenceCheckerImpl sequence_checker; + ThreadCheckerImpl thread_checker; + + // Detach |sequence_checker| and |thread_checker|. Otherwise, they are bound + // to the current thread without a SequenceToken or TaskToken (i.e. + // CalledOnValidSequence/Thread() will always return true on the current + // thread, even when the SequenceToken or TaskToken changes). + sequence_checker.DetachFromSequence(); + thread_checker.DetachFromThread(); + + task_runner->PostTask( + FROM_HERE, + Bind( + [](SequenceCheckerImpl* sequence_checker, + ThreadCheckerImpl* thread_checker, bool* first_task_ran) { + EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); + EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); + EXPECT_TRUE(thread_checker->CalledOnValidThread()); + *first_task_ran = true; + }, + Unretained(&sequence_checker), Unretained(&thread_checker), + Unretained(&first_task_ran))); + + task_runner->PostTask( + FROM_HERE, + Bind( + [](SequenceCheckerImpl* sequence_checker, + ThreadCheckerImpl* thread_checker, bool* second_task_ran) { + EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); + EXPECT_FALSE(sequence_checker->CalledOnValidSequence()); + EXPECT_FALSE(thread_checker->CalledOnValidThread()); + *second_task_ran = true; + }, + Unretained(&sequence_checker), Unretained(&thread_checker), + Unretained(&second_task_ran))); + + RunLoop().RunUntilIdle(); + + EXPECT_TRUE(first_task_ran); + EXPECT_TRUE(second_task_ran); +} + +TEST(ScopedTaskSchedulerTest, CreateSequencedTaskRunnerAndPostTask) { + ScopedTaskScheduler scoped_task_scheduler; + auto task_runner = CreateSequencedTaskRunnerWithTraits(TaskTraits()); + + bool first_task_ran = false; + bool second_task_ran = false; + + SequenceCheckerImpl sequence_checker; + ThreadCheckerImpl thread_checker; + + // Detach |sequence_checker| and |thread_checker|. Otherwise, they are bound + // to the current thread without a SequenceToken or TaskToken (i.e. + // CalledOnValidSequence/Thread() will always return true on the current + // thread, even when the SequenceToken or TaskToken changes). + sequence_checker.DetachFromSequence(); + thread_checker.DetachFromThread(); + + task_runner->PostTask( + FROM_HERE, + Bind( + [](SequenceCheckerImpl* sequence_checker, + ThreadCheckerImpl* thread_checker, bool* first_task_ran) { + EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); + EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); + EXPECT_TRUE(thread_checker->CalledOnValidThread()); + *first_task_ran = true; + }, + Unretained(&sequence_checker), Unretained(&thread_checker), + Unretained(&first_task_ran))); + + task_runner->PostTask( + FROM_HERE, + Bind( + [](SequenceCheckerImpl* sequence_checker, + ThreadCheckerImpl* thread_checker, bool* second_task_ran) { + EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); + EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); + EXPECT_FALSE(thread_checker->CalledOnValidThread()); + *second_task_ran = true; + }, + Unretained(&sequence_checker), Unretained(&thread_checker), + Unretained(&second_task_ran))); + + RunLoop().RunUntilIdle(); + + EXPECT_TRUE(first_task_ran); + EXPECT_TRUE(second_task_ran); +} + +TEST(ScopedTaskSchedulerTest, CreateSingleThreadTaskRunnerAndPostTask) { + ScopedTaskScheduler scoped_task_scheduler; + auto task_runner = CreateSingleThreadTaskRunnerWithTraits(TaskTraits()); + + bool first_task_ran = false; + bool second_task_ran = false; + + SequenceCheckerImpl sequence_checker; + ThreadCheckerImpl thread_checker; + + // Detach |sequence_checker| and |thread_checker|. Otherwise, they are bound + // to the current thread without a SequenceToken or TaskToken (i.e. + // CalledOnValidSequence/Thread() will always return true on the current + // thread, even when the SequenceToken or TaskToken changes). + sequence_checker.DetachFromSequence(); + thread_checker.DetachFromThread(); + + task_runner->PostTask( + FROM_HERE, + Bind( + [](SequenceCheckerImpl* sequence_checker, + ThreadCheckerImpl* thread_checker, bool* first_task_ran) { + EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet()); + EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); + EXPECT_TRUE(thread_checker->CalledOnValidThread()); + *first_task_ran = true; + }, + Unretained(&sequence_checker), Unretained(&thread_checker), + Unretained(&first_task_ran))); + + task_runner->PostTask( + FROM_HERE, + Bind( + [](SequenceCheckerImpl* sequence_checker, + ThreadCheckerImpl* thread_checker, bool* second_task_ran) { + EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet()); + EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); + EXPECT_TRUE(thread_checker->CalledOnValidThread()); + *second_task_ran = true; + }, + Unretained(&sequence_checker), Unretained(&thread_checker), + Unretained(&second_task_ran))); + + RunLoop().RunUntilIdle(); + + EXPECT_TRUE(first_task_ran); + EXPECT_TRUE(second_task_ran); +} + +TEST(ScopedTaskSchedulerTest, ShutdownBehavior) { + bool block_shutdown_task_ran = false; + { + ScopedTaskScheduler scoped_task_scheduler; + PostTaskWithTraits( + FROM_HERE, TaskTraits().WithShutdownBehavior( + TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN), + Bind([]() { + ADD_FAILURE() << "CONTINUE_ON_SHUTDOWN task should not run"; + })); + PostTaskWithTraits(FROM_HERE, TaskTraits().WithShutdownBehavior( + TaskShutdownBehavior::SKIP_ON_SHUTDOWN), + Bind([]() { + ADD_FAILURE() + << "SKIP_ON_SHUTDOWN task should not run"; + })); + PostTaskWithTraits(FROM_HERE, TaskTraits().WithShutdownBehavior( + TaskShutdownBehavior::BLOCK_SHUTDOWN), + Bind( + [](bool* block_shutdown_task_ran) { + *block_shutdown_task_ran = true; + }, + Unretained(&block_shutdown_task_ran))); + } + EXPECT_TRUE(block_shutdown_task_ran); +} + +} // namespace test +} // namespace base
diff --git a/base/threading/sequenced_worker_pool_unittest.cc b/base/threading/sequenced_worker_pool_unittest.cc index 496c567..6e20339 100644 --- a/base/threading/sequenced_worker_pool_unittest.cc +++ b/base/threading/sequenced_worker_pool_unittest.cc
@@ -296,7 +296,8 @@ // Destroys and unregisters the registered TaskScheduler, if any. void DeleteTaskScheduler() { if (TaskScheduler::GetInstance()) { - TaskScheduler::GetInstance()->JoinForTesting(); + static_cast<internal::TaskSchedulerImpl*>(TaskScheduler::GetInstance()) + ->JoinForTesting(); TaskScheduler::SetInstance(nullptr); } }
diff --git a/build/android/gyp/aar.py b/build/android/gyp/aar.py index 566cab3..6c0bc89 100755 --- a/build/android/gyp/aar.py +++ b/build/android/gyp/aar.py
@@ -77,6 +77,7 @@ data['has_classes_jar'] = False data['has_proguard_flags'] = False data['has_native_libraries'] = False + data['has_r_text_file'] = False with zipfile.ZipFile(aar_file) as z: data['is_manifest_empty'] = ( _IsManifestEmpty(z.read('AndroidManifest.xml'))) @@ -101,6 +102,10 @@ data['has_classes_jar'] = True elif name == 'proguard.txt': data['has_proguard_flags'] = True + elif name == 'R.txt': + # Some AARs, e.g. gvr_controller_java, have empty R.txt. Such AARs + # have no resources as well. We treat empty R.txt as having no R.txt. + data['has_r_text_file'] = (z.read('R.txt').strip() != '') print gn_helpers.ToGNString(data)
diff --git a/build/android/gyp/process_resources.py b/build/android/gyp/process_resources.py index 8cac142..1166788e 100755 --- a/build/android/gyp/process_resources.py +++ b/build/android/gyp/process_resources.py
@@ -74,7 +74,11 @@ parser.add_option('--srcjar-out', help='Path to srcjar to contain generated R.java.') parser.add_option('--r-text-out', - help='Path to store the R.txt file generated by appt.') + help='Path to store the generated R.txt file.') + parser.add_option('--r-text-in', + help='Path to pre-existing R.txt for these resources. ' + 'Resource names from it will be used to generate R.java ' + 'instead of aapt-generated R.txt.') parser.add_option('--proguard-file', help='Path to proguard.txt generated file') @@ -169,8 +173,24 @@ # figure out which entries belong to them, but use the values from the # main R.txt file. for entry in _ParseTextSymbolsFile(r_txt_file): - entry = all_resources[(entry.resource_type, entry.name)] - resources_by_type[entry.resource_type].append(entry) + entry = all_resources.get((entry.resource_type, entry.name)) + # For most cases missing entry here is an error. It means that some + # library claims to have or depend on a resource that isn't included into + # the APK. There is one notable exception: Google Play Services (GMS). + # GMS is shipped as a bunch of AARs. One of them - basement - contains + # R.txt with ids of all resources, but most of the resources are in the + # other AARs. However, all other AARs reference their resources via + # basement's R.java so the latter must contain all ids that are in its + # R.txt. Most targets depend on only a subset of GMS AARs so some + # resources are missing, which is okay because the code that references + # them is missing too. We can't get an id for a resource that isn't here + # so the only solution is to skip the resource entry entirely. + # + # We can verify that all entries referenced in the code were generated + # correctly by running Proguard on the APK: it will report missing + # fields. + if entry: + resources_by_type[entry.resource_type].append(entry) for package, resources_by_type in resources_by_package.iteritems(): package_r_java_dir = os.path.join(srcjar_dir, *package.split('.')) @@ -434,6 +454,12 @@ build_utils.Touch(r_txt_path) if not options.include_all_resources: + # --include-all-resources can only be specified for generating final R + # classes for APK. It makes no sense for APK to have pre-generated R.txt + # though, because aapt-generated already lists all available resources. + if options.r_text_in: + r_txt_path = options.r_text_in + packages = list(options.extra_res_packages) r_txt_files = list(options.extra_r_text_files)
diff --git a/build/android/pylib/gtest/gtest_test_instance.py b/build/android/pylib/gtest/gtest_test_instance.py index 3cdf586..394b9e8 100644 --- a/build/android/pylib/gtest/gtest_test_instance.py +++ b/build/android/pylib/gtest/gtest_test_instance.py
@@ -99,13 +99,15 @@ for test in raw_list: if not test: continue - if test[0] != ' ': + if not test.startswith(' '): test_case = test.split()[0] if test_case.endswith('.'): current = test_case - elif not 'YOU HAVE' in test: - test_name = test.split()[0] - ret += [current + test_name] + else: + test = test.strip() + if test and not 'YOU HAVE' in test: + test_name = test.split()[0] + ret += [current + test_name] return ret
diff --git a/build/android/pylib/gtest/gtest_test_instance_test.py b/build/android/pylib/gtest/gtest_test_instance_test.py index 8103108..a7b591a 100755 --- a/build/android/pylib/gtest/gtest_test_instance_test.py +++ b/build/android/pylib/gtest/gtest_test_instance_test.py
@@ -81,6 +81,18 @@ ] self.assertEqual(expected, actual) + def testParseGTestListTests_emptyTestName(self): + raw_output = [ + 'TestCase.', + ' ', + ' nonEmptyTestName', + ] + actual = gtest_test_instance.ParseGTestListTests(raw_output) + expected = [ + 'TestCase.nonEmptyTestName', + ] + self.assertEqual(expected, actual) + def testParseGTestOutput_pass(self): raw_output = [ '[ RUN ] FooTest.Bar',
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 53ef628..f6052ae 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -2538,7 +2538,7 @@ zip_path = invoker.zip_path srcjar_path = invoker.srcjar_path - r_text_path = invoker.r_text_path + r_text_out_path = invoker.r_text_out_path build_config = invoker.build_config android_manifest = invoker.android_manifest @@ -2560,7 +2560,7 @@ outputs = [ zip_path, srcjar_path, - r_text_path, + r_text_out_path, ] _all_resource_dirs = [] @@ -2623,12 +2623,21 @@ "--resource-zip-out", rebase_path(zip_path, root_build_dir), "--r-text-out", - rebase_path(r_text_path, root_build_dir), + rebase_path(r_text_out_path, root_build_dir), "--dependencies-res-zips=@FileArg($rebase_build_config:resources:dependency_zips)", "--extra-res-packages=@FileArg($rebase_build_config:resources:extra_package_names)", "--extra-r-text-files=@FileArg($rebase_build_config:resources:extra_r_text_files)", ] + if (defined(invoker.r_text_in_path)) { + _r_text_in_path = invoker.r_text_in_path + inputs += [ _r_text_in_path ] + args += [ + "--r-text-in", + rebase_path(_r_text_in_path, root_build_dir), + ] + } + if (non_constant_id) { args += [ "--non-constant-id" ] }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 5186640..2a45db4 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -646,6 +646,8 @@ # different application at runtime to access the package's resources. # app_as_shared_lib: If true make a resource package that can be loaded as # both shared_resources and normal application. + # r_text_file: (optional) path to pre-generated R.txt to be used when + # generating R.java instead of resource-based aapt-generated one. # Example: # android_resources("foo_resources") { @@ -667,7 +669,7 @@ base_path = "$target_gen_dir/$target_name" zip_path = base_path + ".resources.zip" srcjar_path = base_path + ".srcjar" - r_text_path = base_path + "_R.txt" + r_text_out_path = base_path + "_R.txt" build_config = base_path + ".build_config" build_config_target_name = "${target_name}__build_config" @@ -700,7 +702,7 @@ # No package means resources override their deps. if (defined(custom_package) || defined(android_manifest)) { - r_text = r_text_path + r_text = r_text_out_path } else { assert(defined(invoker.deps), "Must specify deps when custom_package is omitted.") @@ -731,6 +733,10 @@ deps += [ invoker.android_manifest_dep ] } + if (defined(invoker.r_text_file)) { + r_text_in_path = invoker.r_text_file + } + # Always generate R.onResourcesLoaded() method, it is required for # compiling ResourceRewriter, there is no side effect because the # generated R.class isn't used in final apk. @@ -1675,7 +1681,7 @@ "shared_resources", ]) srcjar_path = "${target_gen_dir}/${target_name}.srcjar" - r_text_path = "${target_gen_dir}/${target_name}_R.txt" + r_text_out_path = "${target_gen_dir}/${target_name}_R.txt" android_manifest = _android_manifest resource_dirs = [ "//build/android/ant/empty/res" ] zip_path = resources_zip_path @@ -2710,8 +2716,14 @@ "${_output_path}/AndroidManifest.xml", ] - if (_scanned_files.resources != []) { + if (_scanned_files.has_r_text_file) { + # Certain packages, in particular Play Services have no R.txt even + # though its presence is mandated by AAR spec. Such packages cause + # spurious rebuilds if this output is specified unconditionally. outputs += [ "${_output_path}/R.txt" ] + } + + if (_scanned_files.resources != []) { outputs += get_path_info( rebase_path(_scanned_files.resources, "", _output_path), "abspath") @@ -2728,7 +2740,7 @@ } # Create the android_resources target for resources. - if (_scanned_files.resources != []) { + if (_scanned_files.resources != [] || _scanned_files.has_r_text_file) { _res_target_name = "${target_name}__res" android_resources(_res_target_name) { forward_variables_from(invoker, [ "deps" ]) @@ -2742,6 +2754,9 @@ rebase_path(_scanned_files.resources, "", _output_path) android_manifest_dep = ":$_unpack_target_name" android_manifest = "${_output_path}/AndroidManifest.xml" + if (_scanned_files.has_r_text_file) { + r_text_file = "${_output_path}/R.txt" + } v14_skip = true } }
diff --git a/chrome/android/java/proguard.flags b/chrome/android/java/proguard.flags index 3a301b7..cedca4c 100644 --- a/chrome/android/java/proguard.flags +++ b/chrome/android/java/proguard.flags
@@ -27,9 +27,6 @@ public <init>(...); } -# Google Play Services warnings are about its resources. --dontwarn com.google.android.gms.R** - # The Google Play services library depends on the legacy Apache HTTP library, # and just adding it as proguard time dependency causes the following warnings: # `library class org.apache.http.params.HttpConnectionParams depends on program
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java index b5509909..b024736 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ActionItem.java
@@ -41,7 +41,6 @@ private boolean mImpressionTracked; public ActionItem(SuggestionsSection section) { - super(section); mCategoryInfo = section.getCategoryInfo(); mParentSection = section; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/AllDismissedItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/AllDismissedItem.java index e03acc5..a5f11e0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/AllDismissedItem.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/AllDismissedItem.java
@@ -22,12 +22,6 @@ * to restore the dismissed sections and load new suggestions from the server. */ public class AllDismissedItem extends OptionalLeaf { - /** - * @param parent The item's parent node. - */ - public AllDismissedItem(NodeParent parent) { - super(parent); - } @Override @ItemViewType
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java index 87151a0..0aaf26f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java
@@ -4,31 +4,34 @@ package org.chromium.chrome.browser.ntp.cards; +import android.support.annotation.CallSuper; + /** * A node in the tree that has a parent and can notify it about changes. * * This class mostly serves as a convenience base class for implementations of {@link TreeNode}. */ public abstract class ChildNode implements TreeNode { - private final NodeParent mParent; + private NodeParent mParent; - protected ChildNode(NodeParent parent) { + @Override + @CallSuper + public void setParent(NodeParent parent) { + assert mParent == null; + assert parent != null; mParent = parent; } - @Override - public void init() {} - protected void notifyItemRangeChanged(int index, int count) { - mParent.onItemRangeChanged(this, index, count); + if (mParent != null) mParent.onItemRangeChanged(this, index, count); } protected void notifyItemRangeInserted(int index, int count) { - mParent.onItemRangeInserted(this, index, count); + if (mParent != null) mParent.onItemRangeInserted(this, index, count); } protected void notifyItemRangeRemoved(int index, int count) { - mParent.onItemRangeRemoved(this, index, count); + if (mParent != null) mParent.onItemRangeRemoved(this, index, count); } protected void notifyItemChanged(int index) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java index 563ccfe2..9fca3e1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java
@@ -19,12 +19,6 @@ * A footer to show some text and a link to learn more. */ public class Footer extends OptionalLeaf { - /** - * @param parent The footer's parent node. - */ - public Footer(NodeParent parent) { - super(parent); - } @Override @ItemViewType
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java index 3d6c70b..0c57425 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java
@@ -4,62 +4,58 @@ package org.chromium.chrome.browser.ntp.cards; +import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; +import java.util.ArrayList; import java.util.List; /** * An inner node in the tree: the root of a subtree, with a list of child nodes. */ -public abstract class InnerNode extends ChildNode implements NodeParent { - public InnerNode(NodeParent parent) { - super(parent); - } - - protected abstract List<TreeNode> getChildren(); +public class InnerNode extends ChildNode implements NodeParent { + private final List<TreeNode> mChildren = new ArrayList<>(); private int getChildIndexForPosition(int position) { if (position < 0) { throw new IndexOutOfBoundsException(Integer.toString(position)); } - List<TreeNode> children = getChildren(); int numItems = 0; - int numChildren = children.size(); + int numChildren = mChildren.size(); for (int i = 0; i < numChildren; i++) { - numItems += children.get(i).getItemCount(); + numItems += mChildren.get(i).getItemCount(); if (position < numItems) return i; } throw new IndexOutOfBoundsException(position + "/" + numItems); } private int getStartingOffsetForChildIndex(int childIndex) { - List<TreeNode> children = getChildren(); - if (childIndex < 0 || childIndex >= children.size()) { - throw new IndexOutOfBoundsException(childIndex + "/" + children.size()); + if (childIndex < 0 || childIndex >= mChildren.size()) { + throw new IndexOutOfBoundsException(childIndex + "/" + mChildren.size()); } int offset = 0; for (int i = 0; i < childIndex; i++) { - offset += children.get(i).getItemCount(); + offset += mChildren.get(i).getItemCount(); } return offset; } int getStartingOffsetForChild(TreeNode child) { - return getStartingOffsetForChildIndex(getChildren().indexOf(child)); + return getStartingOffsetForChildIndex(mChildren.indexOf(child)); } /** * Returns the child whose subtree contains the item at the given position. */ TreeNode getChildForPosition(int position) { - return getChildren().get(getChildIndexForPosition(position)); + return mChildren.get(getChildIndexForPosition(position)); } @Override public int getItemCount() { int numItems = 0; - for (TreeNode child : getChildren()) { + for (TreeNode child : mChildren) { numItems += child.getItemCount(); } return numItems; @@ -69,28 +65,28 @@ @ItemViewType public int getItemViewType(int position) { int index = getChildIndexForPosition(position); - return getChildren().get(index).getItemViewType( + return mChildren.get(index).getItemViewType( position - getStartingOffsetForChildIndex(index)); } @Override public void onBindViewHolder(NewTabPageViewHolder holder, int position) { int index = getChildIndexForPosition(position); - getChildren().get(index).onBindViewHolder( + mChildren.get(index).onBindViewHolder( holder, position - getStartingOffsetForChildIndex(index)); } @Override public SnippetArticle getSuggestionAt(int position) { int index = getChildIndexForPosition(position); - return getChildren().get(index).getSuggestionAt( + return mChildren.get(index).getSuggestionAt( position - getStartingOffsetForChildIndex(index)); } @Override public int getDismissSiblingPosDelta(int position) { int index = getChildIndexForPosition(position); - return getChildren().get(index).getDismissSiblingPosDelta( + return mChildren.get(index).getDismissSiblingPosDelta( position - getStartingOffsetForChildIndex(index)); } @@ -109,33 +105,63 @@ notifyItemRangeRemoved(getStartingOffsetForChild(child) + index, count); } - @Override - public void init() { - super.init(); - for (TreeNode child : getChildren()) { - child.init(); - } - } - /** - * Helper method for adding a new child node. Notifies about the inserted items and initializes - * the child. + * Helper method that adds a new child node and notifies about its insertion. * * @param child The child node to be added. */ - protected void didAddChild(TreeNode child) { + protected void addChild(TreeNode child) { + int insertedIndex = getItemCount(); + mChildren.add(child); + child.setParent(this); + int count = child.getItemCount(); - if (count > 0) onItemRangeInserted(child, 0, count); - child.init(); + if (count > 0) notifyItemRangeInserted(insertedIndex, count); } /** - * Helper method for removing a child node. Notifies about the removed items. + * Helper method that adds all the children and notifies about the inserted items. + */ + protected void addChildren(TreeNode... children) { + int initialCount = getItemCount(); + for (TreeNode child : children) { + mChildren.add(child); + child.setParent(this); + } + + int addedCount = getItemCount() - initialCount; + if (addedCount > 0) notifyItemRangeInserted(initialCount, addedCount); + } + + /** + * Helper method that removes a child node and notifies about the removed items. * * @param child The child node to be removed. */ - protected void willRemoveChild(TreeNode child) { + protected void removeChild(TreeNode child) { + int removedIndex = mChildren.indexOf(child); + if (removedIndex == -1) throw new IndexOutOfBoundsException(); + int count = child.getItemCount(); - if (count > 0) onItemRangeRemoved(child, 0, count); + int childStartingOffset = getStartingOffsetForChildIndex(removedIndex); + + mChildren.remove(removedIndex); + if (count > 0) notifyItemRangeRemoved(childStartingOffset, count); + } + + /** + * Helper method that removes all the children and notifies about the removed items. + */ + protected void removeChildren() { + int itemCount = getItemCount(); + if (itemCount == 0) return; + + mChildren.clear(); + notifyItemRangeRemoved(0, itemCount); + } + + @VisibleForTesting + final List<TreeNode> getChildren() { + return mChildren; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Leaf.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Leaf.java index a2282a0..ade8c74 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Leaf.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Leaf.java
@@ -11,7 +11,7 @@ * If the leaf is not to be a permanent member of the tree, see {@link OptionalLeaf} for an * implementation that will take care of hiding or showing the item. */ -public abstract class Leaf implements TreeNode { +public abstract class Leaf extends ChildNode { @Override public int getItemCount() { return 1; @@ -42,9 +42,6 @@ return 0; } - @Override - public void init() {} - /** * Display the data for this item. * @param holder The view holder that should be updated.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java index ebb7b2d..8b24a1a3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -25,9 +25,6 @@ import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; -import java.util.Arrays; -import java.util.List; - /** * A class that handles merging above the fold elements and below the fold cards into an adapter * that will be used to back the NTP RecyclerView. The first element in the adapter should always be @@ -43,10 +40,6 @@ private final ItemTouchCallbacks mItemTouchCallbacks = new ItemTouchCallbacks(); private NewTabPageRecyclerView mRecyclerView; - /** - * List of all child nodes (which can themselves contain multiple child nodes). - */ - private final List<TreeNode> mChildren; private final InnerNode mRoot; private final AboveTheFoldItem mAboveTheFold = new AboveTheFoldItem(); @@ -132,23 +125,18 @@ mNewTabPageManager = manager; mAboveTheFoldView = aboveTheFoldView; mUiConfig = uiConfig; - mRoot = new InnerNode(this) { - @Override - protected List<TreeNode> getChildren() { - return mChildren; - } - }; + mRoot = new InnerNode(); - mSections = new SectionList(mRoot, mNewTabPageManager, offlinePageBridge); - mSigninPromo = new SignInPromo(mRoot, mNewTabPageManager); - mAllDismissed = new AllDismissedItem(mRoot); - mFooter = new Footer(mRoot); + mSections = new SectionList(mNewTabPageManager, offlinePageBridge); + mSigninPromo = new SignInPromo(mNewTabPageManager); + mAllDismissed = new AllDismissedItem(); + mFooter = new Footer(); - mChildren = Arrays.asList( + mRoot.addChildren( mAboveTheFold, mSections, mSigninPromo, mAllDismissed, mFooter, mBottomSpacer); - mRoot.init(); updateAllDismissedVisibility(); + mRoot.setParent(this); } /** Returns callbacks to configure the interactions with the RecyclerView's items. */ @@ -251,7 +239,7 @@ public void onItemRangeInserted(TreeNode child, int itemPosition, int itemCount) { assert child == mRoot; notifyItemRangeInserted(itemPosition, itemCount); - notifyItemChanged(getItemCount() - 1); // Refresh the spacer too. + mBottomSpacer.refresh(); updateAllDismissedVisibility(); } @@ -260,7 +248,7 @@ public void onItemRangeRemoved(TreeNode child, int itemPosition, int itemCount) { assert child == mRoot; notifyItemRangeRemoved(itemPosition, itemCount); - notifyItemChanged(getItemCount() - 1); // Refresh the spacer too. + mBottomSpacer.refresh(); updateAllDismissedVisibility(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/OptionalLeaf.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/OptionalLeaf.java index af1836a..1a3b7ff 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/OptionalLeaf.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/OptionalLeaf.java
@@ -19,14 +19,6 @@ public abstract class OptionalLeaf extends ChildNode { private boolean mVisible; - /** - * Constructor for {@link OptionalLeaf}. - * By default it is not visible. See {@link #setVisible(boolean)} to update the visibility. - */ - public OptionalLeaf(NodeParent parent) { - super(parent); - } - @Override public int getItemCount() { return isVisible() ? 1 : 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ProgressItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ProgressItem.java index a031edf5..425e3ee 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ProgressItem.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ProgressItem.java
@@ -11,9 +11,6 @@ * @see ProgressViewHolder */ class ProgressItem extends OptionalLeaf { - protected ProgressItem(NodeParent parent) { - super(parent); - } @Override @ItemViewType
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java index 2143c590..5ac8614db 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
@@ -5,6 +5,7 @@ package org.chromium.chrome.browser.ntp.cards; import org.chromium.base.Log; +import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; import org.chromium.chrome.browser.ntp.snippets.CategoryInt; import org.chromium.chrome.browser.ntp.snippets.CategoryStatus; @@ -15,7 +16,6 @@ import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; -import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -29,37 +29,23 @@ /** Maps suggestion categories to sections, with stable iteration ordering. */ private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap<>(); - private final List<TreeNode> mChildren = new ArrayList<>(); private final NewTabPageManager mNewTabPageManager; private final OfflinePageBridge mOfflinePageBridge; - public SectionList(NodeParent parent, NewTabPageManager newTabPageManager, - OfflinePageBridge offlinePageBridge) { - super(parent); + public SectionList(NewTabPageManager newTabPageManager, OfflinePageBridge offlinePageBridge) { mNewTabPageManager = newTabPageManager; mNewTabPageManager.getSuggestionsSource().setObserver(this); mOfflinePageBridge = offlinePageBridge; - } - - @Override - public void init() { - super.init(); resetSections(/* alwaysAllowEmptySections = */ false); } - @Override - protected List<TreeNode> getChildren() { - return mChildren; - } - /** * Resets the sections, reloading the whole new tab page content. * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when * they are empty, even when they are normally not. */ public void resetSections(boolean alwaysAllowEmptySections) { - mSections.clear(); - mChildren.clear(); + removeAllSections(); SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsSource(); int[] categories = suggestionsSource.getCategories(); @@ -105,10 +91,9 @@ // Create the section if needed. if (section == null) { - section = new SuggestionsSection(this, mNewTabPageManager, mOfflinePageBridge, info); + section = new SuggestionsSection(mNewTabPageManager, mOfflinePageBridge, info); mSections.put(category, section); - mChildren.add(section); - didAddChild(section); + addChild(section); } // Add the new suggestions. @@ -229,10 +214,15 @@ removeSection(section); } - private void removeSection(SuggestionsSection section) { + @VisibleForTesting + void removeSection(SuggestionsSection section) { mSections.remove(section.getCategory()); - willRemoveChild(section); - mChildren.remove(section); + removeChild(section); + } + + private void removeAllSections() { + mSections.clear(); + removeChildren(); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java index 1ad2c93..a8c1ca1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java
@@ -42,8 +42,7 @@ @Nullable private final SigninObserver mObserver; - public SignInPromo(NodeParent parent, NewTabPageManager newTabPageManager) { - super(parent); + public SignInPromo(NewTabPageManager newTabPageManager) { mDismissed = ChromePreferenceManager.getInstance(ContextUtils.getApplicationContext()) .getNewTabPageSigninPromoDismissed(); @@ -54,12 +53,7 @@ mObserver = new SigninObserver(signinManager); newTabPageManager.addDestructionObserver(mObserver); } - } - @Override - public void init() { - super.init(); - SigninManager signinManager = SigninManager.get(ContextUtils.getApplicationContext()); setVisible(signinManager.isSignInAllowed() && !signinManager.isSignedInOnNative()); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SpacingItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SpacingItem.java index c4c045d..0409f88 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SpacingItem.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SpacingItem.java
@@ -41,4 +41,9 @@ protected void onBindViewHolder(NewTabPageViewHolder holder) { // Nothing to do. } + + /** Schedules a recalculation of the space occupied by the item. */ + public void refresh() { + notifyItemChanged(0); + } } \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusItem.java index 1c684de8..c68445a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusItem.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusItem.java
@@ -14,20 +14,14 @@ * configuration that affects the NTP suggestions. */ public abstract class StatusItem extends OptionalLeaf implements StatusCardViewHolder.DataSource { - - protected StatusItem(NodeParent parent) { - super(parent); - } - - public static StatusItem createNoSuggestionsItem(SuggestionsSection parentSection) { - return new NoSuggestionsItem(parentSection); + public static StatusItem createNoSuggestionsItem(SuggestionsCategoryInfo categoryInfo) { + return new NoSuggestionsItem(categoryInfo); } private static class NoSuggestionsItem extends StatusItem { private final String mDescription; - public NoSuggestionsItem(SuggestionsSection parentSection) { - super(parentSection); - mDescription = parentSection.getCategoryInfo().getNoSuggestionsMessage(); + public NoSuggestionsItem(SuggestionsCategoryInfo categoryInfo) { + mDescription = categoryInfo.getNoSuggestionsMessage(); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java index f237cfb9..cac3245b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -20,7 +20,6 @@ import org.chromium.chrome.browser.offlinepages.OfflinePageItem; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -41,35 +40,21 @@ private final ActionItem mMoreButton; private final ProgressItem mProgressIndicator; - private final List<TreeNode> mChildren; - private boolean mIsNtpDestroyed; - public SuggestionsSection(NodeParent parent, NewTabPageManager manager, - OfflinePageBridge offlinePageBridge, SuggestionsCategoryInfo info) { - super(parent); + public SuggestionsSection(NewTabPageManager manager, OfflinePageBridge offlinePageBridge, + SuggestionsCategoryInfo info) { mCategoryInfo = info; mOfflinePageBridge = offlinePageBridge; mHeader = new SectionHeader(info.getTitle()); - mSuggestionsList = new SuggestionsList(this, info); - mStatus = StatusItem.createNoSuggestionsItem(this); + mSuggestionsList = new SuggestionsList(info); + mStatus = StatusItem.createNoSuggestionsItem(info); mMoreButton = new ActionItem(this); - mProgressIndicator = new ProgressItem(this); - - mChildren = Arrays.asList( - mHeader, - mSuggestionsList, - mStatus, - mMoreButton, - mProgressIndicator); + mProgressIndicator = new ProgressItem(); + addChildren(mHeader, mSuggestionsList, mStatus, mMoreButton, mProgressIndicator); setupOfflinePageBridgeObserver(manager); - } - - @Override - public void init() { - super.init(); refreshChildrenVisibility(); } @@ -77,8 +62,7 @@ private final List<SnippetArticle> mSuggestions = new ArrayList<>(); private final SuggestionsCategoryInfo mCategoryInfo; - public SuggestionsList(NodeParent parent, SuggestionsCategoryInfo categoryInfo) { - super(parent); + public SuggestionsList(SuggestionsCategoryInfo categoryInfo) { mCategoryInfo = categoryInfo; } @@ -140,11 +124,6 @@ } } - @Override - protected List<TreeNode> getChildren() { - return mChildren; - } - private void setupOfflinePageBridgeObserver(NewTabPageManager manager) { final OfflinePageBridge.OfflinePageModelObserver observer = new OfflinePageBridge.OfflinePageModelObserver() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java index 75997a4..6816a1a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java
@@ -4,8 +4,6 @@ package org.chromium.chrome.browser.ntp.cards; -import android.support.annotation.CallSuper; - import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; /** @@ -17,8 +15,7 @@ * node has been added to the tree, i.e. when it is in the list of its parent's children. * The node may notify its parent about changes that happen during initialization. */ - @CallSuper - void init(); + void setParent(NodeParent parent); /** * Returns the number of items under this subtree. This method may be called
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java index 2069212..3aee75e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java
@@ -42,6 +42,7 @@ @Nullable private InstrumentDetailsCallback mCallback; private boolean mIsWaitingForBillingNormalization; private boolean mIsWaitingForFullCardDetails; + private boolean mHasValidNumberAndName; /** * Builds a payment instrument for the given credit card. @@ -80,7 +81,10 @@ InstrumentDetailsCallback callback) { // The billing address should never be null for a credit card at this point. assert mBillingAddress != null; + assert AutofillAddress.checkAddressCompletionStatus(mBillingAddress) + == AutofillAddress.COMPLETE; assert mIsComplete; + assert mHasValidNumberAndName; assert mCallback == null; mCallback = callback; @@ -210,12 +214,23 @@ @Override public void dismissInstrument() {} - /** @return Whether the card is complete and ready to be sent to the merchant as-is. */ + /** + * @return Whether the card is complete and ready to be sent to the merchant as-is. If true, + * this card has a valid card number, a non-empty name on card, and a complete billing address. + */ public boolean isComplete() { return mIsComplete; } /** + * @return Whether the card number is valid and name on card is non-empty. Billing address is + * not taken into consideration. + */ + public boolean isValid() { + return mHasValidNumberAndName; + } + + /** * Updates the instrument and marks it "complete." Called after the user has edited this * instrument. * @@ -239,15 +254,19 @@ mContext.getResources(), card.getIssuerIconDrawableId())); checkAndUpateCardCompleteness(); assert mIsComplete; + assert mHasValidNumberAndName; } /** * Checks whether card is complete, i.e., can be sent to the merchant as-is without editing * first. And updates edit message, edit title and complete status. * - * For both local and server cards, verifies that the billing address is complete. For local + * For both local and server cards, verifies that the billing address is present. For local * cards also verifies that the card number is valid and the name on card is not empty. * + * Does not check that the billing address has all of the required fields. This is done + * elsewhere to filter out such billing addresses entirely. + * * Does not check the expiration date. If the card is expired, the user has the opportunity * update the expiration date when providing their CVC in the card unmask dialog. * @@ -265,8 +284,10 @@ invalidFieldsCount++; } + mHasValidNumberAndName = true; if (mCard.getIsLocal()) { if (TextUtils.isEmpty(mCard.getName())) { + mHasValidNumberAndName = false; editMessageResId = R.string.payments_name_on_card_required; editTitleResId = R.string.payments_add_name_on_card; invalidFieldsCount++; @@ -275,6 +296,7 @@ if (PersonalDataManager.getInstance().getBasicCardPaymentType( mCard.getNumber().toString(), true) == null) { + mHasValidNumberAndName = false; editMessageResId = R.string.payments_card_number_invalid; editTitleResId = R.string.payments_add_valid_card_number; invalidFieldsCount++;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java index 90a8c8e..e1c98792 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -183,7 +183,7 @@ /** * In-memory mapping of the origins of websites that have recently called canMakePayment() - * to the list of the payment methods that were been queried. Used for throttling the usage of + * to the list of the payment methods that were being queried. Used for throttling the usage of * this call. The mapping is shared among all instances of PaymentRequestImpl in the browser * process on UI thread. The user can reset the throttling mechanism by restarting the browser. */ @@ -268,6 +268,12 @@ /** True if any of the requested payment methods are supported. */ private boolean mArePaymentMethodsSupported; + /** + * True after at least one usable payment instrument has been found. Should be read only after + * all payment apps have been queried. + */ + private boolean mCanMakePayment; + /** The helper to create and fill the response to send to the merchant. */ private PaymentResponseHelper mPaymentResponseHelper; @@ -1174,10 +1180,7 @@ } query.addObserver(this); - if (mPendingApps.isEmpty() && mPendingInstruments.isEmpty()) { - query.setResponse(mPaymentMethodsSection != null - && mPaymentMethodsSection.getSelectedItem() != null); - } + if (isFinishedQueryingPaymentApps()) query.setResponse(mCanMakePayment); } private void respondCanMakePaymentQuery(boolean response) { @@ -1240,18 +1243,22 @@ if (disconnectIfNoPaymentMethodsSupported()) return; // Load the validation rules for each unique region code in the credit card billing - // addresses. + // addresses and check for validity. Set<String> uniqueCountryCodes = new HashSet<>(); for (int i = 0; i < mPendingAutofillInstruments.size(); ++i) { assert mPendingAutofillInstruments.get(i) instanceof AutofillPaymentInstrument; + AutofillPaymentInstrument creditCard = + (AutofillPaymentInstrument) mPendingAutofillInstruments.get(i); - String countryCode = AutofillAddress - .getCountryCode(((AutofillPaymentInstrument) mPendingAutofillInstruments.get( - i)).getBillingAddress()); + String countryCode = AutofillAddress.getCountryCode(creditCard.getBillingAddress()); if (!uniqueCountryCodes.contains(countryCode)) { uniqueCountryCodes.add(countryCode); PersonalDataManager.getInstance().loadRulesForRegion(countryCode); } + + // If there's a card on file with a valid number and a name, then + // PaymentRequest.canMakePayment() returns true. + mCanMakePayment |= creditCard.isValid(); } // List order: @@ -1267,18 +1274,22 @@ mPendingAutofillInstruments.clear(); - // Pre-select the first instrument on the list, if it is complete. + // Possibly pre-select the first instrument on the list. int selection = SectionInformation.NO_SELECTION; if (!mPendingInstruments.isEmpty()) { PaymentInstrument first = mPendingInstruments.get(0); - if (!(first instanceof AutofillPaymentInstrument) - || ((AutofillPaymentInstrument) first).isComplete()) { + if (first instanceof AutofillPaymentInstrument) { + AutofillPaymentInstrument creditCard = (AutofillPaymentInstrument) first; + if (creditCard.isComplete()) selection = 0; + } else { + // If a payment app is available, then PaymentRequest.canMakePayment() returns true. + mCanMakePayment = true; selection = 0; } } CanMakePaymentQuery query = sCanMakePaymentQueries.get(mOrigin); - if (query != null) query.setResponse(selection == 0); + if (query != null) query.setResponse(mCanMakePayment); // The list of payment instruments is ready to display. mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYPE_PAYMENT_METHODS, @@ -1298,10 +1309,7 @@ * @return True if no payment methods are supported */ private boolean disconnectIfNoPaymentMethodsSupported() { - if (mPendingApps == null || !mPendingApps.isEmpty() || !mPendingInstruments.isEmpty()) { - // Waiting for pending apps and instruments. - return false; - } + if (!isFinishedQueryingPaymentApps()) return false; boolean foundPaymentMethods = mPaymentMethodsSection != null && !mPaymentMethodsSection.isEmpty(); @@ -1325,6 +1333,11 @@ return false; } + /** @return True after payment apps have been queried. */ + private boolean isFinishedQueryingPaymentApps() { + return mPendingApps != null && mPendingApps.isEmpty() && mPendingInstruments.isEmpty(); + } + /** * Saves the given instrument in either "autofill" or "non-autofill" list. The separation * enables placing autofill instruments on the bottom of the list.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryNoCardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryNoCardTest.java index fd842d55..5d923ac 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryNoCardTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryNoCardTest.java
@@ -29,8 +29,8 @@ // The user has an incomplete credit card on file. This is not sufficient for // canMakePayment() to return true. new AutofillTestHelper().setCreditCard(new CreditCard("", "https://example.com", true, true, - "Jon Doe", "4111111111111111", "1111", "12", "2050", "visa", R.drawable.pr_visa, - "" /* billingAddressId */, "" /* serverId */)); + "" /* nameOnCard */, "4111111111111111", "1111", "12", "2050", "visa", + R.drawable.pr_visa, "" /* billingAddressId */, "" /* serverId */)); } @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryTest.java index b0443bd..ec5494a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryTest.java
@@ -9,15 +9,14 @@ import org.chromium.base.test.util.Feature; import org.chromium.chrome.R; import org.chromium.chrome.browser.autofill.AutofillTestHelper; -import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; /** - * A payment integration test for checking whether user can make a payment via either payment - * app or a credit card. This user has a complete credit card on file. + * A payment integration test for checking whether user can make a payment via either payment app or + * a credit card. This user has a valid credit card without a billing address on file. */ public class PaymentRequestCanMakePaymentQueryTest extends PaymentRequestTestBase { public PaymentRequestCanMakePaymentQueryTest() { @@ -27,15 +26,11 @@ @Override public void onMainActivityStarted() throws InterruptedException, ExecutionException, TimeoutException { - // The user has a complete credit card on file. This is sufficient for - // canMakePayment() to return true. - AutofillTestHelper helper = new AutofillTestHelper(); - String billingAddressId = helper.setProfile(new AutofillProfile("", "https://example.com", - true, "Jon Doe", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "", - "US", "555-555-5555", "", "en-US")); - helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe", - "4111111111111111", "1111", "12", "2050", "visa", R.drawable.pr_visa, - billingAddressId, "" /* serverId */)); + // The user has a valid credit card without a billing address on file. This is sufficient + // for canMakePayment() to return true. + new AutofillTestHelper().setCreditCard(new CreditCard("", "https://example.com", true, true, + "Jon Doe", "4111111111111111", "1111", "12", "2050", "visa", R.drawable.pr_visa, + "" /* billingAddressId */, "" /* serverId */)); } @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryNoCardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryNoCardTest.java index 6f1d3ce..9413daa 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryNoCardTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryNoCardTest.java
@@ -29,8 +29,8 @@ // The user has an incomplete credit card on file. This is not sufficient for // canMakePayment() to return true. new AutofillTestHelper().setCreditCard(new CreditCard("", "https://example.com", true, true, - "Jon Doe", "4111111111111111", "1111", "12", "2050", "visa", R.drawable.pr_visa, - "" /* billingAddressId */, "" /* serverId */)); + "" /* nameOnCard */, "4111111111111111", "1111", "12", "2050", "visa", + R.drawable.pr_visa, "" /* billingAddressId */, "" /* serverId */)); } @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryTest.java index 56dc5b06..c87d1777 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryTest.java
@@ -9,15 +9,14 @@ import org.chromium.base.test.util.Feature; import org.chromium.chrome.R; import org.chromium.chrome.browser.autofill.AutofillTestHelper; -import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; /** - * A payment integration test for checking whether user can make a payment via a credit card. - * This user has a complete credit card on file. + * A payment integration test for checking whether user can make a payment via a credit card. This + * user has a valid credit card without a billing address on file. */ public class PaymentRequestCcCanMakePaymentQueryTest extends PaymentRequestTestBase { public PaymentRequestCcCanMakePaymentQueryTest() { @@ -27,15 +26,11 @@ @Override public void onMainActivityStarted() throws InterruptedException, ExecutionException, TimeoutException { - // The user has a complete credit card on file. This is sufficient for - // canMakePayment() to return true. - AutofillTestHelper helper = new AutofillTestHelper(); - String billingAddressId = helper.setProfile(new AutofillProfile("", "https://example.com", - true, "Jon Doe", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "", - "US", "555-555-5555", "", "en-US")); - helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe", - "4111111111111111", "1111", "12", "2050", "visa", R.drawable.pr_visa, - billingAddressId, "" /* serverId */)); + // The user has a valid credit card without a billing address on file. This is sufficient + // for canMakePayment() to return true. + new AutofillTestHelper().setCreditCard(new CreditCard("", "https://example.com", true, true, + "Jon Doe", "4111111111111111", "1111", "12", "2050", "visa", R.drawable.pr_visa, + "" /* billingAddressId */, "" /* serverId */)); } @MediumTest
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java index 07e9b5e..2ba0adc 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java
@@ -6,11 +6,19 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import android.support.annotation.Nullable; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,26 +47,15 @@ @Before public void setUp() { MockitoAnnotations.initMocks(this); + mInnerNode = spy(new InnerNode()); - for (int i = 0; i < ITEM_COUNTS.length; i++) { - TreeNode child = mock(TreeNode.class); - when(child.getItemCount()).thenReturn(ITEM_COUNTS[i]); + for (int childItemCount : ITEM_COUNTS) { + TreeNode child = makeDummyNode(childItemCount); mChildren.add(child); + mInnerNode.addChild(child); } - mInnerNode = new InnerNode(mParent) { - @Override - protected List<TreeNode> getChildren() { - return mChildren; - } - }; - } - @Test - public void testInit() { - mInnerNode.init(); - for (TreeNode child : mChildren) { - verify(child).init(); - } + mInnerNode.setParent(mParent); } @Test @@ -112,37 +109,36 @@ } @Test - public void testDidAddChild() { - TreeNode child = mock(TreeNode.class); - when(child.getItemCount()).thenReturn(23); - mChildren.add(3, child); - mInnerNode.didAddChild(child); + public void testAddChild() { + final int itemCountBefore = mInnerNode.getItemCount(); - // The child should have been initialized and the parent notified about the added items. - verify(child).init(); - verify(mParent).onItemRangeInserted(mInnerNode, 6, 23); + TreeNode child = makeDummyNode(23); + mInnerNode.addChild(child); - TreeNode child2 = mock(TreeNode.class); - when(child2.getItemCount()).thenReturn(0); - mChildren.add(4, child2); - mInnerNode.didAddChild(child2); - verify(child2).init(); + // The child should have been initialized and the parent hierarchy notified about it. + verify(child).setParent(eq(mInnerNode)); + verify(mParent).onItemRangeInserted(mInnerNode, itemCountBefore, 23); + + TreeNode child2 = makeDummyNode(0); + mInnerNode.addChild(child2); // The empty child should have been initialized, but there should be no change // notifications. + verify(child2).setParent(eq(mInnerNode)); verifyNoMoreInteractions(mParent); } @Test - public void testWillRemoveChild() { - mInnerNode.willRemoveChild(mChildren.get(4)); - mChildren.remove(4); + public void testRemoveChild() { + TreeNode child = mChildren.get(4); + mInnerNode.removeChild(child); // The parent should have been notified about the removed items. verify(mParent).onItemRangeRemoved(mInnerNode, 6, 3); - mInnerNode.willRemoveChild(mChildren.get(3)); - mChildren.remove(3); + reset(mParent); // Prepare for the #verifyNoMoreInteractions() call below. + TreeNode child2 = mChildren.get(3); + mInnerNode.removeChild(child2); // There should be no change notifications about the empty child. verifyNoMoreInteractions(mParent); @@ -160,5 +156,78 @@ verify(mParent).onItemRangeChanged(mInnerNode, 6, 6502); verify(mParent).onItemRangeRemoved(mInnerNode, 11, 8086); } + + /** + * Tests that {@link ChildNode} sends the change notifications AFTER its child list is modified. + */ + @Test + public void testChangeNotificationsTiming() { + // The MockModeParent will enforce a given number of items in the child when notified. + MockNodeParent parent = spy(new MockNodeParent()); + InnerNode rootNode = new InnerNode(); + + TreeNode[] children = {makeDummyNode(3), makeDummyNode(5)}; + rootNode.addChildren(children); + rootNode.setParent(parent); + + assertThat(rootNode.getItemCount(), is(8)); + verifyZeroInteractions(parent); // Before the parent is set, no notifications. + + parent.expectItemCount(24); + rootNode.addChildren(makeDummyNode(7), makeDummyNode(9)); // Should bundle the insertions. + verify(parent).onItemRangeInserted(eq(rootNode), eq(8), eq(16)); + + parent.expectItemCount(28); + rootNode.addChild(makeDummyNode(4)); + verify(parent).onItemRangeInserted(eq(rootNode), eq(24), eq(4)); + + parent.expectItemCount(23); + rootNode.removeChild(children[1]); + verify(parent).onItemRangeRemoved(eq(rootNode), eq(3), eq(5)); + + parent.expectItemCount(0); + rootNode.removeChildren(); // Bundles the removals in a single change notification + verify(parent).onItemRangeRemoved(eq(rootNode), eq(0), eq(23)); + } + + /** + * Implementation of {@link NodeParent} that checks the item count from the node that + * sends notifications against defined expectations. Fails on unexpected calls. + */ + private static class MockNodeParent implements NodeParent { + @Nullable + private Integer mNextExpectedItemCount; + + public void expectItemCount(int count) { + mNextExpectedItemCount = count; + } + + @Override + public void onItemRangeChanged(TreeNode child, int index, int count) { + checkCount(child); + } + + @Override + public void onItemRangeInserted(TreeNode child, int index, int count) { + checkCount(child); + } + + @Override + public void onItemRangeRemoved(TreeNode child, int index, int count) { + checkCount(child); + } + + private void checkCount(TreeNode child) { + if (mNextExpectedItemCount == null) fail("Unexpected call"); + assertThat(child.getItemCount(), is(mNextExpectedItemCount)); + mNextExpectedItemCount = null; + } + } + + private static TreeNode makeDummyNode(int itemCount) { + TreeNode node = mock(TreeNode.class); + doReturn(itemCount).when(node).getItemCount(); + return node; + } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java index ad5f339..4ae471a 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
@@ -347,8 +347,8 @@ } private SuggestionsSection createSection(SuggestionsCategoryInfo info) { - SuggestionsSection section = new SuggestionsSection(mParent, mManager, mBridge, info); - section.init(); + SuggestionsSection section = new SuggestionsSection(mManager, mBridge, info); + section.setParent(mParent); return section; }
diff --git a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc index 4964b41..eb9b876e 100644 --- a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc +++ b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
@@ -190,7 +190,7 @@ base::MakeUnique<RequestCoordinator>( std::move(policy), std::move(prerenderer_offliner), std::move(queue), std::move(scheduler), network_quality_estimator); - request_coordinator->SetImmediateScheduleCallbackForTest( + request_coordinator->SetInternalStartProcessingCallbackForTest( base::Bind(&android::EvaluationTestScheduler::ImmediateScheduleCallback, base::Unretained(scheduler.get())));
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc index 59611d3a..3e54f436 100644 --- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc +++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.cc
@@ -28,23 +28,19 @@ void ArcKioskAppService::OnAppRegistered( const std::string& app_id, const ArcAppListPrefs::AppInfo& app_info) { - if (app_id == app_id_) - PreconditionsChanged(); + PreconditionsChanged(); } void ArcKioskAppService::OnAppReadyChanged(const std::string& id, bool ready) { - if (id == app_id_) - PreconditionsChanged(); + PreconditionsChanged(); } void ArcKioskAppService::OnPackageListInitialRefreshed() { // The app could already be registered. - app_id_ = GetAppId(); PreconditionsChanged(); } void ArcKioskAppService::OnArcKioskAppsChanged() { - app_id_ = GetAppId(); PreconditionsChanged(); } @@ -73,7 +69,6 @@ app_manager_ = ArcKioskAppManager::Get(); if (app_manager_) { app_manager_->AddObserver(this); - app_id_ = GetAppId(); } pref_change_registrar_.reset(new PrefChangeRegistrar()); pref_change_registrar_->Init(profile_->GetPrefs()); @@ -93,6 +88,9 @@ } void ArcKioskAppService::PreconditionsChanged() { + app_id_ = GetAppId(); + if (app_id_.empty()) + return; app_info_ = prefs_->GetApp(app_id_); if (app_info_ && app_info_->ready && profile_->GetPrefs()->GetBoolean(prefs::kArcPolicyCompliant)) {
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_unittest.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_unittest.cc index c682c2e..76aae0c 100644 --- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_unittest.cc +++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_unittest.cc
@@ -9,7 +9,6 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/scoped_task_scheduler.h" #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.h" #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" @@ -337,7 +336,6 @@ const AccountId test_account_id_ = AccountId::FromUserEmail(kTestUserId); private: - base::test::ScopedTaskScheduler scoped_task_scheduler_; content::TestBrowserThreadBundle thread_bundle_; // The NSS system slot used by EasyUnlockTPMKeyManagers in tests.
diff --git a/chrome/browser/chromeos/net/client_cert_store_chromeos_unittest.cc b/chrome/browser/chromeos/net/client_cert_store_chromeos_unittest.cc index bbd7973..3621e9e 100644 --- a/chrome/browser/chromeos/net/client_cert_store_chromeos_unittest.cc +++ b/chrome/browser/chromeos/net/client_cert_store_chromeos_unittest.cc
@@ -11,7 +11,6 @@ #include "base/location.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/test/scoped_task_scheduler.h" @@ -90,7 +89,6 @@ private: base::test::ScopedTaskScheduler scoped_task_scheduler_; - base::MessageLoopForIO message_loop_; }; // Ensure that cert requests, that are started before the filter is initialized,
diff --git a/chrome/browser/extensions/api/messaging/message_service.cc b/chrome/browser/extensions/api/messaging/message_service.cc index 2dda966..b77fbdee 100644 --- a/chrome/browser/extensions/api/messaging/message_service.cc +++ b/chrome/browser/extensions/api/messaging/message_service.cc
@@ -566,11 +566,6 @@ channel->receiver.reset(params->receiver.release()); AddChannel(std::move(channel_ptr), params->receiver_port_id); - // TODO(robwu): Could |guest_process_id| and |guest_render_frame_routing_id| - // be removed? In the past extension message routing was process-based, but - // now that extensions are routed from a specific RFH, the special casing for - // guest views seems no longer necessary, because the ExtensionMessagePort can - // simply obtain the source process & frame ID directly from the RFH. int guest_process_id = content::ChildProcessHost::kInvalidUniqueID; int guest_render_frame_routing_id = MSG_ROUTING_NONE; if (params->include_guest_process_info) {
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc index 85b7d80..310aee3 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor.cc
@@ -497,6 +497,12 @@ prefetch_manager_, url)); } +void ResourcePrefetchPredictor::OnPrefetchingFinished( + const GURL& main_frame_url) { + if (observer_) + observer_->OnPrefetchingFinished(main_frame_url); +} + void ResourcePrefetchPredictor::SetObserverForTesting(TestObserver* observer) { observer_ = observer; }
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.h b/chrome/browser/predictors/resource_prefetch_predictor.h index d2bbee3..a7ce8bf 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor.h +++ b/chrome/browser/predictors/resource_prefetch_predictor.h
@@ -160,6 +160,10 @@ // |main_frame_url|. void StopPrefetching(const GURL& main_frame_url); + // Called when ResourcePrefetcher is finished, i.e. there is nothing pending + // in flight. + void OnPrefetchingFinished(const GURL& main_frame_url); + // Sets the |observer| to be notified when the resource prefetch predictor // data changes. Previously registered observer will be discarded. Call // this with nullptr parameter to de-register observer. @@ -367,6 +371,8 @@ size_t url_visit_count, const ResourcePrefetchPredictor::PageRequestSummary& summary) {} + virtual void OnPrefetchingFinished(const GURL& main_frame_url) {} + virtual void OnPredictorInitialized() {} protected:
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc index c9ab1bf..70c3131 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
@@ -8,6 +8,7 @@ #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" @@ -73,6 +74,9 @@ GURL url; }; +// Helper class to track and allow waiting for ResourcePrefetchPredictor +// initialization. WARNING: OnPredictorInitialized event will not be fired if +// ResourcePrefetchPredictor is initialized before the observer creation. class InitializationObserver : public TestObserver { public: explicit InitializationObserver(ResourcePrefetchPredictor* predictor) @@ -145,18 +149,17 @@ } // namespace -// Helper class to track and allow waiting for ResourcePrefetchPredictor events. -// These events are also used to verify that ResourcePrefetchPredictor works as -// expected. -class ResourcePrefetchPredictorTestObserver : public TestObserver { +// Helper class to track and allow waiting for a single OnNavigationLearned +// event. The information provided by this event is also used to verify that +// ResourcePrefetchPredictor works as expected. +class LearningObserver : public TestObserver { public: using PageRequestSummary = ResourcePrefetchPredictor::PageRequestSummary; - explicit ResourcePrefetchPredictorTestObserver( - ResourcePrefetchPredictor* predictor, - const size_t expected_url_visit_count, - const PageRequestSummary& expected_summary, - bool match_navigation_id) + LearningObserver(ResourcePrefetchPredictor* predictor, + const size_t expected_url_visit_count, + const PageRequestSummary& expected_summary, + bool match_navigation_id) : TestObserver(predictor), url_visit_count_(expected_url_visit_count), summary_(expected_summary), @@ -181,7 +184,35 @@ PageRequestSummary summary_; bool match_navigation_id_; - DISALLOW_COPY_AND_ASSIGN(ResourcePrefetchPredictorTestObserver); + DISALLOW_COPY_AND_ASSIGN(LearningObserver); +}; + +// Helper class to track and allow waiting for a single OnPrefetchingFinished +// event. No learning events should be fired while this observer is active. +class PrefetchingObserver : public TestObserver { + public: + PrefetchingObserver(ResourcePrefetchPredictor* predictor, + const GURL& expected_main_frame_url) + : TestObserver(predictor), main_frame_url_(expected_main_frame_url) {} + + // TestObserver: + void OnNavigationLearned(size_t url_visit_count, + const PageRequestSummary& summary) override { + ADD_FAILURE() << "Prefetching shouldn't activate learning"; + } + + void OnPrefetchingFinished(const GURL& main_frame_url) override { + EXPECT_EQ(main_frame_url_, main_frame_url); + run_loop_.Quit(); + } + + void Wait() { run_loop_.Run(); } + + private: + base::RunLoop run_loop_; + GURL main_frame_url_; + + DISALLOW_COPY_AND_ASSIGN(PrefetchingObserver); }; class ResourcePrefetchPredictorBrowserTest : public InProcessBrowserTest { @@ -208,6 +239,19 @@ EnsurePredictorInitialized(); } + void TestLearningAndPrefetching(const GURL& main_frame_url) { + // Navigate to |main_frame_url| and check all the expectations. + NavigateToURLAndCheckSubresources(main_frame_url); + ClearCache(); + // It is needed to have at least two resource hits to trigger prefetch. + NavigateToURLAndCheckSubresources(main_frame_url); + ClearCache(); + // Prefetch all needed resources and change expectations so that all + // cacheable resources should be served from cache next navigation. + PrefetchURL(main_frame_url); + NavigateToURLAndCheckSubresources(main_frame_url); + } + void NavigateToURLAndCheckSubresources( const GURL& main_frame_url, WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB) { @@ -219,7 +263,7 @@ url_request_summaries.push_back( GetURLRequestSummaryForResource(endpoint_url, kv.second)); } - ResourcePrefetchPredictorTestObserver observer( + LearningObserver observer( predictor_, UpdateAndGetVisitCount(main_frame_url), CreatePageRequestSummary(endpoint_url.spec(), main_frame_url.spec(), url_request_summaries), @@ -228,6 +272,20 @@ browser(), main_frame_url, disposition, ui_test_utils::BROWSER_TEST_NONE); observer.Wait(); + for (auto& kv : resources_) { + if (!kv.second.is_no_store && kv.second.should_be_recorded) + kv.second.request.was_cached = true; + } + } + + void PrefetchURL(const GURL& main_frame_url) { + PrefetchingObserver observer(predictor_, main_frame_url); + predictor_->StartPrefetching(main_frame_url, PrefetchOrigin::EXTERNAL); + observer.Wait(); + for (auto& kv : resources_) { + if (!kv.second.is_no_store && kv.second.should_be_recorded) + kv.second.request.was_cached = true; + } } ResourceSummary* AddResource(const GURL& resource_url, @@ -273,6 +331,12 @@ } } + void ClearCache() { + chrome::ClearCache(browser()); + for (auto& kv : resources_) + kv.second.request.was_cached = false; + } + // Shortcut for convenience. GURL GetURL(const std::string& path) const { return embedded_test_server()->GetURL(path); @@ -397,7 +461,7 @@ std::map<GURL, size_t> visit_count_; }; -IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, LearningSimple) { +IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, Simple) { // These resources have default priorities that correspond to // blink::typeToPriority function. AddResource(GetURL(kImagePath), content::RESOURCE_TYPE_IMAGE, net::LOWEST); @@ -406,11 +470,10 @@ AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM); AddResource(GetURL(kFontPath), content::RESOURCE_TYPE_FONT_RESOURCE, net::HIGHEST); - NavigateToURLAndCheckSubresources(GetURL(kHtmlSubresourcesPath)); + TestLearningAndPrefetching(GetURL(kHtmlSubresourcesPath)); } -IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, - LearningAfterRedirect) { +IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, Redirect) { AddRedirectChain(GetURL(kRedirectPath), {{net::HTTP_MOVED_PERMANENTLY, GetURL(kHtmlSubresourcesPath)}}); AddResource(GetURL(kImagePath), content::RESOURCE_TYPE_IMAGE, net::LOWEST); @@ -419,11 +482,10 @@ AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM); AddResource(GetURL(kFontPath), content::RESOURCE_TYPE_FONT_RESOURCE, net::HIGHEST); - NavigateToURLAndCheckSubresources(GetURL(kRedirectPath)); + TestLearningAndPrefetching(GetURL(kRedirectPath)); } -IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, - LearningAfterRedirectChain) { +IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, RedirectChain) { AddRedirectChain(GetURL(kRedirectPath), {{net::HTTP_FOUND, GetURL(kRedirectPath2)}, {net::HTTP_MOVED_PERMANENTLY, GetURL(kRedirectPath3)}, @@ -434,15 +496,14 @@ AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM); AddResource(GetURL(kFontPath), content::RESOURCE_TYPE_FONT_RESOURCE, net::HIGHEST); - NavigateToURLAndCheckSubresources(GetURL(kRedirectPath)); + TestLearningAndPrefetching(GetURL(kRedirectPath)); } IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, LearningAfterHttpToHttpsRedirect) { EnableHttpsServer(); AddRedirectChain(GetURL(kRedirectPath), - {{net::HTTP_FOUND, https_server()->GetURL(kRedirectPath2)}, - {net::HTTP_MOVED_PERMANENTLY, + {{net::HTTP_MOVED_PERMANENTLY, https_server()->GetURL(kHtmlSubresourcesPath)}}); AddResource(https_server()->GetURL(kImagePath), content::RESOURCE_TYPE_IMAGE, net::LOWEST); @@ -453,10 +514,12 @@ AddResource(https_server()->GetURL(kFontPath), content::RESOURCE_TYPE_FONT_RESOURCE, net::HIGHEST); NavigateToURLAndCheckSubresources(GetURL(kRedirectPath)); + // TODO(alexilin): Test learning and prefetching once crbug.com/650246 is + // fixed. } IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, - LearningJavascriptDocumentWrite) { + JavascriptDocumentWrite) { auto externalScript = AddExternalResource(GetURL(kScriptDocumentWritePath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM); @@ -465,11 +528,11 @@ AddResource(GetURL(kStylePath), content::RESOURCE_TYPE_STYLESHEET, net::HIGHEST); AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM); - NavigateToURLAndCheckSubresources(GetURL(kHtmlDocumentWritePath)); + TestLearningAndPrefetching(GetURL(kHtmlDocumentWritePath)); } IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, - LearningJavascriptAppendChild) { + JavascriptAppendChild) { auto externalScript = AddExternalResource(GetURL(kScriptAppendChildPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM); @@ -479,11 +542,11 @@ net::HIGHEST); // This script has net::LOWEST priority because it's executed asynchronously. AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT, net::LOWEST); - NavigateToURLAndCheckSubresources(GetURL(kHtmlAppendChildPath)); + TestLearningAndPrefetching(GetURL(kHtmlAppendChildPath)); } IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, - LearningJavascriptInnerHtml) { + JavascriptInnerHtml) { auto externalScript = AddExternalResource( GetURL(kScriptInnerHtmlPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM); externalScript->request.mime_type = kJavascriptMime; @@ -493,13 +556,12 @@ // https://www.w3.org/TR/2014/REC-html5-20141028/scripting-1.html#the-script-element // Script elements don't execute when inserted using innerHTML attribute. AddUnrecordedResources({GetURL(kScriptPath)}); - NavigateToURLAndCheckSubresources(GetURL(kHtmlInnerHtmlPath)); + TestLearningAndPrefetching(GetURL(kHtmlInnerHtmlPath)); } // Requests originated by XMLHttpRequest have content::RESOURCE_TYPE_XHR. // Actual resource type is inferred from the mime-type. -IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, - LearningJavascriptXHR) { +IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, JavascriptXHR) { auto externalScript = AddExternalResource( GetURL(kScriptXHRPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM); externalScript->request.mime_type = kJavascriptMime; @@ -512,12 +574,12 @@ auto script = AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT, net::HIGHEST); script->request.mime_type = kJavascriptMime; - NavigateToURLAndCheckSubresources(GetURL(kHtmlXHRPath)); + TestLearningAndPrefetching(GetURL(kHtmlXHRPath)); } // ResourcePrefetchPredictor ignores all resources requested from subframes. IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, - LearningWithIframe) { + IframeShouldBeIgnored) { // Included from html_iframe.html. AddResource(GetURL(kImagePath2), content::RESOURCE_TYPE_IMAGE, net::LOWEST); AddResource(GetURL(kStylePath2), content::RESOURCE_TYPE_STYLESHEET, @@ -526,7 +588,7 @@ // Included from <iframe src="html_subresources.html"> and not recored. AddUnrecordedResources({GetURL(kImagePath), GetURL(kStylePath), GetURL(kScriptPath), GetURL(kFontPath)}); - NavigateToURLAndCheckSubresources(GetURL(kHtmlIframePath)); + TestLearningAndPrefetching(GetURL(kHtmlIframePath)); } } // namespace predictors
diff --git a/chrome/browser/predictors/resource_prefetcher_manager.cc b/chrome/browser/predictors/resource_prefetcher_manager.cc index 476ba79..7130038 100644 --- a/chrome/browser/predictors/resource_prefetcher_manager.cc +++ b/chrome/browser/predictors/resource_prefetcher_manager.cc
@@ -82,7 +82,13 @@ ResourcePrefetcher* resource_prefetcher) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - const std::string key = resource_prefetcher->main_frame_url().host(); + const GURL& main_frame_url = resource_prefetcher->main_frame_url(); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ResourcePrefetchPredictor::OnPrefetchingFinished, + base::Unretained(predictor_), main_frame_url)); + + const std::string key = main_frame_url.host(); auto it = prefetcher_map_.find(key); DCHECK(it != prefetcher_map_.end()); prefetcher_map_.erase(it);
diff --git a/components/BUILD.gn b/components/BUILD.gn index bfe31f2..1c1eecf 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn
@@ -138,7 +138,7 @@ "//components/sync_bookmarks:unit_tests", "//components/sync_preferences:unit_tests", "//components/sync_sessions:unit_tests", - "//components/task_scheduler_util/initialization:unit_tests", + "//components/task_scheduler_util/common:unit_tests", "//components/task_scheduler_util/variations:unit_tests", "//components/test:test_support", "//components/translate/core/browser:unit_tests",
diff --git a/components/cronet/OWNERS b/components/cronet/OWNERS index cc03ecf..078e99e 100644 --- a/components/cronet/OWNERS +++ b/components/cronet/OWNERS
@@ -1,5 +1,6 @@ kapishnikov@chromium.org mef@chromium.org mmenke@chromium.org +mgersh@chromium.org pauljensen@chromium.org xunjieli@chromium.org
diff --git a/components/offline_pages/core/background/request_coordinator.cc b/components/offline_pages/core/background/request_coordinator.cc index e25b4b09..f098edf 100644 --- a/components/offline_pages/core/background/request_coordinator.cc +++ b/components/offline_pages/core/background/request_coordinator.cc
@@ -178,7 +178,7 @@ active_request_(nullptr), last_offlining_status_(Offliner::RequestStatus::UNKNOWN), scheduler_callback_(base::Bind(&EmptySchedulerCallback)), - immediate_schedule_callback_(base::Bind(&EmptySchedulerCallback)), + internal_start_processing_callback_(base::Bind(&EmptySchedulerCallback)), weak_ptr_factory_(this) { DCHECK(policy_ != nullptr); std::unique_ptr<CleanupTaskFactory> cleanup_factory( @@ -529,6 +529,18 @@ device_conditions, callback); } +// Returns true if the caller should expect a callback, false otherwise. +bool RequestCoordinator::StartImmediateProcessing( + const DeviceConditions& device_conditions, + const base::Callback<void(bool)>& callback) { + OfflinerImmediateStartStatus immediate_start_status = + TryImmediateStart(device_conditions, callback); + UMA_HISTOGRAM_ENUMERATION( + "OfflinePages.Background.ImmediateStartStatus", immediate_start_status, + RequestCoordinator::OfflinerImmediateStartStatus::STATUS_COUNT); + return immediate_start_status == OfflinerImmediateStartStatus::STARTED; +} + bool RequestCoordinator::StartProcessingInternal( const ProcessingWindowState processing_state, const DeviceConditions& device_conditions, @@ -549,14 +561,18 @@ } void RequestCoordinator::StartImmediatelyIfConnected() { - OfflinerImmediateStartStatus immediate_start_status = TryImmediateStart(); - UMA_HISTOGRAM_ENUMERATION( - "OfflinePages.Background.ImmediateStartStatus", immediate_start_status, - RequestCoordinator::OfflinerImmediateStartStatus::STATUS_COUNT); + // Start processing with manufactured conservative battery conditions + // (i.e., assume no battery). + // TODO(dougarnett): Obtain actual battery conditions (from Android/Java). + DeviceConditions device_conditions(false, 0, GetConnectionType()); + StartImmediateProcessing(device_conditions, + internal_start_processing_callback_); } RequestCoordinator::OfflinerImmediateStartStatus -RequestCoordinator::TryImmediateStart() { +RequestCoordinator::TryImmediateStart( + const DeviceConditions& device_conditions, + const base::Callback<void(bool)>& callback) { DVLOG(2) << "Immediate " << __func__; // Make sure not already busy processing. if (is_busy_) @@ -567,11 +583,11 @@ !offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled()) { DVLOG(2) << "low end device, returning"; // Let the scheduler know we are done processing and failed due to svelte. - immediate_schedule_callback_.Run(false); + callback.Run(false); return OfflinerImmediateStartStatus::NOT_STARTED_ON_SVELTE; } - if (GetConnectionType() == + if (device_conditions.GetNetConnectionType() == net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE) { RequestConnectedEventForStarting(); return OfflinerImmediateStartStatus::NO_CONNECTION; @@ -581,13 +597,8 @@ ClearConnectedEventRequest(); } - // Start processing with manufactured conservative battery conditions - // (i.e., assume no battery). - // TODO(dougarnett): Obtain actual battery conditions (from Android/Java). - - DeviceConditions device_conditions(false, 0, GetConnectionType()); if (StartProcessingInternal(ProcessingWindowState::IMMEDIATE_WINDOW, - device_conditions, immediate_schedule_callback_)) + device_conditions, callback)) return OfflinerImmediateStartStatus::STARTED; else return OfflinerImmediateStartStatus::NOT_ACCEPTED;
diff --git a/components/offline_pages/core/background/request_coordinator.h b/components/offline_pages/core/background/request_coordinator.h index 1875759d..854a313 100644 --- a/components/offline_pages/core/background/request_coordinator.h +++ b/components/offline_pages/core/background/request_coordinator.h
@@ -110,6 +110,19 @@ bool StartScheduledProcessing(const DeviceConditions& device_conditions, const base::Callback<void(bool)>& callback); + // Attempts to starts processing of one or more queued save page later + // requests (if device conditions are suitable) in immediate mode + // (opposed to scheduled background mode). This method is suitable to call + // when there is some user action that suggests the user wants to do this + // operation now, if possible, vs. trying to do it in the background when + // idle. + // Returns whether processing was started and that caller should expect + // a callback. If processing was already active or some condition was + // not suitable for immediate processing (e.g., network or low-end device), + // returns false. + bool StartImmediateProcessing(const DeviceConditions& device_conditions, + const base::Callback<void(bool)>& callback); + // Stops the current request processing if active. This is a way for // caller to abort processing; otherwise, processing will complete on // its own. In either case, the callback will be called when processing @@ -133,12 +146,12 @@ scheduler_callback_ = callback; } - // A way to set the callback which would be called if the request will be - // scheduled immediately. Used by testing harness to determine if a request - // has been processed. - void SetImmediateScheduleCallbackForTest( + // A way to set the callback which would be called if processing will be + // triggered immediately internally by the coordinator. Used by testing + // harness to determine if a request has been processed. + void SetInternalStartProcessingCallbackForTest( const base::Callback<void(bool)> callback) { - immediate_schedule_callback_ = callback; + internal_start_processing_callback_ = callback; } void StartImmediatelyForTest() { StartImmediatelyIfConnected(); } @@ -255,7 +268,9 @@ // as to other device conditions). void StartImmediatelyIfConnected(); - OfflinerImmediateStartStatus TryImmediateStart(); + OfflinerImmediateStartStatus TryImmediateStart( + const DeviceConditions& device_conditions, + const base::Callback<void(bool)>& callback); // Requests a callback upon the next network connection to start processing. void RequestConnectedEventForStarting(); @@ -408,14 +423,20 @@ // A set of request_ids that we are holding off until the download manager is // done with them. std::set<int64_t> disabled_requests_; - // Calling this returns to the scheduler across the JNI bridge. + // The processing callback to call when processing the current processing + // window stops. It is set from the Start*Processing() call that triggered + // the processing or it may be the |internal_start_processing_callback_| if + // processing was triggered internally. + // For StartScheduledProcessing() processing, calling its callback returns + // to the scheduler across the JNI bridge. base::Callback<void(bool)> scheduler_callback_; + // Callback invoked when internally triggered processing is done. It is + // kept as a class member so that it may be overridden for test visibility. + base::Callback<void(bool)> internal_start_processing_callback_; // Logger to record events. RequestCoordinatorEventLogger event_logger_; // Timer to watch for pre-render attempts running too long. base::OneShotTimer watchdog_timer_; - // Callback invoked when an immediate request is done (default empty). - base::Callback<void(bool)> immediate_schedule_callback_; // Used for potential immediate processing when we get network connection. std::unique_ptr<ConnectionNotifier> connection_notifier_; // Allows us to pass a weak pointer to callbacks.
diff --git a/components/offline_pages/core/background/request_coordinator_unittest.cc b/components/offline_pages/core/background/request_coordinator_unittest.cc index 1c26bef..1114bf6 100644 --- a/components/offline_pages/core/background/request_coordinator_unittest.cc +++ b/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -119,10 +119,10 @@ bool is_starting() { return coordinator_->is_starting(); } - // Empty callback function. - void ImmediateScheduleCallbackFunction(bool result) { - immediate_schedule_callback_called_ = true; - immediate_schedule_callback_result_ = result; + // Test processing callback function. + void ProcessingCallbackFunction(bool result) { + processing_callback_called_ = true; + processing_callback_result_ = result; } // Callback function which releases a wait for it. @@ -249,17 +249,17 @@ DeviceConditions device_conditions() { return device_conditions_; } - base::Callback<void(bool)> immediate_callback() { - return immediate_callback_; + base::Callback<void(bool)> processing_callback() { + return processing_callback_; } base::Callback<void(bool)> waiting_callback() { return waiting_callback_; } - bool immediate_schedule_callback_called() const { - return immediate_schedule_callback_called_; + bool processing_callback_called() const { + return processing_callback_called_; } - bool immediate_schedule_callback_result() const { - return immediate_schedule_callback_result_; + bool processing_callback_result() const { + return processing_callback_result_; } const base::HistogramTester& histograms() const { return histogram_tester_; } @@ -275,10 +275,10 @@ OfflinerStub* offliner_; base::WaitableEvent waiter_; ObserverStub observer_; - bool immediate_schedule_callback_called_; - bool immediate_schedule_callback_result_; + bool processing_callback_called_; + bool processing_callback_result_; DeviceConditions device_conditions_; - base::Callback<void(bool)> immediate_callback_; + base::Callback<void(bool)> processing_callback_; base::Callback<void(bool)> waiting_callback_; base::HistogramTester histogram_tester_; }; @@ -290,8 +290,8 @@ offliner_(nullptr), waiter_(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED), - immediate_schedule_callback_called_(false), - immediate_schedule_callback_result_(false), + processing_callback_called_(false), + processing_callback_result_(false), device_conditions_(!kPowerRequired, kBatteryPercentageHigh, net::NetworkChangeNotifier::CONNECTION_3G) {} @@ -314,8 +314,8 @@ std::move(scheduler_stub), network_quality_estimator_.get())); coordinator_->AddObserver(&observer_); SetNetworkConnected(true); - immediate_callback_ = - base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + processing_callback_ = + base::Bind(&RequestCoordinatorTest::ProcessingCallbackFunction, base::Unretained(this)); // Override the normal immediate callback with a wait releasing callback. waiting_callback_ = base::Bind( @@ -362,7 +362,7 @@ // Override the processing callback for test visiblity. base::Callback<void(bool)> callback = - base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction, + base::Bind(&RequestCoordinatorTest::ProcessingCallbackFunction, base::Unretained(this)); coordinator()->SetProcessingCallbackForTest(callback); @@ -399,10 +399,10 @@ TEST_F(RequestCoordinatorTest, StartScheduledProcessingWithNoRequests) { EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(), - immediate_callback())); + processing_callback())); PumpLoop(); - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); // Verify queue depth UMA for starting scheduled processing on empty queue. if (base::SysInfo::IsLowEndDevice()) { @@ -428,22 +428,90 @@ // Sending the request to the offliner should make it busy. EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(), - immediate_callback())); + processing_callback())); PumpLoop(); EXPECT_TRUE(is_busy()); // Since the offliner is disabled, this callback should not be called. - EXPECT_FALSE(immediate_schedule_callback_called()); + EXPECT_FALSE(processing_callback_called()); - // Now trying to start processing on another request should return false. + // Now trying to start processing should return false since already busy. EXPECT_FALSE(coordinator()->StartScheduledProcessing(device_conditions(), - immediate_callback())); + processing_callback())); +} + +TEST_F(RequestCoordinatorTest, StartImmediateProcessingWithNoRequests) { + // Ensure not low-end device so immediate start can happen. + SetIsLowEndDeviceForTest(false); + + EXPECT_TRUE(coordinator()->StartImmediateProcessing(device_conditions(), + processing_callback())); + PumpLoop(); + + EXPECT_TRUE(processing_callback_called()); + + histograms().ExpectBucketCount("OfflinePages.Background.ImmediateStartStatus", + 0 /* STARTED */, 1); +} + +TEST_F(RequestCoordinatorTest, StartImmediateProcessingOnSvelte) { + // Set as low-end device to verfiy immediate processing will not start. + SetIsLowEndDeviceForTest(true); + + EXPECT_FALSE(coordinator()->StartImmediateProcessing(device_conditions(), + processing_callback())); + histograms().ExpectBucketCount("OfflinePages.Background.ImmediateStartStatus", + 5 /* NOT_STARTED_ON_SVELTE */, 1); +} + +TEST_F(RequestCoordinatorTest, StartImmediateProcessingWhenDisconnected) { + // Ensure not low-end device so immediate start can happen. + SetIsLowEndDeviceForTest(false); + + DeviceConditions disconnected_conditions( + !kPowerRequired, kBatteryPercentageHigh, + net::NetworkChangeNotifier::CONNECTION_NONE); + EXPECT_FALSE(coordinator()->StartImmediateProcessing(disconnected_conditions, + processing_callback())); + histograms().ExpectBucketCount("OfflinePages.Background.ImmediateStartStatus", + 3 /* NO_CONNECTION */, 1); +} + +TEST_F(RequestCoordinatorTest, StartImmediateProcessingWithRequestInProgress) { + // Ensure not low-end device so immediate start can happen. + SetIsLowEndDeviceForTest(false); + + // Start processing for this request. + EXPECT_NE(coordinator()->SavePageLater( + kUrl1, kClientId1, kUserRequested, + RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), + 0); + + // Disable the automatic offliner callback. + EnableOfflinerCallback(false); + + // Sending the request to the offliner should make it busy. + EXPECT_TRUE(coordinator()->StartImmediateProcessing(device_conditions(), + processing_callback())); + PumpLoop(); + + EXPECT_TRUE(is_busy()); + // Since the offliner is disabled, this callback should not be called. + EXPECT_FALSE(processing_callback_called()); + + // Now trying to start processing should return false since already busy. + EXPECT_FALSE(coordinator()->StartImmediateProcessing(device_conditions(), + processing_callback())); + + histograms().ExpectBucketCount("OfflinePages.Background.ImmediateStartStatus", + 1 /* BUSY */, 1); } TEST_F(RequestCoordinatorTest, SavePageLater) { // The user-requested request which gets processed by SavePageLater // would invoke user request callback. - coordinator()->SetImmediateScheduleCallbackForTest(immediate_callback()); + coordinator()->SetInternalStartProcessingCallbackForTest( + processing_callback()); EXPECT_NE(coordinator()->SavePageLater( kUrl1, kClientId1, kUserRequested, @@ -456,7 +524,7 @@ // Wait for callbacks to finish, both request queue and offliner. PumpLoop(); - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); // Check the request queue is as expected. EXPECT_EQ(1UL, last_requests().size()); @@ -489,7 +557,8 @@ TEST_F(RequestCoordinatorTest, SavePageLaterFailed) { // The user-requested request which gets processed by SavePageLater // would invoke user request callback. - coordinator()->SetImmediateScheduleCallbackForTest(immediate_callback()); + coordinator()->SetInternalStartProcessingCallbackForTest( + processing_callback()); EXPECT_TRUE( coordinator()->SavePageLater( @@ -505,11 +574,11 @@ // On low-end devices the callback will be called with false since the // processing started but failed due to svelte devices. - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); if (base::SysInfo::IsLowEndDevice()) { - EXPECT_FALSE(immediate_schedule_callback_result()); + EXPECT_FALSE(processing_callback_result()); } else { - EXPECT_TRUE(immediate_schedule_callback_result()); + EXPECT_TRUE(processing_callback_result()); } // Check the request queue is as expected. @@ -540,7 +609,7 @@ // for callbacks. SendOfflinerDoneCallback(request, Offliner::RequestStatus::SAVED); PumpLoop(); - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); // Verify the request gets removed from the queue, and wait for callbacks. coordinator()->queue()->GetRequests(base::Bind( @@ -575,7 +644,7 @@ // for callbacks. SendOfflinerDoneCallback(request, Offliner::RequestStatus::SAVED); PumpLoop(); - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); // Verify not busy with 2nd request (since no connection). EXPECT_FALSE(is_busy()); @@ -607,7 +676,7 @@ // For retriable failure, processing should continue to 2nd request so // no scheduler callback yet. - EXPECT_FALSE(immediate_schedule_callback_called()); + EXPECT_FALSE(processing_callback_called()); // Busy processing 2nd request. EXPECT_TRUE(is_busy()); @@ -646,7 +715,7 @@ // For no retry failure, processing should continue to 2nd request so // no scheduler callback yet. - EXPECT_FALSE(immediate_schedule_callback_called()); + EXPECT_FALSE(processing_callback_called()); // Busy processing 2nd request. EXPECT_TRUE(is_busy()); @@ -683,7 +752,7 @@ // For no next failure, processing should not continue to 2nd request so // expect scheduler callback. - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); // Not busy for NO_NEXT failure. EXPECT_FALSE(is_busy()); @@ -707,7 +776,7 @@ SendOfflinerDoneCallback(request, Offliner::RequestStatus::FOREGROUND_CANCELED); PumpLoop(); - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); // Verify the request is not removed from the queue, and wait for callbacks. coordinator()->queue()->GetRequests(base::Bind( @@ -730,7 +799,7 @@ // for callbacks. SendOfflinerDoneCallback(request, Offliner::RequestStatus::LOADING_CANCELED); PumpLoop(); - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); // Verify the request is not removed from the queue, and wait for callbacks. coordinator()->queue()->GetRequests(base::Bind( @@ -749,7 +818,7 @@ // we should make a scheduler entry for a non-user requested item. TEST_F(RequestCoordinatorTest, RequestNotPickedDisabledItemsRemain) { coordinator()->StartScheduledProcessing(device_conditions(), - immediate_callback()); + processing_callback()); EXPECT_TRUE(is_starting()); // Call RequestNotPicked, simulating a request on the disabled list. @@ -770,7 +839,7 @@ // we should make a scheduler entry for a non-user requested item. TEST_F(RequestCoordinatorTest, RequestNotPickedNonUserRequestedItemsRemain) { coordinator()->StartScheduledProcessing(device_conditions(), - immediate_callback()); + processing_callback()); EXPECT_TRUE(is_starting()); // Call RequestNotPicked, and make sure we pick schedule a task for non user @@ -779,7 +848,7 @@ PumpLoop(); EXPECT_FALSE(is_starting()); - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); // The scheduler should have been called to schedule the non-user requested // task. @@ -833,11 +902,11 @@ DisableLoading(); EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(), - immediate_callback())); + processing_callback())); // Let the async callbacks in the request coordinator run. PumpLoop(); - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); EXPECT_FALSE(is_starting()); EXPECT_EQ(Offliner::LOADING_NOT_STARTED, last_offlining_status()); @@ -852,7 +921,7 @@ PumpLoop(); EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(), - immediate_callback())); + processing_callback())); EXPECT_TRUE(is_starting()); // Now, quick, before it can do much (we haven't called PumpLoop), cancel it. @@ -860,7 +929,7 @@ // Let the async callbacks in the request coordinator run. PumpLoop(); - EXPECT_TRUE(immediate_schedule_callback_called()); + EXPECT_TRUE(processing_callback_called()); EXPECT_FALSE(is_starting()); @@ -884,7 +953,7 @@ EnableOfflinerCallback(false); EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(), - immediate_callback())); + processing_callback())); EXPECT_TRUE(is_starting()); // Let all the async parts of the start processing pipeline run to completion. @@ -896,7 +965,7 @@ observer().Clear(); // Since the offliner is disabled, this callback should not be called. - EXPECT_FALSE(immediate_schedule_callback_called()); + EXPECT_FALSE(processing_callback_called()); // Coordinator should now be busy. EXPECT_TRUE(is_busy()); @@ -935,12 +1004,12 @@ EnableOfflinerCallback(false); EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(), - immediate_callback())); + processing_callback())); // Let all the async parts of the start processing pipeline run to completion. PumpLoop(); // Since the offliner is disabled, this callback should not be called. - EXPECT_FALSE(immediate_schedule_callback_called()); + EXPECT_FALSE(processing_callback_called()); // Remove the request while it is processing. std::vector<int64_t> request_ids{kRequestId1};
diff --git a/components/task_scheduler_util/browser/BUILD.gn b/components/task_scheduler_util/browser/BUILD.gn new file mode 100644 index 0000000..502d832 --- /dev/null +++ b/components/task_scheduler_util/browser/BUILD.gn
@@ -0,0 +1,17 @@ +# 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. + +static_library("browser") { + sources = [ + "initialization.cc", + "initialization.h", + ] + + deps = [ + "//base", + "//base:base_static", + "//components/task_scheduler_util/common", + "//components/variations", + ] +}
diff --git a/components/task_scheduler_util/browser/DEPS b/components/task_scheduler_util/browser/DEPS new file mode 100644 index 0000000..80f6f90 --- /dev/null +++ b/components/task_scheduler_util/browser/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+components/variations", +]
diff --git a/components/task_scheduler_util/browser/initialization.cc b/components/task_scheduler_util/browser/initialization.cc new file mode 100644 index 0000000..3f1624b9 --- /dev/null +++ b/components/task_scheduler_util/browser/initialization.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 "components/task_scheduler_util/browser/initialization.h" + +#include <map> +#include <string> + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/task_scheduler/switches.h" +#include "base/task_scheduler/task_traits.h" +#include "base/threading/platform_thread.h" +#include "base/threading/sequenced_worker_pool.h" +#include "components/task_scheduler_util/common/variations_util.h" +#include "components/variations/variations_associated_data.h" + +namespace task_scheduler_util { + +namespace { + +constexpr char kFieldTrialName[] = "BrowserScheduler"; + +enum WorkerPoolType : size_t { + BACKGROUND = 0, + BACKGROUND_FILE_IO, + FOREGROUND, + FOREGROUND_FILE_IO, + WORKER_POOL_COUNT // Always last. +}; + +} // namespace + +std::vector<base::SchedulerWorkerPoolParams> +GetBrowserWorkerPoolParamsFromVariations() { + using ThreadPriority = base::ThreadPriority; + + std::map<std::string, std::string> variation_params; + if (!::variations::GetVariationParams(kFieldTrialName, &variation_params)) + return std::vector<base::SchedulerWorkerPoolParams>(); + + std::vector<SchedulerImmutableWorkerPoolParams> constant_worker_pool_params; + DCHECK_EQ(BACKGROUND, constant_worker_pool_params.size()); + constant_worker_pool_params.emplace_back("Background", + ThreadPriority::BACKGROUND); + DCHECK_EQ(BACKGROUND_FILE_IO, constant_worker_pool_params.size()); + constant_worker_pool_params.emplace_back("BackgroundFileIO", + ThreadPriority::BACKGROUND); + DCHECK_EQ(FOREGROUND, constant_worker_pool_params.size()); + constant_worker_pool_params.emplace_back("Foreground", + ThreadPriority::NORMAL); + DCHECK_EQ(FOREGROUND_FILE_IO, constant_worker_pool_params.size()); + constant_worker_pool_params.emplace_back("ForegroundFileIO", + ThreadPriority::NORMAL); + + return GetWorkerPoolParams(constant_worker_pool_params, variation_params); +} + +size_t BrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits) { + const bool is_background = + traits.priority() == base::TaskPriority::BACKGROUND; + if (traits.with_file_io()) + return is_background ? BACKGROUND_FILE_IO : FOREGROUND_FILE_IO; + return is_background ? BACKGROUND : FOREGROUND; +} + +void MaybePerformBrowserTaskSchedulerRedirection() { + // TODO(gab): Remove this when http://crbug.com/622400 concludes. + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableBrowserTaskScheduler) && + variations::GetVariationParamValue( + kFieldTrialName, "RedirectSequencedWorkerPools") == "true") { + const base::TaskPriority max_task_priority = + variations::GetVariationParamValue( + kFieldTrialName, "CapSequencedWorkerPoolsAtUserVisible") == "true" + ? base::TaskPriority::USER_VISIBLE + : base::TaskPriority::HIGHEST; + base::SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess( + max_task_priority); + } +} + +} // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/browser/initialization.h b/components/task_scheduler_util/browser/initialization.h new file mode 100644 index 0000000..d4584ee --- /dev/null +++ b/components/task_scheduler_util/browser/initialization.h
@@ -0,0 +1,35 @@ +// 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_TASK_SCHEDULER_UTIL_BROWSER_INITIALIZATION_H_ +#define COMPONENTS_TASK_SCHEDULER_UTIL_BROWSER_INITIALIZATION_H_ + +#include <stddef.h> + +#include <vector> + +#include "base/task_scheduler/scheduler_worker_pool_params.h" + +namespace base { +class TaskTraits; +} + +namespace task_scheduler_util { + +// Gets a vector of SchedulerWorkerPoolParams to initialize TaskScheduler in the +// browser based off variations. Returns an empty vector on failure. +std::vector<base::SchedulerWorkerPoolParams> +GetBrowserWorkerPoolParamsFromVariations(); + +// Maps |traits| to the index of a browser worker pool vector provided by +// GetBrowserWorkerPoolParamsFromVariations(). +size_t BrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits); + +// Redirects zero-to-many PostTask APIs to the browser task scheduler based off +// variations. +void MaybePerformBrowserTaskSchedulerRedirection(); + +} // namespace task_scheduler_util + +#endif // COMPONENTS_TASK_SCHEDULER_UTIL_BROWSER_INITIALIZATION_H_
diff --git a/components/task_scheduler_util/common/BUILD.gn b/components/task_scheduler_util/common/BUILD.gn new file mode 100644 index 0000000..d0e0566 --- /dev/null +++ b/components/task_scheduler_util/common/BUILD.gn
@@ -0,0 +1,26 @@ +# 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. + +static_library("common") { + sources = [ + "variations_util.cc", + "variations_util.h", + ] + + deps = [ + "//base", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "variations_util_unittest.cc", + ] + deps = [ + ":common", + "//base", + "//testing/gtest", + ] +}
diff --git a/components/task_scheduler_util/common/variations_util.cc b/components/task_scheduler_util/common/variations_util.cc new file mode 100644 index 0000000..ad823597 --- /dev/null +++ b/components/task_scheduler_util/common/variations_util.cc
@@ -0,0 +1,108 @@ +// 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/task_scheduler_util/common/variations_util.h" + +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_split.h" +#include "base/task_scheduler/initialization_util.h" +#include "base/time/time.h" + +namespace task_scheduler_util { + +namespace { + +struct SchedulerCustomizableWorkerPoolParams { + base::SchedulerWorkerPoolParams::StandbyThreadPolicy standby_thread_policy; + int max_threads = 0; + base::TimeDelta detach_period; +}; + +// Converts |pool_descriptor| to a SchedulerWorkerPoolVariableParams. Returns a +// default SchedulerWorkerPoolVariableParams on failure. +// +// |pool_descriptor| is a semi-colon separated value string with the following +// items: +// 0. Minimum Thread Count (int) +// 1. Maximum Thread Count (int) +// 2. Thread Count Multiplier (double) +// 3. Thread Count Offset (int) +// 4. Detach Time in Milliseconds (int) +// 5. Standby Thread Policy (string) +// Additional values may appear as necessary and will be ignored. +SchedulerCustomizableWorkerPoolParams StringToVariableWorkerPoolParams( + const base::StringPiece pool_descriptor) { + using StandbyThreadPolicy = + base::SchedulerWorkerPoolParams::StandbyThreadPolicy; + const std::vector<base::StringPiece> tokens = SplitStringPiece( + pool_descriptor, ";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + // Normally, we wouldn't initialize the values below because we don't read + // from them before we write to them. However, some compilers (like MSVC) + // complain about uninitialized variables due to the as_string() call below. + int min = 0; + int max = 0; + double cores_multiplier = 0.0; + int offset = 0; + int detach_milliseconds = 0; + // Checking for a size greater than the expected amount allows us to be + // forward compatible if we add more variation values. + if (tokens.size() >= 5 && base::StringToInt(tokens[0], &min) && + base::StringToInt(tokens[1], &max) && + base::StringToDouble(tokens[2].as_string(), &cores_multiplier) && + base::StringToInt(tokens[3], &offset) && + base::StringToInt(tokens[4], &detach_milliseconds)) { + SchedulerCustomizableWorkerPoolParams params; + params.max_threads = base::RecommendedMaxNumberOfThreadsInPool( + min, max, cores_multiplier, offset); + params.detach_period = + base::TimeDelta::FromMilliseconds(detach_milliseconds); + params.standby_thread_policy = (tokens.size() >= 6 && tokens[5] == "lazy") + ? StandbyThreadPolicy::LAZY + : StandbyThreadPolicy::ONE; + return params; + } + DLOG(ERROR) << "Invalid Worker Pool Descriptor: " << pool_descriptor; + return SchedulerCustomizableWorkerPoolParams(); +} + +} // namespace + +SchedulerImmutableWorkerPoolParams::SchedulerImmutableWorkerPoolParams( + const char* name, + base::ThreadPriority priority_hint) + : name_(name), priority_hint_(priority_hint) {} + +std::vector<base::SchedulerWorkerPoolParams> GetWorkerPoolParams( + const std::vector<SchedulerImmutableWorkerPoolParams>& + constant_worker_pool_params_vector, + const std::map<std::string, std::string>& variation_params) { + std::vector<base::SchedulerWorkerPoolParams> worker_pool_params_vector; + for (const auto& constant_worker_pool_params : + constant_worker_pool_params_vector) { + const char* const worker_pool_name = constant_worker_pool_params.name(); + auto it = variation_params.find(worker_pool_name); + if (it == variation_params.end()) { + DLOG(ERROR) << "Missing Worker Pool Configuration: " << worker_pool_name; + return std::vector<base::SchedulerWorkerPoolParams>(); + } + const auto variable_worker_pool_params = + StringToVariableWorkerPoolParams(it->second); + if (variable_worker_pool_params.max_threads <= 0 || + variable_worker_pool_params.detach_period <= base::TimeDelta()) { + DLOG(ERROR) << "Invalid Worker Pool Configuration: " << worker_pool_name + << " [" << it->second << "]"; + return std::vector<base::SchedulerWorkerPoolParams>(); + } + worker_pool_params_vector.emplace_back( + worker_pool_name, constant_worker_pool_params.priority_hint(), + variable_worker_pool_params.standby_thread_policy, + variable_worker_pool_params.max_threads, + variable_worker_pool_params.detach_period); + } + return worker_pool_params_vector; +} + +} // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/common/variations_util.h b/components/task_scheduler_util/common/variations_util.h new file mode 100644 index 0000000..aa37d25b --- /dev/null +++ b/components/task_scheduler_util/common/variations_util.h
@@ -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. + +#ifndef COMPONENTS_TASK_SCHEDULER_UTIL_COMMON_VARIATIONS_UTIL_H_ +#define COMPONENTS_TASK_SCHEDULER_UTIL_COMMON_VARIATIONS_UTIL_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/task_scheduler/scheduler_worker_pool_params.h" +#include "base/threading/platform_thread.h" + +namespace task_scheduler_util { + +class SchedulerImmutableWorkerPoolParams { + public: + SchedulerImmutableWorkerPoolParams(const char* name, + base::ThreadPriority priority_hint); + + const char* name() const { return name_; } + base::ThreadPriority priority_hint() const { return priority_hint_; } + + private: + const char* name_; + base::ThreadPriority priority_hint_; +}; + +// Returns a SchedulerWorkerPoolParams vector to initialize pools specified in +// |constant_worker_pool_params_vector|. SchedulerWorkerPoolParams members +// without a counterpart in SchedulerImmutableWorkerPoolParams are initialized +// based of |variation_params|. Returns an empty vector on failure. +std::vector<base::SchedulerWorkerPoolParams> GetWorkerPoolParams( + const std::vector<SchedulerImmutableWorkerPoolParams>& + constant_worker_pool_params_vector, + const std::map<std::string, std::string>& variation_params); + +} // namespace task_scheduler_util + +#endif // COMPONENTS_TASK_SCHEDULER_UTIL_COMMON_VARIATIONS_UTIL_H_
diff --git a/components/task_scheduler_util/common/variations_util_unittest.cc b/components/task_scheduler_util/common/variations_util_unittest.cc new file mode 100644 index 0000000..386d0b0 --- /dev/null +++ b/components/task_scheduler_util/common/variations_util_unittest.cc
@@ -0,0 +1,148 @@ +// 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/task_scheduler_util/common/variations_util.h" + +#include <map> +#include <string> +#include <vector> + +#include "base/task_scheduler/scheduler_worker_pool_params.h" +#include "base/threading/platform_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace task_scheduler_util { + +namespace { + +using StandbyThreadPolicy = + base::SchedulerWorkerPoolParams::StandbyThreadPolicy; +using ThreadPriority = base::ThreadPriority; + +std::vector<SchedulerImmutableWorkerPoolParams> GetImmutableWorkerPoolParams() { + std::vector<SchedulerImmutableWorkerPoolParams> constant_worker_pool_params; + constant_worker_pool_params.emplace_back("Background", + ThreadPriority::BACKGROUND); + constant_worker_pool_params.emplace_back("BackgroundFileIO", + ThreadPriority::BACKGROUND); + constant_worker_pool_params.emplace_back("Foreground", + ThreadPriority::NORMAL); + constant_worker_pool_params.emplace_back("ForegroundFileIO", + ThreadPriority::NORMAL); + return constant_worker_pool_params; +} + +} // namespace + +TEST(TaskSchedulerUtilVariationsUtilTest, OrderingParams5) { + std::map<std::string, std::string> variation_params; + variation_params["Background"] = "1;1;1;0;42"; + variation_params["BackgroundFileIO"] = "2;2;1;0;52"; + variation_params["Foreground"] = "4;4;1;0;62"; + variation_params["ForegroundFileIO"] = "8;8;1;0;72"; + + auto params_vector = + GetWorkerPoolParams(GetImmutableWorkerPoolParams(), variation_params); + ASSERT_EQ(4U, params_vector.size()); + + EXPECT_EQ("Background", params_vector[0].name()); + EXPECT_EQ(ThreadPriority::BACKGROUND, params_vector[0].priority_hint()); + EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[0].standby_thread_policy()); + EXPECT_EQ(1U, params_vector[0].max_threads()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(42), + params_vector[0].suggested_reclaim_time()); + + EXPECT_EQ("BackgroundFileIO", params_vector[1].name()); + EXPECT_EQ(ThreadPriority::BACKGROUND, params_vector[1].priority_hint()); + EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[1].standby_thread_policy()); + EXPECT_EQ(2U, params_vector[1].max_threads()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(52), + params_vector[1].suggested_reclaim_time()); + + EXPECT_EQ("Foreground", params_vector[2].name()); + EXPECT_EQ(ThreadPriority::NORMAL, params_vector[2].priority_hint()); + EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[2].standby_thread_policy()); + EXPECT_EQ(4U, params_vector[2].max_threads()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(62), + params_vector[2].suggested_reclaim_time()); + + EXPECT_EQ("ForegroundFileIO", params_vector[3].name()); + EXPECT_EQ(ThreadPriority::NORMAL, params_vector[3].priority_hint()); + EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[3].standby_thread_policy()); + EXPECT_EQ(8U, params_vector[3].max_threads()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(72), + params_vector[3].suggested_reclaim_time()); +} + +TEST(TaskSchedulerUtilVariationsUtilTest, OrderingParams6) { + std::map<std::string, std::string> variation_params; + variation_params["Background"] = "1;1;1;0;42;lazy"; + variation_params["BackgroundFileIO"] = "2;2;1;0;52;one"; + variation_params["Foreground"] = "4;4;1;0;62;lazy"; + variation_params["ForegroundFileIO"] = "8;8;1;0;72;one"; + + auto params_vector = + GetWorkerPoolParams(GetImmutableWorkerPoolParams(), variation_params); + ASSERT_EQ(4U, params_vector.size()); + + EXPECT_EQ("Background", params_vector[0].name()); + EXPECT_EQ(ThreadPriority::BACKGROUND, params_vector[0].priority_hint()); + EXPECT_EQ(StandbyThreadPolicy::LAZY, + params_vector[0].standby_thread_policy()); + EXPECT_EQ(1U, params_vector[0].max_threads()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(42), + params_vector[0].suggested_reclaim_time()); + + EXPECT_EQ("BackgroundFileIO", params_vector[1].name()); + EXPECT_EQ(ThreadPriority::BACKGROUND, params_vector[1].priority_hint()); + EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[1].standby_thread_policy()); + EXPECT_EQ(2U, params_vector[1].max_threads()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(52), + params_vector[1].suggested_reclaim_time()); + + EXPECT_EQ("Foreground", params_vector[2].name()); + EXPECT_EQ(ThreadPriority::NORMAL, params_vector[2].priority_hint()); + EXPECT_EQ(StandbyThreadPolicy::LAZY, + params_vector[2].standby_thread_policy()); + EXPECT_EQ(4U, params_vector[2].max_threads()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(62), + params_vector[2].suggested_reclaim_time()); + + EXPECT_EQ("ForegroundFileIO", params_vector[3].name()); + EXPECT_EQ(ThreadPriority::NORMAL, params_vector[3].priority_hint()); + EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[3].standby_thread_policy()); + EXPECT_EQ(8U, params_vector[3].max_threads()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(72), + params_vector[3].suggested_reclaim_time()); +} + +TEST(TaskSchedulerUtilVariationsUtilTest, NoData) { + EXPECT_TRUE(GetWorkerPoolParams(GetImmutableWorkerPoolParams(), + std::map<std::string, std::string>()) + .empty()); +} + +TEST(TaskSchedulerUtilVariationsUtilTest, IncompleteParameters) { + std::map<std::string, std::string> variation_params; + variation_params["Background"] = "1;1;1;0"; + variation_params["BackgroundFileIO"] = "2;2;1;0"; + variation_params["Foreground"] = "4;4;1;0"; + variation_params["ForegroundFileIO"] = "8;8;1;0"; + EXPECT_TRUE( + GetWorkerPoolParams(GetImmutableWorkerPoolParams(), variation_params) + .empty()); +} + +TEST(TaskSchedulerUtilVariationsUtilTest, InvalidParameters) { + std::map<std::string, std::string> variation_params; + variation_params["Background"] = "a;b;c;d;e"; + variation_params["BackgroundFileIO"] = "a;b;c;d;e"; + variation_params["Foreground"] = "a;b;c;d;e"; + variation_params["ForegroundFileIO"] = "a;b;c;d;e"; + EXPECT_TRUE( + GetWorkerPoolParams(GetImmutableWorkerPoolParams(), variation_params) + .empty()); +} + +} // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/initialization/BUILD.gn b/components/task_scheduler_util/initialization/BUILD.gn index 213326b..26c6ba24 100644 --- a/components/task_scheduler_util/initialization/BUILD.gn +++ b/components/task_scheduler_util/initialization/BUILD.gn
@@ -9,18 +9,6 @@ ] deps = [ - "//base", - ] -} - -source_set("unit_tests") { - testonly = true - sources = [ - "browser_util_unittest.cc", - ] - deps = [ - ":initialization", - "//base", - "//testing/gtest", + "//components/task_scheduler_util/browser", ] }
diff --git a/components/task_scheduler_util/initialization/browser_util.cc b/components/task_scheduler_util/initialization/browser_util.cc index ff9bbf9..5dccd998 100644 --- a/components/task_scheduler_util/initialization/browser_util.cc +++ b/components/task_scheduler_util/initialization/browser_util.cc
@@ -2,125 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(fdoray): Remove this file once TaskScheduler initialization in the +// browser process uses the components/task_scheduler_util/browser/ API on all +// platforms. + #include "components/task_scheduler_util/initialization/browser_util.h" -#include <map> -#include <string> - -#include "base/task_scheduler/initialization_util.h" -#include "base/task_scheduler/switches.h" -#include "base/task_scheduler/task_traits.h" -#include "base/threading/sequenced_worker_pool.h" +#include "components/task_scheduler_util/browser/initialization.h" namespace task_scheduler_util { namespace initialization { -namespace { - -using StandbyThreadPolicy = - base::SchedulerWorkerPoolParams::StandbyThreadPolicy; -using ThreadPriority = base::ThreadPriority; - -struct SchedulerWorkerPoolCustomizableConfiguration { - SchedulerWorkerPoolCustomizableConfiguration( - const char* name_in, - ThreadPriority priority_hint_in, - const SingleWorkerPoolConfiguration& single_worker_pool_config_in) - : name(name_in), - priority_hint(priority_hint_in), - single_worker_pool_config(single_worker_pool_config_in) {} - - const char* name; - ThreadPriority priority_hint; - const SingleWorkerPoolConfiguration& single_worker_pool_config; -}; - -} // namespace - -std::vector<base::SchedulerWorkerPoolParams> -BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams( - const BrowserWorkerPoolsConfiguration& config) { - const SchedulerWorkerPoolCustomizableConfiguration worker_pool_config[] = { - SchedulerWorkerPoolCustomizableConfiguration("Background", - ThreadPriority::BACKGROUND, - config.background), - SchedulerWorkerPoolCustomizableConfiguration("BackgroundFileIO", - ThreadPriority::BACKGROUND, - config.background_file_io), - SchedulerWorkerPoolCustomizableConfiguration("Foreground", - ThreadPriority::NORMAL, - config.foreground), - SchedulerWorkerPoolCustomizableConfiguration("ForegroundFileIO", - ThreadPriority::NORMAL, - config.foreground_file_io), - - }; - static_assert(arraysize(worker_pool_config) == WORKER_POOL_COUNT, - "Mismatched Worker Pool Types and Predefined Parameters"); - constexpr size_t kNumWorkerPoolsDefined = sizeof(config) / - sizeof(config.background); - static_assert(arraysize(worker_pool_config) == kNumWorkerPoolsDefined, - "Mismatch in predefined parameters and worker pools."); - std::vector<base::SchedulerWorkerPoolParams> params_vector; - for (const auto& config : worker_pool_config) { - params_vector.emplace_back( - config.name, config.priority_hint, - config.single_worker_pool_config.standby_thread_policy, - config.single_worker_pool_config.threads, - config.single_worker_pool_config.detach_period); - } - DCHECK_EQ(WORKER_POOL_COUNT, params_vector.size()); - return params_vector; -} - // Returns the worker pool index for |traits| defaulting to FOREGROUND or // FOREGROUND_FILE_IO on any priorities other than background. size_t BrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits) { - const bool is_background = - traits.priority() == base::TaskPriority::BACKGROUND; - if (traits.with_file_io()) - return is_background ? BACKGROUND_FILE_IO : FOREGROUND_FILE_IO; - - return is_background ? BACKGROUND : FOREGROUND; + return ::task_scheduler_util::BrowserWorkerPoolIndexForTraits(traits); } -#if defined(OS_IOS) -std::vector<base::SchedulerWorkerPoolParams> -GetDefaultBrowserSchedulerWorkerPoolParams() { - constexpr size_t kNumWorkerPoolsDefined = - sizeof(BrowserWorkerPoolsConfiguration) / - sizeof(SingleWorkerPoolConfiguration); - static_assert(kNumWorkerPoolsDefined == 4, - "Expected 4 worker pools in BrowserWorkerPoolsConfiguration"); - BrowserWorkerPoolsConfiguration config; - constexpr size_t kSizeAssignedFields = - sizeof(config.background.threads) + - sizeof(config.background.detach_period) + - sizeof(config.background.standby_thread_policy); - static_assert(kSizeAssignedFields == sizeof(config.background), - "Not all fields were assigned"); - config.background.standby_thread_policy = StandbyThreadPolicy::ONE; - config.background.threads = - base::RecommendedMaxNumberOfThreadsInPool(2, 8, 0.1, 0); - config.background.detach_period = base::TimeDelta::FromSeconds(30); - - config.background_file_io.standby_thread_policy = StandbyThreadPolicy::ONE; - config.background_file_io.threads = - base::RecommendedMaxNumberOfThreadsInPool(2, 8, 0.1, 0); - config.background_file_io.detach_period = base::TimeDelta::FromSeconds(30); - - config.foreground.standby_thread_policy = StandbyThreadPolicy::ONE; - config.foreground.threads = - base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.3, 0); - config.foreground.detach_period = base::TimeDelta::FromSeconds(30); - - config.foreground_file_io.standby_thread_policy = StandbyThreadPolicy::ONE; - config.foreground_file_io.threads = - base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.3, 0); - config.foreground_file_io.detach_period = base::TimeDelta::FromSeconds(30); - return BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams(config); -} -#endif // defined(OS_IOS) - } // namespace initialization } // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/initialization/browser_util.h b/components/task_scheduler_util/initialization/browser_util.h index 8d95d889..873ca7bb 100644 --- a/components/task_scheduler_util/initialization/browser_util.h +++ b/components/task_scheduler_util/initialization/browser_util.h
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(fdoray): Remove this file once TaskScheduler initialization in the +// browser process uses the components/task_scheduler_util/browser/ API on all +// platforms. + #ifndef COMPONENTS_TASK_SCHEDULER_UTIL_INITIALIZATION_BROWSER_UTIL_H_ #define COMPONENTS_TASK_SCHEDULER_UTIL_INITIALIZATION_BROWSER_UTIL_H_ -#include <vector> - -#include "base/task_scheduler/scheduler_worker_pool_params.h" -#include "base/time/time.h" -#include "build/build_config.h" +#include <stddef.h> namespace base { class TaskTraits; @@ -18,45 +18,11 @@ namespace task_scheduler_util { namespace initialization { -enum WorkerPoolType : size_t { - BACKGROUND = 0, - BACKGROUND_FILE_IO, - FOREGROUND, - FOREGROUND_FILE_IO, - WORKER_POOL_COUNT // Always last. -}; - -struct SingleWorkerPoolConfiguration { - base::SchedulerWorkerPoolParams::StandbyThreadPolicy standby_thread_policy; - int threads = 0; - base::TimeDelta detach_period; -}; - -struct BrowserWorkerPoolsConfiguration { - SingleWorkerPoolConfiguration background; - SingleWorkerPoolConfiguration background_file_io; - SingleWorkerPoolConfiguration foreground; - SingleWorkerPoolConfiguration foreground_file_io; -}; - -// Converts a BrowserWorkerPoolsConfiguration to a vector of -// base::SchedulerWorkerPoolParams for consumption by task scheduler -// initialization. -std::vector<base::SchedulerWorkerPoolParams> -BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams( - const BrowserWorkerPoolsConfiguration& config); - // Maps |traits| to the index of a browser worker pool vector provided by // BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams() or // GetDefaultBrowserSchedulerWorkerPoolParams(). size_t BrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits); -#if defined(OS_IOS) -// Returns the default browser scheduler worker pool params. -std::vector<base::SchedulerWorkerPoolParams> -GetDefaultBrowserSchedulerWorkerPoolParams(); -#endif // defined(OS_IOS) - } // namespace initialization } // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/initialization/browser_util_unittest.cc b/components/task_scheduler_util/initialization/browser_util_unittest.cc deleted file mode 100644 index 7dad6ffe1..0000000 --- a/components/task_scheduler_util/initialization/browser_util_unittest.cc +++ /dev/null
@@ -1,65 +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 "components/task_scheduler_util/initialization/browser_util.h" - -#include <vector> - -#include "base/task_scheduler/scheduler_worker_pool_params.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace task_scheduler_util { -namespace initialization { - -TEST(TaskSchedulerUtilBrowserUtilTest, CheckOrdering) { - using StandbyThreadPolicy = - base::SchedulerWorkerPoolParams::StandbyThreadPolicy; - BrowserWorkerPoolsConfiguration config; - config.background.standby_thread_policy = StandbyThreadPolicy::LAZY; - config.background.threads = 1; - config.background.detach_period = base::TimeDelta::FromSeconds(2); - - config.background_file_io.standby_thread_policy = StandbyThreadPolicy::ONE; - config.background_file_io.threads = 3; - config.background_file_io.detach_period = base::TimeDelta::FromSeconds(4); - - config.foreground.standby_thread_policy = StandbyThreadPolicy::LAZY; - config.foreground.threads = 5; - config.foreground.detach_period = base::TimeDelta::FromSeconds(6); - - config.foreground_file_io.standby_thread_policy = StandbyThreadPolicy::ONE; - config.foreground_file_io.threads = 7; - config.foreground_file_io.detach_period = base::TimeDelta::FromSeconds(8); - - const std::vector<base::SchedulerWorkerPoolParams> params_vector = - BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams(config); - - ASSERT_EQ(4U, params_vector.size()); - - EXPECT_EQ(StandbyThreadPolicy::LAZY, - params_vector[0].standby_thread_policy()); - EXPECT_EQ(1U, params_vector[0].max_threads()); - EXPECT_EQ(base::TimeDelta::FromSeconds(2), - params_vector[0].suggested_reclaim_time()); - - EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[1].standby_thread_policy()); - EXPECT_EQ(3U, params_vector[1].max_threads()); - EXPECT_EQ(base::TimeDelta::FromSeconds(4), - params_vector[1].suggested_reclaim_time()); - - EXPECT_EQ(StandbyThreadPolicy::LAZY, - params_vector[2].standby_thread_policy()); - EXPECT_EQ(5U, params_vector[2].max_threads()); - EXPECT_EQ(base::TimeDelta::FromSeconds(6), - params_vector[2].suggested_reclaim_time()); - - EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[3].standby_thread_policy()); - EXPECT_EQ(7U, params_vector[3].max_threads()); - EXPECT_EQ(base::TimeDelta::FromSeconds(8), - params_vector[3].suggested_reclaim_time()); -} - -} // namespace initialization -} // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/variations/BUILD.gn b/components/task_scheduler_util/variations/BUILD.gn index 64aab01..5d66710 100644 --- a/components/task_scheduler_util/variations/BUILD.gn +++ b/components/task_scheduler_util/variations/BUILD.gn
@@ -10,9 +10,7 @@ deps = [ "//base", - "//base:base_static", - "//components/task_scheduler_util/initialization", - "//components/variations", + "//components/task_scheduler_util/browser", ] }
diff --git a/components/task_scheduler_util/variations/browser_variations_util.cc b/components/task_scheduler_util/variations/browser_variations_util.cc index 29f2e32..3c02359 100644 --- a/components/task_scheduler_util/variations/browser_variations_util.cc +++ b/components/task_scheduler_util/variations/browser_variations_util.cc
@@ -2,157 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(fdoray): Remove this file once TaskScheduler initialization in the +// browser process uses the components/task_scheduler_util/browser/ API on all +// platforms. + #include "components/task_scheduler_util/variations/browser_variations_util.h" -#include <map> -#include <string> -#include <vector> - -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_split.h" -#include "base/task_scheduler/initialization_util.h" -#include "base/task_scheduler/scheduler_worker_pool_params.h" -#include "base/task_scheduler/switches.h" -#include "base/task_scheduler/task_traits.h" -#include "base/threading/sequenced_worker_pool.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "components/task_scheduler_util/initialization/browser_util.h" -#include "components/variations/variations_associated_data.h" +#include "components/task_scheduler_util/browser/initialization.h" namespace task_scheduler_util { namespace variations { -namespace { - -constexpr char kFieldTrialName[] = "BrowserScheduler"; - -// Converts |pool_descriptor| to a SingleWorkerPoolConfiguration. Returns a -// default SingleWorkerPoolConfiguration on failure. -// -// |pool_descriptor| is a semi-colon separated value string with the following -// items: -// 0. Minimum Thread Count (int) -// 1. Maximum Thread Count (int) -// 2. Thread Count Multiplier (double) -// 3. Thread Count Offset (int) -// 4. Detach Time in Milliseconds (milliseconds) -// 5. Standby Thread Policy (string) -// Additional values may appear as necessary and will be ignored. -initialization::SingleWorkerPoolConfiguration -StringToSingleWorkerPoolConfiguration(const base::StringPiece pool_descriptor) { - using StandbyThreadPolicy = - base::SchedulerWorkerPoolParams::StandbyThreadPolicy; - const std::vector<base::StringPiece> tokens = SplitStringPiece( - pool_descriptor, ";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - // Normally, we wouldn't initialize the values below because we don't read - // from them before we write to them. However, some compilers (like MSVC) - // complain about uninitialized varaibles due to the as_string() call below. - int min = 0; - int max = 0; - double cores_multiplier = 0.0; - int offset = 0; - int detach_milliseconds = 0; - // Checking for a size greater than the expected amount allows us to be - // forward compatible if we add more variation values. - if (tokens.size() >= 5 && base::StringToInt(tokens[0], &min) && - base::StringToInt(tokens[1], &max) && - base::StringToDouble(tokens[2].as_string(), &cores_multiplier) && - base::StringToInt(tokens[3], &offset) && - base::StringToInt(tokens[4], &detach_milliseconds)) { - initialization::SingleWorkerPoolConfiguration config; - config.threads = base::RecommendedMaxNumberOfThreadsInPool( - min, max, cores_multiplier, offset); - config.detach_period = - base::TimeDelta::FromMilliseconds(detach_milliseconds); - config.standby_thread_policy = (tokens.size() >= 6 && tokens[5] == "lazy") - ? StandbyThreadPolicy::LAZY - : StandbyThreadPolicy::ONE; - return config; - } - DLOG(ERROR) << "Invalid Worker Pool Descriptor: " << pool_descriptor; - return initialization::SingleWorkerPoolConfiguration(); -} - -// Converts a browser-based |variation_params| to -// std::vector<base::SchedulerWorkerPoolParams>. Returns an empty vector on -// failure. -std::vector<base::SchedulerWorkerPoolParams> -VariationsParamsToSchedulerWorkerPoolParamsVector( - const std::map<std::string, std::string>& variation_params) { - static const char* const kWorkerPoolNames[] = { - "Background", "BackgroundFileIO", "Foreground", "ForegroundFileIO"}; - static_assert( - arraysize(kWorkerPoolNames) == initialization::WORKER_POOL_COUNT, - "Mismatched Worker Pool Types and Worker Pool Names"); - initialization::BrowserWorkerPoolsConfiguration config; - initialization::SingleWorkerPoolConfiguration* const all_pools[]{ - &config.background, &config.background_file_io, &config.foreground, - &config.foreground_file_io, - }; - static_assert(arraysize(kWorkerPoolNames) == arraysize(all_pools), - "Mismatched Worker Pool Names and All Pools Array"); - for (size_t i = 0; i < arraysize(kWorkerPoolNames); ++i) { - const auto* const worker_pool_name = kWorkerPoolNames[i]; - const auto pair = variation_params.find(worker_pool_name); - if (pair == variation_params.end()) { - DLOG(ERROR) << "Missing Worker Pool Configuration: " << worker_pool_name; - return std::vector<base::SchedulerWorkerPoolParams>(); - } - - auto* const pool_config = all_pools[i]; - *pool_config = StringToSingleWorkerPoolConfiguration(pair->second); - if (pool_config->threads <= 0 || - pool_config->detach_period <= base::TimeDelta()) { - DLOG(ERROR) << "Invalid Worker Pool Configuration: " << worker_pool_name - << " [" << pair->second << "]"; - return std::vector<base::SchedulerWorkerPoolParams>(); - } - } - return BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams(config); -} - -} // namespace - std::vector<base::SchedulerWorkerPoolParams> GetBrowserSchedulerWorkerPoolParamsFromVariations() { - std::map<std::string, std::string> variation_params; - if (!::variations::GetVariationParams(kFieldTrialName, &variation_params)) - return std::vector<base::SchedulerWorkerPoolParams>(); - - return VariationsParamsToSchedulerWorkerPoolParamsVector(variation_params); + return ::task_scheduler_util::GetBrowserWorkerPoolParamsFromVariations(); } void MaybePerformBrowserTaskSchedulerRedirection() { - std::map<std::string, std::string> variation_params; - ::variations::GetVariationParams(kFieldTrialName, &variation_params); - - // TODO(gab): Remove this when http://crbug.com/622400 concludes. - const auto sequenced_worker_pool_param = - variation_params.find("RedirectSequencedWorkerPools"); - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableBrowserTaskScheduler) && - sequenced_worker_pool_param != variation_params.end() && - sequenced_worker_pool_param->second == "true") { - // Check a variation that allows capping all redirections at USER_VISIBLE - // (no USER_BLOCKING) to observe the side-effects of multiple priority - // levels in the foreground IO pool. - const auto sequenced_worker_pool_cap_priority_param = - variation_params.find("CapSequencedWorkerPoolsAtUserVisible"); - - const base::TaskPriority max_task_priority = - sequenced_worker_pool_cap_priority_param != variation_params.end() && - sequenced_worker_pool_cap_priority_param->second == "true" - ? base::TaskPriority::USER_VISIBLE - : base::TaskPriority::HIGHEST; - - base::SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess( - max_task_priority); - } else { - base::SequencedWorkerPool::EnableForProcess(); - } + ::task_scheduler_util::MaybePerformBrowserTaskSchedulerRedirection(); } } // variations
diff --git a/components/task_scheduler_util/variations/browser_variations_util.h b/components/task_scheduler_util/variations/browser_variations_util.h index 114d5d3..b0857c9d 100644 --- a/components/task_scheduler_util/variations/browser_variations_util.h +++ b/components/task_scheduler_util/variations/browser_variations_util.h
@@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(fdoray): Remove this file once TaskScheduler initialization in the +// browser process uses the components/task_scheduler_util/browser/ API on all +// platforms. + #ifndef COMPONENTS_TASK_SCHEDULER_UTIL_VARIATIONS_BROWSER_VARIATIONS_UTIL_H_ #define COMPONENTS_TASK_SCHEDULER_UTIL_VARIATIONS_BROWSER_VARIATIONS_UTIL_H_
diff --git a/components/task_scheduler_util/variations/browser_variations_util_unittest.cc b/components/task_scheduler_util/variations/browser_variations_util_unittest.cc index 9350c6d0..e2924cf3 100644 --- a/components/task_scheduler_util/variations/browser_variations_util_unittest.cc +++ b/components/task_scheduler_util/variations/browser_variations_util_unittest.cc
@@ -2,12 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(fdoray): Remove this file once TaskScheduler initialization in the +// browser process uses the components/task_scheduler_util/browser/ API on all +// platforms. + #include "components/task_scheduler_util/variations/browser_variations_util.h" #include <map> #include <vector> #include "base/metrics/field_trial.h" +#include "base/task_scheduler/scheduler_worker_pool_params.h" #include "components/variations/variations_associated_data.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc index e405b13..136c0ed0 100644 --- a/content/browser/media/session/media_session_impl.cc +++ b/content/browser/media/session/media_session_impl.cc
@@ -575,6 +575,7 @@ void MediaSessionImpl::OnServiceCreated(MediaSessionServiceImpl* service) { services_[service->GetRenderFrameHost()] = service; + UpdateRoutedService(); } void MediaSessionImpl::OnServiceDestroyed(MediaSessionServiceImpl* service) { @@ -617,11 +618,7 @@ } bool MediaSessionImpl::IsServiceActiveForRenderFrameHost(RenderFrameHost* rfh) { - if (!services_.count(rfh)) - return false; - - return services_[rfh]->metadata().has_value() || - !services_[rfh]->actions().empty(); + return services_.find(rfh) != services_.end(); } void MediaSessionImpl::UpdateRoutedService() {
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc index 1fddbe252..198a6ce34 100644 --- a/content/browser/media/session/media_session_impl_browsertest.cc +++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -157,7 +157,6 @@ void EnsureMediaSessionService() { mock_media_session_service_.reset(new MockMediaSessionServiceImpl( shell()->web_contents()->GetMainFrame())); - mock_media_session_service_->SetMetadata(content::MediaMetadata()); } void SetPlaybackState(blink::mojom::MediaSessionPlaybackState state) {
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc index b1b8c64..1f486748 100644 --- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc +++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -74,7 +74,10 @@ protected: void CreateServiceForFrame(TestRenderFrameHost* frame) { services_[frame] = base::MakeUnique<MockMediaSessionServiceImpl>(frame); - services_[frame]->SetMetadata(MediaMetadata()); + } + + void DestroyServiceForFrame(TestRenderFrameHost* frame) { + services_.erase(frame); } void StartPlayerForFrame(TestRenderFrameHost* frame) { @@ -133,26 +136,40 @@ } TEST_F(MediaSessionImplServiceRoutingTest, - OnlyMainFrameProducesAudioButHasInactiveService) { - StartPlayerForFrame(main_frame_); - + OnlyMainFrameProducesAudioButHasDestroyedService) { CreateServiceForFrame(main_frame_); - services_[main_frame_]->SetMetadata(base::nullopt); + StartPlayerForFrame(main_frame_); + DestroyServiceForFrame(main_frame_); ASSERT_EQ(nullptr, ComputeServiceForRouting()); } TEST_F(MediaSessionImplServiceRoutingTest, - OnlySubFrameProducesAudioButHasInactiveService) { - StartPlayerForFrame(sub_frame_); - + OnlySubFrameProducesAudioButHasDestroyedService) { CreateServiceForFrame(sub_frame_); - services_[sub_frame_]->SetMetadata(base::nullopt); + StartPlayerForFrame(sub_frame_); + DestroyServiceForFrame(sub_frame_); ASSERT_EQ(nullptr, ComputeServiceForRouting()); } TEST_F(MediaSessionImplServiceRoutingTest, + OnlyMainFrameProducesAudioAndServiceIsCreatedAfterwards) { + StartPlayerForFrame(main_frame_); + CreateServiceForFrame(main_frame_); + + ASSERT_EQ(services_[main_frame_].get(), ComputeServiceForRouting()); +} + +TEST_F(MediaSessionImplServiceRoutingTest, + OnlySubFrameProducesAudioAndServiceIsCreatedAfterwards) { + StartPlayerForFrame(sub_frame_); + CreateServiceForFrame(sub_frame_); + + ASSERT_EQ(services_[sub_frame_].get(), ComputeServiceForRouting()); +} + +TEST_F(MediaSessionImplServiceRoutingTest, BothFrameProducesAudioButOnlySubFrameHasService) { StartPlayerForFrame(main_frame_); StartPlayerForFrame(sub_frame_);
diff --git a/content/public/test/test_browser_thread_bundle.cc b/content/public/test/test_browser_thread_bundle.cc index d683112..211ecdfa 100644 --- a/content/public/test/test_browser_thread_bundle.cc +++ b/content/public/test/test_browser_thread_bundle.cc
@@ -7,6 +7,7 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/test/scoped_task_scheduler.h" #include "content/browser/browser_thread_impl.h" #include "content/public/test/test_browser_thread.h" @@ -52,6 +53,8 @@ ui_thread_->Stop(); base::RunLoop().RunUntilIdle(); + task_scheduler_.reset(); + // |message_loop_| needs to explicitly go away before fake threads in order // for DestructionObservers hooked to |message_loop_| to be able to invoke // BrowserThread::CurrentlyOn() -- ref. ~TestBrowserThread(). @@ -71,6 +74,9 @@ message_loop_.reset(new base::MessageLoopForUI()); } + task_scheduler_.reset( + new base::test::ScopedTaskScheduler(message_loop_.get())); + ui_thread_.reset( new TestBrowserThread(BrowserThread::UI, message_loop_.get()));
diff --git a/content/public/test/test_browser_thread_bundle.h b/content/public/test/test_browser_thread_bundle.h index 2e1e69bd..0138a723 100644 --- a/content/public/test/test_browser_thread_bundle.h +++ b/content/public/test/test_browser_thread_bundle.h
@@ -3,34 +3,27 @@ // found in the LICENSE file. // TestBrowserThreadBundle is a convenience class for creating a set of -// TestBrowserThreads in unit tests. For most tests, it is sufficient to -// just instantiate the TestBrowserThreadBundle as a member variable. -// It is a good idea to put the TestBrowserThreadBundle as the first member -// variable in test classes, so it is destroyed last, and the test threads -// always exist from the perspective of other classes. +// TestBrowserThreads, a blocking pool, and a task scheduler in unit tests. For +// most tests, it is sufficient to just instantiate the TestBrowserThreadBundle +// as a member variable. It is a good idea to put the TestBrowserThreadBundle as +// the first member variable in test classes, so it is destroyed last, and the +// test threads always exist from the perspective of other classes. // -// By default, all of the created TestBrowserThreads will be backed by a single -// shared MessageLoop. If a test truly needs separate threads, it can do -// so by passing the appropriate combination of option values during -// the TestBrowserThreadBundle construction. +// By default, all of the created TestBrowserThreads and the task scheduler will +// be backed by a single shared MessageLoop. If a test truly needs separate +// threads, it can do so by passing the appropriate combination of option values +// during the TestBrowserThreadBundle construction. // -// The TestBrowserThreadBundle will attempt to drain the MessageLoop on -// destruction. Sometimes a test needs to drain currently enqueued tasks -// mid-test. Browser tests should call content::RunAllPendingInMessageLoop(). -// Unit tests should use base::RunLoop (e.g., base::RunLoop().RunUntilIdle()). -// TODO(phajdan.jr): Revise this comment after switch to Aura. +// To synchronously run tasks posted to task scheduler or to TestBrowserThreads +// that use the shared MessageLoop, call RunLoop::Run/RunUntilIdle() on the +// thread where the TestBrowserThreadBundle lives. The destructor of +// TestBrowserThreadBundle runs remaining TestBrowserThreads tasks, remaining +// blocking pool tasks, and remaining BLOCK_SHUTDOWN task scheduler tasks. // -// The TestBrowserThreadBundle will also flush the blocking pool on destruction. -// We do this to avoid memory leaks, particularly in the case of threads posting -// tasks to the blocking pool via PostTaskAndReply. By ensuring that the tasks -// are run while the originating TestBroswserThreads still exist, we prevent -// leakage of PostTaskAndReplyRelay objects. We also flush the blocking pool -// again at the point where it would normally be shut down, to better simulate -// the normal thread shutdown process. -// -// Some tests using the IO thread expect a MessageLoopForIO. Passing -// IO_MAINLOOP will use a MessageLoopForIO for the main MessageLoop. -// Most of the time, this avoids needing to use a REAL_IO_THREAD. +// If a test needs a MessageLoopForIO on the main thread, it should use the +// IO_MAINLOOP option. This also allows task scheduler tasks to use +// FileDescriptorWatcher. Most of the time, IO_MAINLOOP avoids needing to use a +// REAL_IO_THREAD. // // For some tests it is important to emulate real browser startup. During real // browser startup some initialization is done (e.g. creation of thread objects) @@ -51,6 +44,9 @@ namespace base { class MessageLoop; +namespace test { +class ScopedTaskScheduler; +} // namespace test } // namespace base namespace content { @@ -84,6 +80,7 @@ void Init(); std::unique_ptr<base::MessageLoop> message_loop_; + std::unique_ptr<base::test::ScopedTaskScheduler> task_scheduler_; std::unique_ptr<TestBrowserThread> ui_thread_; std::unique_ptr<TestBrowserThread> db_thread_; std::unique_ptr<TestBrowserThread> file_thread_;
diff --git a/third_party/WebKit/LayoutTests/fullscreen/full-screen-test.js b/third_party/WebKit/LayoutTests/fullscreen/full-screen-test.js index ae6ff34..29484405 100644 --- a/third_party/WebKit/LayoutTests/fullscreen/full-screen-test.js +++ b/third_party/WebKit/LayoutTests/fullscreen/full-screen-test.js
@@ -137,10 +137,12 @@ function endTest() { + if (testEnded) + return; consoleWrite("END OF TEST"); testEnded = true; if (window.testRunner) - testRunner.notifyDone(); + testRunner.layoutAndPaintAsyncThen(() => testRunner.notifyDone()); } function logResult(success, text)
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/MANIFEST.json b/third_party/WebKit/LayoutTests/imported/wpt/MANIFEST.json index e30b7cb..2d6dce32 100644 --- a/third_party/WebKit/LayoutTests/imported/wpt/MANIFEST.json +++ b/third_party/WebKit/LayoutTests/imported/wpt/MANIFEST.json
@@ -5569,6 +5569,12 @@ "url": "/WebIDL/ecmascript-binding/interface-object.html" } ], + "accelerometer/idlharness.https.html": [ + { + "path": "accelerometer/idlharness.https.html", + "url": "/accelerometer/idlharness.https.html" + } + ], "clear-site-data/navigation.html": [ { "path": "clear-site-data/navigation.html", @@ -6238,6 +6244,12 @@ "url": "/dom/events/CustomEvent.html" } ], + "dom/events/Event-cancelBubble.html": [ + { + "path": "dom/events/Event-cancelBubble.html", + "url": "/dom/events/Event-cancelBubble.html" + } + ], "dom/events/Event-constants.html": [ { "path": "dom/events/Event-constants.html", @@ -6262,6 +6274,12 @@ "url": "/dom/events/Event-defaultPrevented.html" } ], + "dom/events/Event-dispatch-bubble-canceled.html": [ + { + "path": "dom/events/Event-dispatch-bubble-canceled.html", + "url": "/dom/events/Event-dispatch-bubble-canceled.html" + } + ], "dom/events/Event-dispatch-bubbles-false.html": [ { "path": "dom/events/Event-dispatch-bubbles-false.html", @@ -6292,6 +6310,12 @@ "url": "/dom/events/Event-dispatch-handlers-changed.html" } ], + "dom/events/Event-dispatch-multiple-cancelBubble.html": [ + { + "path": "dom/events/Event-dispatch-multiple-cancelBubble.html", + "url": "/dom/events/Event-dispatch-multiple-cancelBubble.html" + } + ], "dom/events/Event-dispatch-multiple-stopPropagation.html": [ { "path": "dom/events/Event-dispatch-multiple-stopPropagation.html", @@ -8654,6 +8678,12 @@ "url": "/gamepad/idlharness.html" } ], + "gyroscope/idlharness.https.html": [ + { + "path": "gyroscope/idlharness.https.html", + "url": "/gyroscope/idlharness.https.html" + } + ], "hr-time/basic.html": [ { "path": "hr-time/basic.html", @@ -14801,6 +14831,12 @@ "url": "/input-events/idlharness.html" } ], + "magnetometer/idlharness.https.html": [ + { + "path": "magnetometer/idlharness.https.html", + "url": "/magnetometer/idlharness.https.html" + } + ], "mediacapture-streams/GUM-api.https.html": [ { "path": "mediacapture-streams/GUM-api.https.html", @@ -14975,6 +15011,84 @@ "url": "/pointerevents/pointerevent_touch-action-verification.html" } ], + "preload/avoid_delaying_onload_link_preload.html": [ + { + "path": "preload/avoid_delaying_onload_link_preload.html", + "url": "/preload/avoid_delaying_onload_link_preload.html" + } + ], + "preload/delaying_onload_link_preload_after_discovery.html": [ + { + "path": "preload/delaying_onload_link_preload_after_discovery.html", + "url": "/preload/delaying_onload_link_preload_after_discovery.html" + } + ], + "preload/download_resources.html": [ + { + "path": "preload/download_resources.html", + "url": "/preload/download_resources.html" + } + ], + "preload/dynamic_adding_preload.html": [ + { + "path": "preload/dynamic_adding_preload.html", + "url": "/preload/dynamic_adding_preload.html" + } + ], + "preload/link_header_preload.html": [ + { + "path": "preload/link_header_preload.html", + "url": "/preload/link_header_preload.html" + } + ], + "preload/link_header_preload_delay_onload.html": [ + { + "path": "preload/link_header_preload_delay_onload.html", + "url": "/preload/link_header_preload_delay_onload.html" + } + ], + "preload/onerror_event.html": [ + { + "path": "preload/onerror_event.html", + "url": "/preload/onerror_event.html" + } + ], + "preload/onload_event.html": [ + { + "path": "preload/onload_event.html", + "url": "/preload/onload_event.html" + } + ], + "preload/preload-csp.sub.html": [ + { + "path": "preload/preload-csp.sub.html", + "url": "/preload/preload-csp.sub.html" + } + ], + "preload/preload-default-csp.sub.html": [ + { + "path": "preload/preload-default-csp.sub.html", + "url": "/preload/preload-default-csp.sub.html" + } + ], + "preload/preload_with_type.html": [ + { + "path": "preload/preload_with_type.html", + "url": "/preload/preload_with_type.html" + } + ], + "preload/single_download_late_used_preload.html": [ + { + "path": "preload/single_download_late_used_preload.html", + "url": "/preload/single_download_late_used_preload.html" + } + ], + "preload/single_download_preload.html": [ + { + "path": "preload/single_download_preload.html", + "url": "/preload/single_download_preload.html" + } + ], "quirks-mode/blocks-ignore-line-height.html": [ { "path": "quirks-mode/blocks-ignore-line-height.html",
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/accelerometer/idlharness.https-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/accelerometer/idlharness.https-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/accelerometer/idlharness.https-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/accelerometer/idlharness.https.html b/third_party/WebKit/LayoutTests/imported/wpt/accelerometer/idlharness.https.html new file mode 100644 index 0000000..79cd5c9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/accelerometer/idlharness.https.html
@@ -0,0 +1,113 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Accelerometer Sensor IDL tests</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="help" href="https://www.w3.org/TR/accelerometer/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/WebIDLParser.js"></script> +<script src="/resources/idlharness.js"></script> +<style> + pre { + display: none; + } +</style> +<div id="log"></div> + +<pre id="idl"> +interface Event { +}; + +interface EventTarget { +}; + +interface EventHandler { +}; + +interface Error { +}; + +dictionary EventInit { +}; +</pre> + +<pre id="generic-idl"> +[SecureContext] +interface Sensor : EventTarget { + readonly attribute SensorState state; + readonly attribute SensorReading? reading; + void start(); + void stop(); + attribute EventHandler onchange; + attribute EventHandler onactivate; + attribute EventHandler onerror; +}; + +dictionary SensorOptions { + double? frequency; +}; + +enum SensorState { + "idle", + "activating", + "activated", + "errored" +}; + +[SecureContext] +interface SensorReading { + readonly attribute DOMHighResTimeStamp timeStamp; +}; + +[SecureContext, Constructor(DOMString type, SensorErrorEventInit errorEventInitDict)] +interface SensorErrorEvent : Event { + readonly attribute Error error; +}; + +dictionary SensorErrorEventInit : EventInit { + required Error error; +}; + +</pre> + +<pre id="accelerometer-idl"> +[Constructor(optional AccelerometerOptions accelerometerOptions)] +interface Accelerometer : Sensor { + readonly attribute AccelerometerReading? reading; + readonly attribute boolean includesGravity; +}; + +dictionary AccelerometerOptions : SensorOptions { + boolean includeGravity = true; +}; + +[Constructor(AccelerometerReadingInit AccelerometerReadingInit)] +interface AccelerometerReading : SensorReading { + readonly attribute double x; + readonly attribute double y; + readonly attribute double z; +}; + +dictionary AccelerometerReadingInit { + double x = 0; + double y = 0; + double z = 0; +}; +</pre> + +<script> +(function() { + "use strict"; + var idl_array = new IdlArray(); + idl_array.add_untested_idls(document.getElementById('idl').textContent); + idl_array.add_untested_idls(document.getElementById('generic-idl').textContent); + idl_array.add_idls(document.getElementById('accelerometer-idl').textContent); + + idl_array.add_objects({ + Accelerometer: ['new Accelerometer();'], + AccelerometerReading: ['new AccelerometerReading({x: 0.5, y: 0.5, z: 0.5});'] + }); + + idl_array.test(); +})(); +</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-cancelBubble-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-cancelBubble-expected.txt new file mode 100644 index 0000000..073ac2b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-cancelBubble-expected.txt
@@ -0,0 +1,11 @@ +This is a testharness.js-based test. +PASS cancelBubble must be false when an event is initially created. +FAIL Initializing an event must set cancelBubble to false. assert_false: initEvent() must set cancelBubble to false. [bubbles=true] expected false got true +FAIL stopPropagation() must set cancelBubble to true. assert_true: stopPropagation() must set cancelBubble to true. expected true got false +FAIL stopImmediatePropagation() must set cancelBubble to true. assert_true: stopImmediatePropagation() must set cancelBubble to true. expected true got false +FAIL Event.cancelBubble=false must have no effect. assert_true: cancelBubble must still be true after attempting to set it to false. expected true got false +FAIL Event.cancelBubble=false must have no effect during event propagation. assert_unreached: Setting Event.cancelBubble=false after setting Event.cancelBubble=true should have no effect. Reached unreachable code +PASS cancelBubble must be false after an event has been dispatched. +FAIL Event.cancelBubble=true must set the stop propagation flag. assert_unreached: Setting cancelBubble=true should stop the event from propagating further, including during the Capture Phase. Reached unreachable code +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-cancelBubble.html b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-cancelBubble.html new file mode 100644 index 0000000..d8d2d72 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-cancelBubble.html
@@ -0,0 +1,132 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Event.cancelBubble</title> + <link rel="author" title="Chris Rebert" href="http://chrisrebert.com"> + <link rel="help" href="https://dom.spec.whatwg.org/#dom-event-cancelbubble"> + <meta name="flags" content="dom"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <div id="outer"> + <div id="middle"> + <div id="inner"></div> + </div> + </div> + <script> +test(function () { + // See https://dom.spec.whatwg.org/#stop-propagation-flag + var e = document.createEvent('Event'); + assert_false(e.cancelBubble, "cancelBubble must be false after event creation."); +}, "cancelBubble must be false when an event is initially created."); + +test(function () { + // See https://dom.spec.whatwg.org/#concept-event-initialize + + // Event which bubbles. + var one = document.createEvent('Event'); + one.cancelBubble = true; + one.initEvent('foo', true/*bubbles*/, false/*cancelable*/); + assert_false(one.cancelBubble, "initEvent() must set cancelBubble to false. [bubbles=true]"); + // Re-initialization. + one.cancelBubble = true; + one.initEvent('foo', true/*bubbles*/, false/*cancelable*/); + assert_false(one.cancelBubble, "2nd initEvent() call must set cancelBubble to false. [bubbles=true]"); + + // Event which doesn't bubble. + var two = document.createEvent('Event'); + two.cancelBubble = true; + two.initEvent('foo', false/*bubbles*/, false/*cancelable*/); + assert_false(two.cancelBubble, "initEvent() must set cancelBubble to false. [bubbles=false]"); + // Re-initialization. + two.cancelBubble = true; + two.initEvent('foo', false/*bubbles*/, false/*cancelable*/); + assert_false(two.cancelBubble, "2nd initEvent() call must set cancelBubble to false. [bubbles=false]"); +}, "Initializing an event must set cancelBubble to false."); + +test(function () { + // See https://dom.spec.whatwg.org/#dom-event-stoppropagation + var e = document.createEvent('Event'); + e.stopPropagation(); + assert_true(e.cancelBubble, "stopPropagation() must set cancelBubble to true."); +}, "stopPropagation() must set cancelBubble to true."); + +test(function () { + // See https://dom.spec.whatwg.org/#dom-event-stopimmediatepropagation + var e = document.createEvent('Event'); + e.stopImmediatePropagation(); + assert_true(e.cancelBubble, "stopImmediatePropagation() must set cancelBubble to true."); +}, "stopImmediatePropagation() must set cancelBubble to true."); + +test(function () { + var one = document.createEvent('Event'); + one.stopPropagation(); + one.cancelBubble = false; + assert_true(one.cancelBubble, "cancelBubble must still be true after attempting to set it to false."); +}, "Event.cancelBubble=false must have no effect."); + +test(function (t) { + var outer = document.getElementById('outer'); + var middle = document.getElementById('middle'); + var inner = document.getElementById('inner'); + + outer.addEventListener('barbaz', t.step_func(function () { + assert_unreached("Setting Event.cancelBubble=false after setting Event.cancelBubble=true should have no effect."); + }), false/*useCapture*/); + + middle.addEventListener('barbaz', function (e) { + e.cancelBubble = true;// Stop propagation. + e.cancelBubble = false;// Should be a no-op. + }, false/*useCapture*/); + + var barbazEvent = document.createEvent('Event'); + barbazEvent.initEvent('barbaz', true/*bubbles*/, false/*cancelable*/); + inner.dispatchEvent(barbazEvent); +}, "Event.cancelBubble=false must have no effect during event propagation."); + +test(function () { + // See https://dom.spec.whatwg.org/#concept-event-dispatch + // "14. Unset event’s [...] stop propagation flag," + var e = document.createEvent('Event'); + e.initEvent('foobar', true/*bubbles*/, true/*cancelable*/); + document.body.addEventListener('foobar', function listener(e) { + e.stopPropagation(); + }); + document.body.dispatchEvent(e); + assert_false(e.cancelBubble, "cancelBubble must be false after an event has been dispatched."); +}, "cancelBubble must be false after an event has been dispatched."); + +test(function (t) { + var outer = document.getElementById('outer'); + var middle = document.getElementById('middle'); + var inner = document.getElementById('inner'); + + var propagationStopper = function (e) { + e.cancelBubble = true; + }; + + // Bubble phase + middle.addEventListener('bar', propagationStopper, false/*useCapture*/); + outer.addEventListener('bar', t.step_func(function listenerOne() { + assert_unreached("Setting cancelBubble=true should stop the event from bubbling further."); + }), false/*useCapture*/); + + var barEvent = document.createEvent('Event'); + barEvent.initEvent('bar', true/*bubbles*/, false/*cancelable*/); + inner.dispatchEvent(barEvent); + + // Capture phase + outer.addEventListener('qux', propagationStopper, true/*useCapture*/); + middle.addEventListener('qux', t.step_func(function listenerTwo() { + assert_unreached("Setting cancelBubble=true should stop the event from propagating further, including during the Capture Phase."); + }), true/*useCapture*/); + + var quxEvent = document.createEvent('Event'); + quxEvent.initEvent('qux', false/*bubbles*/, false/*cancelable*/); + inner.dispatchEvent(quxEvent); +}, "Event.cancelBubble=true must set the stop propagation flag."); + </script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-bubble-canceled-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-bubble-canceled-expected.txt new file mode 100644 index 0000000..1c3c1b8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-bubble-canceled-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Setting cancelBubble=true prior to dispatchEvent() assert_array_equals: actual_targets lengths differ, expected 0 got 9 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-bubble-canceled.html b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-bubble-canceled.html new file mode 100644 index 0000000..20f398f6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-bubble-canceled.html
@@ -0,0 +1,59 @@ +<!DOCTYPE html> +<html> +<head> +<title>Setting cancelBubble=true prior to dispatchEvent()</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> + +<table id="table" border="1" style="display: none"> + <tbody id="table-body"> + <tr id="table-row"> + <td id="table-cell">Shady Grove</td> + <td>Aeolian</td> + </tr> + <tr id="parent"> + <td id="target">Over the river, Charlie</td> + <td>Dorian</td> + </tr> + </tbody> +</table> + +<script> +test(function() { + var event = "foo"; + var target = document.getElementById("target"); + var parent = document.getElementById("parent"); + var tbody = document.getElementById("table-body"); + var table = document.getElementById("table"); + var body = document.body; + var html = document.documentElement; + var current_targets = [window, document, html, body, table, tbody, parent, target]; + var expected_targets = []; + var actual_targets = []; + var expected_phases = []; + var actual_phases = []; + + var test_event = function(evt) { + actual_targets.push(evt.currentTarget); + actual_phases.push(evt.eventPhase); + }; + + for (var i = 0; i < current_targets.length; ++i) { + current_targets[i].addEventListener(event, test_event, true); + current_targets[i].addEventListener(event, test_event, false); + } + + var evt = document.createEvent("Event"); + evt.initEvent(event, true, true); + evt.cancelBubble = true; + target.dispatchEvent(evt); + + assert_array_equals(actual_targets, expected_targets, "actual_targets"); + assert_array_equals(actual_phases, expected_phases, "actual_phases"); +}); +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-multiple-cancelBubble-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-multiple-cancelBubble-expected.txt new file mode 100644 index 0000000..fc880274 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-multiple-cancelBubble-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Multiple dispatchEvent() and cancelBubble assert_array_equals: lengths differ, expected 2 got 1 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-multiple-cancelBubble.html b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-multiple-cancelBubble.html new file mode 100644 index 0000000..2873fd77 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-dispatch-multiple-cancelBubble.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> +<title>Multiple dispatchEvent() and cancelBubble</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<div id=log></div> + +<div id="parent" style="display: none"> + <input id="target" type="hidden" value=""/> +</div> + +<script> +test(function() { + var event_type = "foo"; + var target = document.getElementById("target"); + var parent = document.getElementById("parent"); + var actual_result; + var test_event = function(evt) { + actual_result.push(evt.currentTarget); + + if (parent == evt.currentTarget) { + evt.cancelBubble = true; + } + }; + + var evt = document.createEvent("Event"); + evt.initEvent(event_type, true, true); + + target.addEventListener(event_type, test_event, false); + parent.addEventListener(event_type, test_event, false); + document.addEventListener(event_type, test_event, false); + window.addEventListener(event_type, test_event, false); + + actual_result = []; + target.dispatchEvent(evt); + assert_array_equals(actual_result, [target, parent]); + + actual_result = []; + parent.dispatchEvent(evt); + assert_array_equals(actual_result, [parent]); + + actual_result = []; + document.dispatchEvent(evt); + assert_array_equals(actual_result, [document, window]); +}); +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-initEvent.html b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-initEvent.html index 85abdff2..568232a5 100644 --- a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-initEvent.html +++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-initEvent.html
@@ -85,6 +85,7 @@ var target = document.createElement("div") var called = false target.addEventListener("type", function() { called = true }, false) + assert_false(e.cancelBubble, "cancelBubble must be false") assert_true(target.dispatchEvent(e), "dispatchEvent must return true") assert_true(called, "Listener must be called") }, "Calling initEvent must unset the stop propagation flag.")
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-propagation-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-propagation-expected.txt new file mode 100644 index 0000000..718901d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-propagation-expected.txt
@@ -0,0 +1,10 @@ +This is a testharness.js-based test. +PASS Newly-created Event +PASS After stopPropagation() +PASS Reinitialized after stopPropagation() +PASS After stopImmediatePropagation() +PASS Reinitialized after stopImmediatePropagation() +FAIL After cancelBubble=true assert_equals: Propagation flag expected false but got true +PASS Reinitialized after cancelBubble=true +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-propagation.html b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-propagation.html index 459d45c..33989eb 100644 --- a/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-propagation.html +++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/events/Event-propagation.html
@@ -38,4 +38,11 @@ testPropagationFlag(ev, false, "After stopImmediatePropagation()"); ev.initEvent("foo", true, false); testPropagationFlag(ev, true, "Reinitialized after stopImmediatePropagation()"); + +var ev = document.createEvent("Event"); +ev.initEvent("foo", true, false); +ev.cancelBubble = true; +testPropagationFlag(ev, false, "After cancelBubble=true"); +ev.initEvent("foo", true, false); +testPropagationFlag(ev, true, "Reinitialized after cancelBubble=true"); </script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/gyroscope/idlharness.https-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/gyroscope/idlharness.https-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/gyroscope/idlharness.https-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/gyroscope/idlharness.https.html b/third_party/WebKit/LayoutTests/imported/wpt/gyroscope/idlharness.https.html new file mode 100644 index 0000000..53d3b81c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/gyroscope/idlharness.https.html
@@ -0,0 +1,108 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Gyroscope Sensor IDL tests</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="help" href="https://www.w3.org/TR/gyroscope/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/WebIDLParser.js"></script> +<script src="/resources/idlharness.js"></script> +<style> + pre { + display: none; + } +</style> +<div id="log"></div> + +<pre id="idl"> +interface Event { +}; + +interface EventTarget { +}; + +interface EventHandler { +}; + +interface Error { +}; + +dictionary EventInit { +}; +</pre> + +<pre id="generic-idl"> +[SecureContext] +interface Sensor : EventTarget { + readonly attribute SensorState state; + readonly attribute SensorReading? reading; + void start(); + void stop(); + attribute EventHandler onchange; + attribute EventHandler onactivate; + attribute EventHandler onerror; +}; + +dictionary SensorOptions { + double? frequency; +}; + +enum SensorState { + "idle", + "activating", + "activated", + "errored" +}; + +[SecureContext] +interface SensorReading { + readonly attribute DOMHighResTimeStamp timeStamp; +}; + +[SecureContext, Constructor(DOMString type, SensorErrorEventInit errorEventInitDict)] +interface SensorErrorEvent : Event { + readonly attribute Error error; +}; + +dictionary SensorErrorEventInit : EventInit { + required Error error; +}; + +</pre> + +<pre id="gyroscope-idl"> +[Constructor(optional SensorOptions sensorOptions)] +interface Gyroscope : Sensor { + readonly attribute GyroscopeReading? reading; +}; + +[Constructor(GyroscopeReadingInit GyroscopeReadingInit)] +interface GyroscopeReading : SensorReading { + readonly attribute unrestricted double x; + readonly attribute unrestricted double y; + readonly attribute unrestricted double z; +}; + +dictionary GyroscopeReadingInit { + unrestricted double x = 0; + unrestricted double y = 0; + unrestricted double z = 0; +}; +</pre> + +<script> +(function() { + "use strict"; + var idl_array = new IdlArray(); + idl_array.add_untested_idls(document.getElementById('idl').textContent); + idl_array.add_untested_idls(document.getElementById('generic-idl').textContent); + idl_array.add_idls(document.getElementById('gyroscope-idl').textContent); + + idl_array.add_objects({ + Gyroscope: ['new Gyroscope();'], + GyroscopeReading: ['new GyroscopeReading({x: 0.5, y: 0.5, z: 0.5});'] + }); + + idl_array.test(); +})(); +</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/magnetometer/idlharness.https-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/magnetometer/idlharness.https-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/magnetometer/idlharness.https-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/magnetometer/idlharness.https.html b/third_party/WebKit/LayoutTests/imported/wpt/magnetometer/idlharness.https.html new file mode 100644 index 0000000..00fdafe --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/magnetometer/idlharness.https.html
@@ -0,0 +1,108 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Magnetometer Sensor IDL tests</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="help" href="https://www.w3.org/TR/magnetometer/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/WebIDLParser.js"></script> +<script src="/resources/idlharness.js"></script> +<style> + pre { + display: none; + } +</style> +<div id="log"></div> + +<pre id="idl"> +interface Event { +}; + +interface EventTarget { +}; + +interface EventHandler { +}; + +interface Error { +}; + +dictionary EventInit { +}; +</pre> + +<pre id="generic-idl"> +[SecureContext] +interface Sensor : EventTarget { + readonly attribute SensorState state; + readonly attribute SensorReading? reading; + void start(); + void stop(); + attribute EventHandler onchange; + attribute EventHandler onactivate; + attribute EventHandler onerror; +}; + +dictionary SensorOptions { + double? frequency; +}; + +enum SensorState { + "idle", + "activating", + "activated", + "errored" +}; + +[SecureContext] +interface SensorReading { + readonly attribute DOMHighResTimeStamp timeStamp; +}; + +[SecureContext, Constructor(DOMString type, SensorErrorEventInit errorEventInitDict)] +interface SensorErrorEvent : Event { + readonly attribute Error error; +}; + +dictionary SensorErrorEventInit : EventInit { + required Error error; +}; + +</pre> + +<pre id="magnetometer-idl"> +[Constructor(optional SensorOptions sensorOptions)] +interface Magnetometer : Sensor { + readonly attribute MagnetometerReading? reading; +}; + +[Constructor(MagnetometerReadingInit magnetometerReadingInit)] +interface MagnetometerReading : SensorReading { + readonly attribute double x; + readonly attribute double y; + readonly attribute double z; +}; + +dictionary MagnetometerReadingInit { + double x = 0; + double y = 0; + double z = 0; +}; +</pre> + +<script> +(function() { + "use strict"; + var idl_array = new IdlArray(); + idl_array.add_untested_idls(document.getElementById('idl').textContent); + idl_array.add_untested_idls(document.getElementById('generic-idl').textContent); + idl_array.add_idls(document.getElementById('magnetometer-idl').textContent); + + idl_array.add_objects({ + Magnetometer: ['new Magnetometer();'], + MagnetometerReading: ['new MagnetometerReading({x: 0.5, y: 0.5, z: 0.5});'] + }); + + idl_array.test(); +})(); +</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/avoid_delaying_onload_link_preload-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/avoid_delaying_onload_link_preload-expected.txt new file mode 100644 index 0000000..94f13eaa --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/avoid_delaying_onload_link_preload-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Makes sure link preload preloaded resources are not delaying onload assert_equals: expected 2 but got 0 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/avoid_delaying_onload_link_preload.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/avoid_delaying_onload_link_preload.html new file mode 100644 index 0000000..e92658dd2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/avoid_delaying_onload_link_preload.html
@@ -0,0 +1,13 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure link preload preloaded resources are not delaying onload'); +</script> +<link rel=preload href="resources/dummy.js?pipe=trickle(d5)" as=script> +<script> + window.addEventListener("load", t.step_func(function() { + assert_equals(performance.getEntriesByType("resource").length, 2); + t.done(); + })); +</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/delaying_onload_link_preload_after_discovery-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/delaying_onload_link_preload_after_discovery-expected.txt new file mode 100644 index 0000000..497aad5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/delaying_onload_link_preload_after_discovery-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Makes sure link preload preloaded resources are delaying onload after discovery assert_equals: expected 4 but got 0 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/delaying_onload_link_preload_after_discovery.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/delaying_onload_link_preload_after_discovery.html new file mode 100644 index 0000000..44a8cef --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/delaying_onload_link_preload_after_discovery.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure link preload preloaded resources are delaying onload after discovery'); +</script> +<link rel=preload href="resources/dummy.js?pipe=trickle(d5)" as=script> +<link rel=preload href="resources/square.png?pipe=trickle(d5)" as=image> +<body> +<script> + window.addEventListener("load", t.step_func(function() { + assert_equals(performance.getEntriesByType("resource").length, 4); + t.done(); + })); + var script = document.createElement("script"); + script.src = "resources/dummy.js?pipe=trickle(d5)"; + document.body.appendChild(script); + var img = new Image(); + img.src = "resources/square.png?pipe=trickle(d5)"; +</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/download_resources-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/download_resources-expected.txt new file mode 100644 index 0000000..d0b9ad4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/download_resources-expected.txt
@@ -0,0 +1,5 @@ +CONSOLE WARNING: line 15: <link rel=preload> must have a valid `as` value +This is a testharness.js-based test. +FAIL Makes sure that preloaded resources are downloaded assert_equals: expected 11 but got 0 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/download_resources.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/download_resources.html new file mode 100644 index 0000000..a5f275a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/download_resources.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that preloaded resources are downloaded'); +</script> +<link rel=preload href="resources/dummy.js" as=script> +<link rel=preload href="resources/dummy.css" as=style> +<link rel=preload href="resources/square.png" as=image> +<link rel=preload href="/fonts/CanvasTest.ttf" as=font crossorigin> +<link rel=preload href="/media/white.mp4" as=media> +<link rel=preload href="/media/sound_5.oga" as=media> +<link rel=preload href="/media/foo.vtt" as=media> +<link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing> +<link rel=preload href="resources/dummy.xml"> +<body> +<script src="resources/dummy.js?pipe=trickle(d5)"></script> +<script> + window.addEventListener("load", t.step_func(function() { + var entries = performance.getEntriesByType("resource"); + assert_equals(performance.getEntriesByType("resource").length, 11); + t.done(); + })); +</script> +</body>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/dynamic_adding_preload-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/dynamic_adding_preload-expected.txt new file mode 100644 index 0000000..cb06632 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/dynamic_adding_preload-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Makes sure that a dynamically added preloaded resource is downloaded assert_equals: expected 3 but got 0 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/dynamic_adding_preload.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/dynamic_adding_preload.html new file mode 100644 index 0000000..3b341b5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/dynamic_adding_preload.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that a dynamically added preloaded resource is downloaded'); +</script> +<body> +<script> + t.step(function() { + var link = document.createElement("link"); + assert_true(link.relList && link.relList.supports && link.relList.supports("preload"), "relList is not defined or browser doesn't support preload."); + link.as = "script"; + link.rel = "preload"; + link.href = "resources/dummy.js"; + link.onload = t.step_func(function() { + t.step_timeout(function() { + assert_equals(performance.getEntriesByType("resource").length, 3); + t.done(); + }, 0); + }); + document.body.appendChild(link); + }); +</script> +</body>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload-expected.txt new file mode 100644 index 0000000..12f8bbf8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Makes sure that Link headers preload resources assert_equals: expected 6 but got 0 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload.html new file mode 100644 index 0000000..a63ffde --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload.html
@@ -0,0 +1,16 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that Link headers preload resources'); +</script> +<body> +<script src="resources/dummy.js?pipe=trickle(d5)"></script> +<script> + window.addEventListener("load", t.step_func(function() { + var entries = performance.getEntriesByType("resource"); + assert_equals(entries.length, 6); + t.done(); + })); +</script> +</body>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload.html.headers b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload.html.headers new file mode 100644 index 0000000..e61b22e0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload.html.headers
@@ -0,0 +1,4 @@ +Link: </preload/resources/dummy.js>;rel=preload;as=script +Link: </preload/resources/dummy.css>;rel=preload;as=style +Link: </preload/resources/square.png>;rel=preload;as=image +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload_delay_onload-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload_delay_onload-expected.txt new file mode 100644 index 0000000..75961b8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload_delay_onload-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Makes sure that Link headers preload resources and block window.onload after resource discovery assert_true: expected true got false +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload_delay_onload.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload_delay_onload.html new file mode 100644 index 0000000..2ee24b8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload_delay_onload.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that Link headers preload resources and block window.onload after resource discovery'); +</script> +<body> +<style> + #background { + width: 200px; + height: 200px; + background-image: url(resources/square.png?background); + } +</style> +<link rel="stylesheet" href="resources/dummy.css"> +<script src="resources/dummy.js"></script> +<div id="background"></div> +<script> + document.write('<img src="resources/square.png">'); + window.addEventListener("load", t.step_func(function() { + var entries = performance.getEntriesByType("resource"); + var found_background_first = false; + for (var i = 0; i < entries.length; ++i) { + var entry = entries[i]; + if (entry.name.indexOf("square") != -1) { + if (entry.name.indexOf("background") != -1) + found_background_first = true; + } + } + assert_true(found_background_first); + assert_equals(entries.length, 6); + t.done(); + })); +</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload_delay_onload.html.headers b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload_delay_onload.html.headers new file mode 100644 index 0000000..6dad172 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/link_header_preload_delay_onload.html.headers
@@ -0,0 +1,5 @@ +Link: </preload/resources/square.png?background>;rel=preload;as=image +Link: </preload/resources/dummy.js>;rel=preload;as=script +Link: </preload/resources/dummy.css>;rel=preload;as=style +Link: </preload/resources/square.png>;rel=preload;as=image +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/onerror_event-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/onerror_event-expected.txt new file mode 100644 index 0000000..2e89d50 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/onerror_event-expected.txt
@@ -0,0 +1,7 @@ +CONSOLE WARNING: line 23: <link rel=preload> must have a valid `as` value +CONSOLE WARNING: line 24: <link rel=preload> must have a valid `as` value +CONSOLE WARNING: line 26: <link rel=preload> must have a valid `as` value +This is a testharness.js-based test. +FAIL Makes sure that preloaded resources trigger the onerror event assert_true: style triggered error event expected true got false +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/onerror_event.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/onerror_event.html new file mode 100644 index 0000000..cb2d9ff --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/onerror_event.html
@@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<head></head> +<body> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that preloaded resources trigger the onerror event'); + var scriptFailed = false; + var styleFailed = false; + var imageFailed = false; + var fontFailed = false; + var videoFailed = false; + var audioFailed = false; + var trackFailed = false; + var gibberishFailed = false; + var noTypeFailed = false; +</script> +<link rel=preload href="non-existent/dummy.js" as=script onerror="scriptFailed = true;"> +<link rel=preload href="non-existent/dummy.css" as=style onerror="styleFailed = true;"> +<link rel=preload href="non-existent/square.png" as=image onerror="imageFailed = true;"> +<link rel=preload href="non-existent/Ahem.ttf" as=font crossorigin onerror="fontFailed = true;"> +<link rel=preload href="non-existent/test.mp4" as=video onerror="videoFailed = true;"> +<link rel=preload href="non-existent/test.oga" as=audio onerror="audioFailed = true;"> +<link rel=preload href="non-existent/security/captions.vtt" as=media onerror="trackFailed = true;"> +<link rel=preload href="non-existent/dummy.xml" as=foobarxmlthing onerror="gibberishFailed = true;"> +<link rel=preload href="non-existent/dummy.xml" onerror="noTypeFailed = true;"> +<script src="resources/dummy.js?pipe=trickle(d5)"></script> +<script> + window.onload = t.step_func(function() { + assert_true(styleFailed, "style triggered error event"); + assert_true(scriptFailed, "script triggered error event"); + assert_true(imageFailed, "image triggered error event"); + assert_true(fontFailed, "font triggered error event"); + assert_true(videoFailed, "video triggered error event"); + assert_true(audioFailed, "audio triggered error event"); + assert_true(trackFailed, "track triggered error event"); + assert_true(gibberishFailed, "gibberish as value triggered error event"); + assert_true(noTypeFailed, "empty as triggered error event"); + t.done(); + }); +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/onload_event-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/onload_event-expected.txt new file mode 100644 index 0000000..16ff81f3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/onload_event-expected.txt
@@ -0,0 +1,5 @@ +CONSOLE WARNING: line 24: <link rel=preload> must have a valid `as` value +This is a testharness.js-based test. +FAIL Makes sure that preloaded resources trigger the onload event assert_true: style triggered load event expected true got false +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/onload_event.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/onload_event.html new file mode 100644 index 0000000..2f19cc2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/onload_event.html
@@ -0,0 +1,43 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that preloaded resources trigger the onload event'); + var scriptLoaded = false; + var styleLoaded = false; + var imageLoaded = false; + var fontLoaded = false; + var videoLoaded = false; + var audioLoaded = false; + var trackLoaded = false; + var gibberishLoaded = false; + var gibberishErrored = false; + var noTypeLoaded = false; +</script> +<link rel=preload href="resources/dummy.js" as=script onload="scriptLoaded = true;"> +<link rel=preload href="resources/dummy.css" as=style onload="styleLoaded = true;"> +<link rel=preload href="resources/square.png" as=image onload="imageLoaded = true;"> +<link rel=preload href="/fonts/CanvasTest.ttf" as=font crossorigin onload="fontLoaded = true;"> +<link rel=preload href="/media/white.mp4" as=media onload="videoLoaded = true;"> +<link rel=preload href="/media/sound_5.oga" as=media onload="audioLoaded = true;"> +<link rel=preload href="/media/foo.vtt" as=media onload="trackLoaded = true;"> +<link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing onload="gibberishLoaded = true;" onerror="gibberishErrored = true;"> +<link rel=preload href="resources/dummy.xml" onload="noTypeLoaded = true;"> +<body> +<script src="resources/dummy.js?pipe=trickle(d5)"></script> +<script> + window.onload = t.step_func(function() { + assert_true(styleLoaded, "style triggered load event"); + assert_true(scriptLoaded, "script triggered load event"); + assert_true(imageLoaded, "image triggered load event"); + assert_true(fontLoaded, "font triggered load event"); + assert_true(videoLoaded, "video triggered load event"); + assert_true(audioLoaded, "audio triggered load event"); + assert_true(trackLoaded, "track triggered load event"); + assert_false(gibberishLoaded, "gibberish as value triggered load event"); + assert_true(gibberishErrored, "gibberish as value triggered error event"); + assert_true(noTypeLoaded, "empty as triggered load event"); + t.done(); + }); +</script> +</body>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/preload-csp.sub.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/preload-csp.sub.html new file mode 100644 index 0000000..3f3b42e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/preload-csp.sub.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; font-src 'none'; style-src 'none'; img-src 'none'; media-src 'none'; connect-src 'none'"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that preload requests respect CSP'); +</script> +<link rel=preload href="{{host}}:{{ports[http][1]}}/preload/resources/dummy.js" as=style> +<link rel=preload href="resources/dummy.css" as=style> +<link rel=preload href="resources/square.png" as=image> +<link rel=preload href="/fonts/CanvasTest.ttf" as=font crossorigin> +<link rel=preload href="/media/white.mp4" as=media> +<link rel=preload href="/media/sound_5.oga" as=media> +<link rel=preload href="/media/foo.vtt" as=media> +<link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing> +<link rel=preload href="resources/dummy.xml"> +<body> +<script src="resources/dummy.js?pipe=trickle(d5)"></script> +<script> + var assertRequestTerminated = function(url) { + var entries = performance.getEntriesByName(new URL(url, location.href).href); + assert_equals(entries.length, 0); + }; + window.onload = t.step_func(function() { + assertRequestTerminated("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js"); + assertRequestTerminated("resources/dummy.css"); + assertRequestTerminated("resources/square.png"); + assertRequestTerminated("/fonts/CanvasTest.ttf"); + assertRequestTerminated("/media/white.mp4"); + assertRequestTerminated("/media/sound_5.oga"); + assertRequestTerminated("/media/foo.vtt"); + assertRequestTerminated("resources/dummy.xml"); + t.done(); + }); +</script> +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/preload-default-csp.sub.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/preload-default-csp.sub.html new file mode 100644 index 0000000..76c1b53 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/preload-default-csp.sub.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; default-src 'none'"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that preload requests respect CSP'); +</script> +<link rel=preload href="{{host}}:{{ports[http][1]}}/preload/resources/dummy.js" as=style> +<link rel=preload href="resources/dummy.css" as=style> +<link rel=preload href="resources/square.png" as=image> +<link rel=preload href="/fonts/CanvasTest.ttf" as=font crossorigin> +<link rel=preload href="/media/white.mp4" as=media> +<link rel=preload href="/media/sound_5.oga" as=media> +<link rel=preload href="/media/foo.vtt" as=media> +<link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing> +<link rel=preload href="resources/dummy.xml"> +<body> +<script src="resources/dummy.js?pipe=trickle(d5)"></script> +<script> + var assertRequestTerminated = function(url) { + var entries = performance.getEntriesByName(new URL(url, location.href).href); + assert_equals(entries.length, 0); + }; + window.onload = t.step_func(function() { + assertRequestTerminated("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js"); + assertRequestTerminated("resources/dummy.css"); + assertRequestTerminated("resources/square.png"); + assertRequestTerminated("/fonts/CanvasTest.ttf"); + assertRequestTerminated("/media/white.mp4"); + assertRequestTerminated("/media/sound_5.oga"); + assertRequestTerminated("/media/foo.vtt"); + assertRequestTerminated("resources/dummy.xml"); + t.done(); + }); +</script> +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/preload_with_type-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/preload_with_type-expected.txt new file mode 100644 index 0000000..6cef0b3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/preload_with_type-expected.txt
@@ -0,0 +1,12 @@ +CONSOLE WARNING: line 38: <link rel=preload> has an unsupported `type` value +CONSOLE WARNING: line 39: <link rel=preload> has an unsupported `type` value +CONSOLE WARNING: line 40: <link rel=preload> has an unsupported `type` value +CONSOLE WARNING: line 41: <link rel=preload> has an unsupported `type` value +CONSOLE WARNING: line 42: <link rel=preload> has an unsupported `type` value +CONSOLE WARNING: line 44: <link rel=preload> has an unsupported `type` value +CONSOLE WARNING: line 45: <link rel=preload> has an unsupported `type` value +CONSOLE WARNING: line 47: <link rel=preload> has an unsupported `type` value +This is a testharness.js-based test. +FAIL Makes sure that preloaded resources with a type attribute trigger the onload event assert_true: style triggered load event expected true got false +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/preload_with_type.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/preload_with_type.html new file mode 100644 index 0000000..1678258 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/preload_with_type.html
@@ -0,0 +1,64 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/media.js"></script> +<script> + var t = async_test('Makes sure that preloaded resources with a type attribute trigger the onload event'); + var scriptLoaded = false; + var styleLoaded = false; + var imageLoaded = false; + var fontLoaded = false; + var videoLoaded = false; + var audioLoaded = false; + var trackLoaded = false; + var gibberishLoaded = 0; + var getFormat = function(url) { + var dot = url.lastIndexOf('.'); + if (dot != -1) { + var extension = url.substring(dot + 1); + if (extension.startsWith("og")) + return "ogg"; + return extension; + } + return null; + }; + var videoURL = getVideoURI("/media/A4"); + var audioURL = getAudioURI("/media/sound_5"); + var videoFormat = getFormat(videoURL); + var audioFormat = getFormat(audioURL); +</script> +<link rel=preload href="resources/dummy.js" as=script type="text/javascript" onload="scriptLoaded = true;"> +<link rel=preload href="resources/dummy.css" as=style type="text/css" onload="styleLoaded = true;"> +<link rel=preload href="resources/square.png" as=image type="image/png" onload="imageLoaded = true;"> +<link rel=preload href="/fonts/CanvasTest.ttf" as=font type="font/ttf" crossorigin onload="fontLoaded = true;"> +<script> + document.write('<link rel=preload href="' + videoURL + '" as=media type="video/' + videoFormat + '" onload="videoLoaded = true;">'); + document.write('<link rel=preload href="' + audioURL + '" as=media type="audio/' + audioFormat + '" onload="audioLoaded = true;">'); +</script> +<link rel=preload href="/media/foo.vtt" as=media type="text/vtt" onload="trackLoaded = true;"> +<link rel=preload href="resources/dummy.js" as=script type="application/foobar" onload="gibberishLoaded++;"> +<link rel=preload href="resources/dummy.css" as=style type="text/foobar" onload="gibberishLoaded++;"> +<link rel=preload href="resources/square.png" as=image type="image/foobar" onload="gibberishLoaded++;"> +<link rel=preload href="/fonts/CanvasTest.ttf" as=font type="font/foobar" crossorigin onload="gibberishLoaded++;"> +<script> + document.write('<link rel=preload href="' + videoURL + '" as=media type="video/foobar" onload="gibberishLoaded++;">'); + document.write('<link rel=preload href="' + audioURL + '" as=media type="audio/foobar" onload="gibberishLoaded++;">'); +</script> +<link rel=preload href="/media/foo.vtt" as=media type="text/foobar" onload="gibberishLoaded++;"> +<body> +<script src="resources/dummy.js?pipe=trickle(d5)"></script> +<script> + window.onload = t.step_func(function() { + assert_true(styleLoaded, "style triggered load event"); + assert_true(scriptLoaded, "script triggered load event"); + assert_true(imageLoaded, "image triggered load event"); + assert_true(fontLoaded, "font triggered load event"); + assert_true(videoLoaded, "video triggered load event"); + assert_true(audioLoaded, "audio triggered load event"); + assert_true(trackLoaded, "track triggered load event"); + assert_equals(gibberishLoaded, 0, "resources with gibberish type should not be loaded"); + t.done(); + }); +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/dummy.css b/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/dummy.css new file mode 100644 index 0000000..5097166a0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/dummy.css
@@ -0,0 +1 @@ +/* This is just a dummy, empty CSS file */
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/dummy.js b/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/dummy.js new file mode 100644 index 0000000..cfcb9d8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/dummy.js
@@ -0,0 +1 @@ +// This is example JS content. Nothing to see here.
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/dummy.xml b/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/dummy.xml new file mode 100644 index 0000000..0d88d0c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/dummy.xml
@@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<root>Text.me</root>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/square.png b/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/square.png new file mode 100644 index 0000000..01c9666a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/resources/square.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_late_used_preload-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_late_used_preload-expected.txt new file mode 100644 index 0000000..7f916fd51 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_late_used_preload-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Makes sure that preloaded resources are not downloaded again when used assert_equals: expected 3 but got 0 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_late_used_preload.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_late_used_preload.html new file mode 100644 index 0000000..64e7085 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_late_used_preload.html
@@ -0,0 +1,19 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that preloaded resources are not downloaded again when used'); +</script> +<div id=result></div> +<link rel=preload href="resources/square.png" as=image> +<script> + window.addEventListener("load", t.step_func(function() { + t.step_timeout(function() { + document.getElementById("result").innerHTML = "<image src='resources/square.png'>"; + t.step_timeout(function() { + assert_equals(performance.getEntriesByType("resource").length, 3); + t.done(); + }, 1000); + }, 1000); + })); +</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_preload-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_preload-expected.txt new file mode 100644 index 0000000..b1dcffab --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_preload-expected.txt
@@ -0,0 +1,5 @@ +CONSOLE WARNING: line 15: <link rel=preload> must have a valid `as` value +This is a testharness.js-based test. +FAIL Makes sure that preloaded resources are not downloaded again when used assert_equals: expected 14 but got 0 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_preload.html b/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_preload.html new file mode 100644 index 0000000..0c20194 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/preload/single_download_preload.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + var t = async_test('Makes sure that preloaded resources are not downloaded again when used'); +</script> +<link rel=preload href="resources/dummy.js" as=script> +<link rel=preload href="resources/dummy.css" as=style> +<link rel=preload href="resources/square.png" as=image> +<link rel=preload href="resources/square.png?background" as=image> +<link rel=preload href="/fonts/CanvasTest.ttf" as=font crossorigin> +<link rel=preload href="/media/white.mp4" as=media> +<link rel=preload href="/media/sound_5.oga" as=media> +<link rel=preload href="/media/foo.vtt" as=media> +<link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing> +<link rel=preload href="resources/dummy.xml"> +<body> +<script src="resources/dummy.js?pipe=trickle(d5)"></script> +<style> + #background { + width: 200px; + height: 200px; + background-image: url(resources/square.png?background); + } + @font-face { + font-family:ahem; + src: url(/fonts/CanvasTest.ttf); + } + span { font-family: ahem, Arial; } +</style> +<link rel="stylesheet" href="resources/dummy.css"> +<script src="resources/dummy.js"></script> +<div id="background"></div> +<img src="resources/square.png"> +<video src="/media/white.mp4"> + <track kind=subtitles src="/media/foo.vtt" srclang=en> +</video> +<audio src="/media/sound_5.oga"></audio> +<script> + var xhr = new XMLHttpRequest(); + xhr.open("GET", "resources/dummy.xml"); + xhr.send(); + + window.addEventListener("load", t.step_func(function() { + // Audio and video show 2 extra requests as the main request is followed by a range request + assert_equals(performance.getEntriesByType("resource").length, 14); + t.done(); + })); +</script> +<span>PASS - this text is here just so that the browser will download the font.</span>
diff --git a/third_party/WebKit/LayoutTests/sensor/resources/generic-sensor-tests.js b/third_party/WebKit/LayoutTests/sensor/resources/generic-sensor-tests.js index 52d878d..ee5f312 100644 --- a/third_party/WebKit/LayoutTests/sensor/resources/generic-sensor-tests.js +++ b/third_party/WebKit/LayoutTests/sensor/resources/generic-sensor-tests.js
@@ -304,10 +304,7 @@ // By the moment slow sensor (9 Hz) is notified for the // next time, the fast sensor (30 Hz) has been notified // for int(30/9) = 3 times. - // In actual implementation updates are bound to rAF, - // (not to a timer) sometimes fast sensor gets invoked 4 times. - assert_true(fastSensorNotifiedCounter == 3 || - fastSensorNotifiedCounter == 4); + assert_equals(fastSensorNotifiedCounter, 3); fastSensor.stop(); slowSensor.stop(); resolve(mockSensor);
diff --git a/third_party/WebKit/Source/core/dom/StyleEngine.cpp b/third_party/WebKit/Source/core/dom/StyleEngine.cpp index 63ed121d..045bf79 100644 --- a/third_party/WebKit/Source/core/dom/StyleEngine.cpp +++ b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
@@ -460,6 +460,7 @@ clearResolver(); m_viewportResolver = nullptr; m_mediaQueryEvaluator = nullptr; + clearFontCache(); } void StyleEngine::clearFontCache() {
diff --git a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp index a9ae3ce..407f538 100644 --- a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp +++ b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp
@@ -191,15 +191,8 @@ FrameHost* oldFrameHost, FrameHost* newFrameHost) { ASSERT(newFrameHost != oldFrameHost); - for (size_t i = 0; i < EventHandlerClassCount; ++i) { - EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i); - const EventTargetSet* targets = - &oldFrameHost->eventHandlerRegistry().m_targets[handlerClass]; - for (unsigned count = targets->count(&target); count > 0; --count) - newFrameHost->eventHandlerRegistry().didAddEventHandler(target, - handlerClass); - } - oldFrameHost->eventHandlerRegistry().didRemoveAllEventHandlers(target); + oldFrameHost->eventHandlerRegistry().didMoveOutOfFrameHost(target); + newFrameHost->eventHandlerRegistry().didMoveIntoFrameHost(target); } void EventHandlerRegistry::didRemoveAllEventHandlers(EventTarget& target) {
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp index f2371c9..cd50a3b 100644 --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -1488,18 +1488,17 @@ } if (error == WebMediaPlayer::NetworkStateNetworkError && - m_readyState >= kHaveMetadata) + m_readyState >= kHaveMetadata) { mediaEngineError(MediaError::create(MediaError::kMediaErrNetwork)); - else if (error == WebMediaPlayer::NetworkStateDecodeError) + } else if (error == WebMediaPlayer::NetworkStateDecodeError) { mediaEngineError(MediaError::create(MediaError::kMediaErrDecode)); - else if ((error == WebMediaPlayer::NetworkStateFormatError || - error == WebMediaPlayer::NetworkStateNetworkError) && - m_loadState == LoadingFromSrcAttr) + } else if ((error == WebMediaPlayer::NetworkStateFormatError || + error == WebMediaPlayer::NetworkStateNetworkError) && + m_loadState == LoadingFromSrcAttr) { noneSupported(); + } updateDisplayState(); - if (mediaControls()) - mediaControls()->reset(); } void HTMLMediaElement::setNetworkState(WebMediaPlayer::NetworkState state) { @@ -1658,8 +1657,6 @@ jumped = true; } - if (mediaControls()) - mediaControls()->reset(); if (layoutObject()) layoutObject()->updateFromElement(); } @@ -3852,11 +3849,12 @@ } void HTMLMediaElement::setNetworkState(NetworkState state) { - if (m_networkState != state) { - m_networkState = state; - if (MediaControls* controls = mediaControls()) - controls->networkStateChanged(); - } + if (m_networkState == state) + return; + + m_networkState = state; + if (mediaControls()) + mediaControls()->networkStateChanged(); } void HTMLMediaElement::videoWillBeDrawnToCanvas() const {
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp index ded935a..bd03224 100644 --- a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp +++ b/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
@@ -748,6 +748,18 @@ m_toggleClosedCaptionsButton->updateDisplayType(); } +void MediaControls::onError() { + // TODO(mlamouri): we should only change the aspects of the control that need + // to be changed. + reset(); +} + +void MediaControls::onLoadedMetadata() { + // TODO(mlamouri): we should only change the aspects of the control that need + // to be changed. + reset(); +} + void MediaControls::notifyPanelWidthChanged(const LayoutUnit& newWidth) { // Don't bother to do any work if this matches the most recent panel // width, since we're called after layout.
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControls.h b/third_party/WebKit/Source/core/html/shadow/MediaControls.h index 636e41a7..5112210 100644 --- a/third_party/WebKit/Source/core/html/shadow/MediaControls.h +++ b/third_party/WebKit/Source/core/html/shadow/MediaControls.h
@@ -160,6 +160,8 @@ void onPause(); void onTextTracksAddedOrRemoved(); void onTextTracksChanged(); + void onError(); + void onLoadedMetadata(); Member<HTMLMediaElement> m_mediaElement;
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp index d440d05..b2a71ec3 100644 --- a/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp +++ b/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp
@@ -26,6 +26,10 @@ false); m_mediaControls->m_mediaElement->addEventListener( EventTypeNames::durationchange, this, false); + m_mediaControls->m_mediaElement->addEventListener(EventTypeNames::error, this, + false); + m_mediaControls->m_mediaElement->addEventListener( + EventTypeNames::loadedmetadata, this, false); // TextTracks events. TextTrackList* textTracks = m_mediaControls->m_mediaElement->textTracks(); @@ -66,6 +70,14 @@ m_mediaControls->onPause(); return; } + if (event->type() == EventTypeNames::error) { + m_mediaControls->onError(); + return; + } + if (event->type() == EventTypeNames::loadedmetadata) { + m_mediaControls->onLoadedMetadata(); + return; + } // TextTracks events. if (event->type() == EventTypeNames::addtrack ||
diff --git a/third_party/WebKit/Source/modules/sensor/BUILD.gn b/third_party/WebKit/Source/modules/sensor/BUILD.gn index 6f59d028..8bc2ebb 100644 --- a/third_party/WebKit/Source/modules/sensor/BUILD.gn +++ b/third_party/WebKit/Source/modules/sensor/BUILD.gn
@@ -32,8 +32,8 @@ "SensorProxy.h", "SensorReading.cpp", "SensorReading.h", - "SensorReadingUpdater.cpp", - "SensorReadingUpdater.h", + "SensorUpdateNotificationStrategy.cpp", + "SensorUpdateNotificationStrategy.h", ] deps = [
diff --git a/third_party/WebKit/Source/modules/sensor/Sensor.cpp b/third_party/WebKit/Source/modules/sensor/Sensor.cpp index 5f83ae8..62e2a5c 100644 --- a/third_party/WebKit/Source/modules/sensor/Sensor.cpp +++ b/third_party/WebKit/Source/modules/sensor/Sensor.cpp
@@ -13,6 +13,7 @@ #include "modules/sensor/SensorErrorEvent.h" #include "modules/sensor/SensorProviderProxy.h" #include "modules/sensor/SensorReading.h" +#include "modules/sensor/SensorUpdateNotificationStrategy.h" using namespace device::mojom::blink; @@ -25,8 +26,7 @@ : ContextLifecycleObserver(executionContext), m_sensorOptions(sensorOptions), m_type(type), - m_state(Sensor::SensorState::Idle), - m_lastUpdateTimestamp(0.0) { + m_state(Sensor::SensorState::Idle) { // Check secure context. String errorMessage; if (!executionContext->isSecureContext(errorMessage)) { @@ -164,7 +164,7 @@ m_sensorProxy = provider->getSensorProxy(m_type); if (!m_sensorProxy) { - m_sensorProxy = provider->createSensorProxy(m_type, document, + m_sensorProxy = provider->createSensorProxy(m_type, document->page(), createSensorReadingFactory()); } } @@ -182,15 +182,10 @@ startListening(); } -void Sensor::onSensorReadingChanged(double timestamp) { - if (m_state != Sensor::SensorState::Activated) - return; - - DCHECK_GT(m_configuration->frequency, 0.0); - double period = 1 / m_configuration->frequency; - if (timestamp - m_lastUpdateTimestamp >= period) { - m_lastUpdateTimestamp = timestamp; - notifySensorReadingChanged(); +void Sensor::onSensorReadingChanged() { + if (m_state == Sensor::SensorState::Activated) { + DCHECK(m_sensorUpdateNotifier); + m_sensorUpdateNotifier->onSensorReadingChanged(); } } @@ -198,6 +193,8 @@ const String& sanitizedMessage, const String& unsanitizedMessage) { reportError(code, sanitizedMessage, unsanitizedMessage); + if (m_sensorUpdateNotifier) + m_sensorUpdateNotifier->cancelPendingNotifications(); } void Sensor::onStartRequestCompleted(bool result) { @@ -211,6 +208,13 @@ return; } + DCHECK(m_configuration); + DCHECK(m_sensorProxy); + auto updateCallback = + WTF::bind(&Sensor::onSensorUpdateNotification, wrapWeakPersistent(this)); + DCHECK_GT(m_configuration->frequency, 0); + m_sensorUpdateNotifier = SensorUpdateNotificationStrategy::create( + m_configuration->frequency, std::move(updateCallback)); updateState(Sensor::SensorState::Activated); } @@ -241,6 +245,9 @@ DCHECK(m_sensorProxy); updateState(Sensor::SensorState::Idle); + if (m_sensorUpdateNotifier) + m_sensorUpdateNotifier->cancelPendingNotifications(); + if (m_sensorProxy->isInitialized()) { DCHECK(m_configuration); m_sensorProxy->removeConfiguration(m_configuration->Clone()); @@ -248,6 +255,25 @@ m_sensorProxy->removeObserver(this); } +void Sensor::onSensorUpdateNotification() { + if (m_state != Sensor::SensorState::Activated) + return; + + DCHECK(m_sensorProxy); + DCHECK(m_sensorProxy->isInitialized()); + DCHECK(m_sensorProxy->sensorReading()); + + if (getExecutionContext() && + m_sensorProxy->sensorReading()->isReadingUpdated(m_storedData)) { + getExecutionContext()->postTask( + TaskType::Sensor, BLINK_FROM_HERE, + createSameThreadTask(&Sensor::notifySensorReadingChanged, + wrapWeakPersistent(this))); + } + + m_storedData = m_sensorProxy->sensorReading()->data(); +} + void Sensor::updateState(Sensor::SensorState newState) { if (newState == m_state) return; @@ -277,14 +303,13 @@ } } -void Sensor::notifySensorReadingChanged() { - DCHECK(m_sensorProxy); - DCHECK(m_sensorProxy->sensorReading()); +void Sensor::onSuspended() { + if (m_sensorUpdateNotifier) + m_sensorUpdateNotifier->cancelPendingNotifications(); +} - if (m_sensorProxy->sensorReading()->isReadingUpdated(m_storedData)) { - m_storedData = m_sensorProxy->sensorReading()->data(); - dispatchEvent(Event::create(EventTypeNames::change)); - } +void Sensor::notifySensorReadingChanged() { + dispatchEvent(Event::create(EventTypeNames::change)); } void Sensor::notifyOnActivate() {
diff --git a/third_party/WebKit/Source/modules/sensor/Sensor.h b/third_party/WebKit/Source/modules/sensor/Sensor.h index b599797..1e2e7ef 100644 --- a/third_party/WebKit/Source/modules/sensor/Sensor.h +++ b/third_party/WebKit/Source/modules/sensor/Sensor.h
@@ -20,6 +20,7 @@ class ExceptionState; class ExecutionContext; class SensorReading; +class SensorUpdateNotificationStrategy; class Sensor : public EventTargetWithInlineData, public ActiveScriptWrappable<Sensor>, @@ -82,10 +83,11 @@ // SensorController::Observer overrides. void onSensorInitialized() override; - void onSensorReadingChanged(double timestamp) override; + void onSensorReadingChanged() override; void onSensorError(ExceptionCode, const String& sanitizedMessage, const String& unsanitizedMessage) override; + void onSuspended() override; void onStartRequestCompleted(bool); void onStopRequestCompleted(bool); @@ -93,6 +95,8 @@ void startListening(); void stopListening(); + void onSensorUpdateNotification(); + void updateState(SensorState newState); void reportError(ExceptionCode = UnknownError, const String& sanitizedMessage = String(), @@ -107,9 +111,9 @@ device::mojom::blink::SensorType m_type; SensorState m_state; Member<SensorProxy> m_sensorProxy; + std::unique_ptr<SensorUpdateNotificationStrategy> m_sensorUpdateNotifier; device::SensorReading m_storedData; SensorConfigurationPtr m_configuration; - double m_lastUpdateTimestamp; }; } // namespace blink
diff --git a/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.cpp b/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.cpp index 9dca7431..be951e0 100644 --- a/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.cpp +++ b/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.cpp
@@ -55,12 +55,12 @@ SensorProxy* SensorProviderProxy::createSensorProxy( device::mojom::blink::SensorType type, - Document* document, + Page* page, std::unique_ptr<SensorReadingFactory> readingFactory) { DCHECK(!getSensorProxy(type)); SensorProxy* sensor = - new SensorProxy(type, this, document, std::move(readingFactory)); + new SensorProxy(type, this, page, std::move(readingFactory)); m_sensorProxies.add(sensor); return sensor;
diff --git a/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.h b/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.h index 6cb2293a..e9c7701 100644 --- a/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.h +++ b/third_party/WebKit/Source/modules/sensor/SensorProviderProxy.h
@@ -13,7 +13,7 @@ namespace blink { -class Document; +class Page; class SensorProxy; class SensorReadingFactory; @@ -31,7 +31,7 @@ ~SensorProviderProxy(); SensorProxy* createSensorProxy(device::mojom::blink::SensorType, - Document*, + Page*, std::unique_ptr<SensorReadingFactory>); SensorProxy* getSensorProxy(device::mojom::blink::SensorType);
diff --git a/third_party/WebKit/Source/modules/sensor/SensorProxy.cpp b/third_party/WebKit/Source/modules/sensor/SensorProxy.cpp index 1d3e991f..44df768 100644 --- a/third_party/WebKit/Source/modules/sensor/SensorProxy.cpp +++ b/third_party/WebKit/Source/modules/sensor/SensorProxy.cpp
@@ -4,11 +4,9 @@ #include "modules/sensor/SensorProxy.h" -#include "core/dom/Document.h" #include "core/frame/LocalFrame.h" #include "modules/sensor/SensorProviderProxy.h" #include "modules/sensor/SensorReading.h" -#include "modules/sensor/SensorReadingUpdater.h" #include "platform/mojo/MojoHelper.h" #include "public/platform/Platform.h" @@ -18,18 +16,18 @@ SensorProxy::SensorProxy(SensorType sensorType, SensorProviderProxy* provider, - Document* document, + Page* page, std::unique_ptr<SensorReadingFactory> readingFactory) - : PageVisibilityObserver(document->page()), + : PageVisibilityObserver(page), m_type(sensorType), m_mode(ReportingMode::CONTINUOUS), m_provider(provider), m_clientBinding(this), m_state(SensorProxy::Uninitialized), m_suspended(false), - m_document(document), m_readingFactory(std::move(readingFactory)), - m_maximumFrequency(0.0) {} + m_maximumFrequency(0.0), + m_timer(this, &SensorProxy::onTimerFired) {} SensorProxy::~SensorProxy() {} @@ -38,8 +36,6 @@ } DEFINE_TRACE(SensorProxy) { - visitor->trace(m_document); - visitor->trace(m_readingUpdater); visitor->trace(m_reading); visitor->trace(m_observers); visitor->trace(m_provider); @@ -71,10 +67,6 @@ callback); } -bool SensorProxy::isActive() const { - return isInitialized() && !m_suspended && !m_frequenciesUsed.isEmpty(); -} - void SensorProxy::addConfiguration( SensorConfigurationPtr configuration, std::unique_ptr<Function<void(bool)>> callback) { @@ -101,6 +93,12 @@ m_sensor->Suspend(); m_suspended = true; + + if (usesPollingTimer()) + updatePollingStatus(); + + for (Observer* observer : m_observers) + observer->onSuspended(); } void SensorProxy::resume() { @@ -111,8 +109,8 @@ m_sensor->Resume(); m_suspended = false; - if (isActive()) - m_readingUpdater->start(); + if (usesPollingTimer()) + updatePollingStatus(); } const SensorConfiguration* SensorProxy::defaultConfig() const { @@ -120,6 +118,10 @@ return m_defaultConfig.get(); } +bool SensorProxy::usesPollingTimer() const { + return isInitialized() && (m_mode == ReportingMode::CONTINUOUS); +} + void SensorProxy::updateSensorReading() { DCHECK(isInitialized()); DCHECK(m_readingFactory); @@ -134,14 +136,9 @@ } m_reading = m_readingFactory->createSensorReading(readingData); -} -void SensorProxy::notifySensorChanged(double timestamp) { - // This notification leads to sync 'onchange' event sending, so - // we must cache m_observers as it can be modified within event handlers. - auto copy = m_observers; - for (Observer* observer : copy) - observer->onSensorReadingChanged(timestamp); + for (Observer* observer : m_observers) + observer->onSensorReadingChanged(); } void SensorProxy::RaiseError() { @@ -150,8 +147,7 @@ void SensorProxy::SensorReadingChanged() { DCHECK_EQ(ReportingMode::ON_CHANGE, m_mode); - if (isActive()) - m_readingUpdater->start(); + updateSensorReading(); } void SensorProxy::pageVisibilityChanged() { @@ -173,9 +169,12 @@ return; } - m_state = Uninitialized; - m_frequenciesUsed.clear(); + if (usesPollingTimer()) { // Stop polling. + m_frequenciesUsed.clear(); + updatePollingStatus(); + } + m_state = Uninitialized; // The m_sensor.reset() will release all callbacks and its bound parameters, // therefore, handleSensorError accepts messages by value. m_sensor.reset(); @@ -229,10 +228,7 @@ m_sensor.set_connection_error_handler( convertToBaseCallback(std::move(errorCallback))); - m_readingUpdater = SensorReadingUpdater::create(this, m_mode); - m_state = Initialized; - for (Observer* observer : m_observers) observer->onSensorInitialized(); } @@ -241,11 +237,9 @@ double frequency, std::unique_ptr<Function<void(bool)>> callback, bool result) { - if (result) { + if (usesPollingTimer() && result) { m_frequenciesUsed.append(frequency); - std::sort(m_frequenciesUsed.begin(), m_frequenciesUsed.end()); - if (isActive()) - m_readingUpdater->start(); + updatePollingStatus(); } (*callback)(result); @@ -256,6 +250,9 @@ if (!result) DVLOG(1) << "Failure at sensor configuration removal"; + if (!usesPollingTimer()) + return; + size_t index = m_frequenciesUsed.find(frequency); if (index == kNotFound) { // Could happen e.g. if 'handleSensorError' was called before. @@ -263,6 +260,7 @@ } m_frequenciesUsed.remove(index); + updatePollingStatus(); } bool SensorProxy::tryReadFromBuffer(device::SensorReading& result) { @@ -278,4 +276,28 @@ return true; } +void SensorProxy::updatePollingStatus() { + DCHECK(usesPollingTimer()); + + if (m_suspended || m_frequenciesUsed.isEmpty()) { + m_timer.stop(); + return; + } + // TODO(Mikhail): Consider using sorted queue instead of searching + // max element each time. + auto it = + std::max_element(m_frequenciesUsed.begin(), m_frequenciesUsed.end()); + DCHECK_GT(*it, 0.0); + + double repeatInterval = 1 / *it; + if (!m_timer.isActive() || m_timer.repeatInterval() != repeatInterval) { + updateSensorReading(); + m_timer.startRepeating(repeatInterval, BLINK_FROM_HERE); + } +} + +void SensorProxy::onTimerFired(TimerBase*) { + updateSensorReading(); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/modules/sensor/SensorProxy.h b/third_party/WebKit/Source/modules/sensor/SensorProxy.h index 3787ef84..3243636 100644 --- a/third_party/WebKit/Source/modules/sensor/SensorProxy.h +++ b/third_party/WebKit/Source/modules/sensor/SensorProxy.h
@@ -12,6 +12,7 @@ #include "device/generic_sensor/public/interfaces/sensor_provider.mojom-blink.h" #include "mojo/public/cpp/bindings/binding.h" #include "platform/Supplementable.h" +#include "platform/Timer.h" #include "platform/heap/Handle.h" #include "wtf/Vector.h" @@ -20,7 +21,6 @@ class SensorProviderProxy; class SensorReading; class SensorReadingFactory; -class SensorReadingUpdater; // This class wraps 'Sensor' mojo interface and used by multiple // JS sensor instances of the same type (within a single frame). @@ -38,16 +38,13 @@ // methods can be called. virtual void onSensorInitialized() {} // Platfrom sensort reading has changed. - // |timestamp| Reference timestamp in seconds of the moment when - // sensor reading was updated from the buffer. - // Note: |timestamp| values are only used to calculate elapsed time - // between shared buffer readings. These values *do not* correspond - // to sensor reading timestamps which are obtained on platform side. - virtual void onSensorReadingChanged(double timestamp) {} + virtual void onSensorReadingChanged() {} // An error has occurred. virtual void onSensorError(ExceptionCode, const String& sanitizedMessage, const String& unsanitizedMessage) {} + // Sensor reading change notification is suspended. + virtual void onSuspended() {} }; ~SensorProxy(); @@ -62,10 +59,6 @@ bool isInitializing() const { return m_state == Initializing; } bool isInitialized() const { return m_state == Initialized; } - // Is watching new reading data (initialized, not suspended and has - // configurations added). - bool isActive() const; - void addConfiguration(device::mojom::blink::SensorConfigurationPtr, std::unique_ptr<Function<void(bool)>>); @@ -86,25 +79,20 @@ double maximumFrequency() const { return m_maximumFrequency; } - Document* document() const { return m_document; } - const WTF::Vector<double>& frequenciesUsed() const { - return m_frequenciesUsed; - } - DECLARE_VIRTUAL_TRACE(); private: friend class SensorProviderProxy; - friend class SensorReadingUpdaterContinuous; - friend class SensorReadingUpdaterOnChange; SensorProxy(device::mojom::blink::SensorType, SensorProviderProxy*, - Document*, + Page*, std::unique_ptr<SensorReadingFactory>); + // Returns true if this instance is using polling timer to + // periodically fetch reading data from shared buffer. + bool usesPollingTimer() const; // Updates sensor reading from shared buffer. void updateSensorReading(); - void notifySensorChanged(double timestamp); // device::mojom::blink::SensorClient overrides. void RaiseError() override; @@ -128,7 +116,8 @@ void onRemoveConfigurationCompleted(double frequency, bool result); bool tryReadFromBuffer(device::SensorReading& result); - void onAnimationFrame(double timestamp); + void updatePollingStatus(); + void onTimerFired(TimerBase*); device::mojom::blink::SensorType m_type; device::mojom::blink::ReportingMode m_mode; @@ -145,14 +134,13 @@ mojo::ScopedSharedBufferHandle m_sharedBufferHandle; mojo::ScopedSharedBufferMapping m_sharedBuffer; bool m_suspended; - Member<Document> m_document; Member<SensorReading> m_reading; std::unique_ptr<SensorReadingFactory> m_readingFactory; double m_maximumFrequency; - Member<SensorReadingUpdater> m_readingUpdater; + // Used for continious reporting mode. + Timer<SensorProxy> m_timer; WTF::Vector<double> m_frequenciesUsed; - double m_lastRafTimestamp; using ReadingBuffer = device::SensorReadingSharedBuffer; static_assert(
diff --git a/third_party/WebKit/Source/modules/sensor/SensorReadingUpdater.cpp b/third_party/WebKit/Source/modules/sensor/SensorReadingUpdater.cpp deleted file mode 100644 index 3d35f7da..0000000 --- a/third_party/WebKit/Source/modules/sensor/SensorReadingUpdater.cpp +++ /dev/null
@@ -1,112 +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 "modules/sensor/SensorReadingUpdater.h" - -#include "core/dom/Document.h" -#include "device/generic_sensor/public/interfaces/sensor.mojom-blink.h" -#include "modules/sensor/SensorProxy.h" -#include "wtf/CurrentTime.h" - -using device::mojom::blink::ReportingMode; - -namespace blink { - -SensorReadingUpdater::SensorReadingUpdater(SensorProxy* sensorProxy) - : m_sensorProxy(sensorProxy), m_hasPendingAnimationFrameTask(false) {} - -void SensorReadingUpdater::enqueueAnimationFrameTask() { - if (m_hasPendingAnimationFrameTask) - return; - - auto callback = WTF::bind(&SensorReadingUpdater::onAnimationFrame, - wrapWeakPersistent(this)); - m_sensorProxy->document()->enqueueAnimationFrameTask(std::move(callback)); - m_hasPendingAnimationFrameTask = true; -} - -void SensorReadingUpdater::start() { - enqueueAnimationFrameTask(); -} - -void SensorReadingUpdater::onAnimationFrame() { - m_hasPendingAnimationFrameTask = false; - onAnimationFrameInternal(); -} - -DEFINE_TRACE(SensorReadingUpdater) { - visitor->trace(m_sensorProxy); -} - -class SensorReadingUpdaterContinuous : public SensorReadingUpdater { - public: - explicit SensorReadingUpdaterContinuous(SensorProxy* sensorProxy) - : SensorReadingUpdater(sensorProxy) {} - - DEFINE_INLINE_VIRTUAL_TRACE() { SensorReadingUpdater::trace(visitor); } - - protected: - void onAnimationFrameInternal() override { - if (!m_sensorProxy->isActive()) - return; - - m_sensorProxy->updateSensorReading(); - m_sensorProxy->notifySensorChanged(WTF::monotonicallyIncreasingTime()); - enqueueAnimationFrameTask(); - } -}; - -// New data is fetched from shared buffer only once after 'start()' -// call. Further, notification is send until every client is updated -// (i.e. until longest notification period elapses) rAF stops after that. -class SensorReadingUpdaterOnChange : public SensorReadingUpdater { - public: - explicit SensorReadingUpdaterOnChange(SensorProxy* sensorProxy) - : SensorReadingUpdater(sensorProxy), - m_newDataArrivedTime(0.0), - m_newDataArrived(false) {} - - DEFINE_INLINE_VIRTUAL_TRACE() { SensorReadingUpdater::trace(visitor); } - - void start() override { - m_newDataArrived = true; - SensorReadingUpdater::start(); - } - - protected: - void onAnimationFrameInternal() override { - if (!m_sensorProxy->isActive()) - return; - - double timestamp = WTF::monotonicallyIncreasingTime(); - - if (m_newDataArrived) { - m_newDataArrived = false; - m_sensorProxy->updateSensorReading(); - m_newDataArrivedTime = timestamp; - } - m_sensorProxy->notifySensorChanged(timestamp); - - DCHECK_GT(m_sensorProxy->frequenciesUsed().front(), 0.0); - double longestNotificationPeriod = - 1 / m_sensorProxy->frequenciesUsed().front(); - - if (timestamp - m_newDataArrivedTime <= longestNotificationPeriod) - enqueueAnimationFrameTask(); - } - - private: - double m_newDataArrivedTime; - bool m_newDataArrived; -}; - -// static -SensorReadingUpdater* SensorReadingUpdater::create(SensorProxy* proxy, - ReportingMode mode) { - if (mode == ReportingMode::CONTINUOUS) - return new SensorReadingUpdaterContinuous(proxy); - return new SensorReadingUpdaterOnChange(proxy); -} - -} // namespace blink
diff --git a/third_party/WebKit/Source/modules/sensor/SensorReadingUpdater.h b/third_party/WebKit/Source/modules/sensor/SensorReadingUpdater.h deleted file mode 100644 index fa28150..0000000 --- a/third_party/WebKit/Source/modules/sensor/SensorReadingUpdater.h +++ /dev/null
@@ -1,39 +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 SensorReadingUpdater_h -#define SensorReadingUpdater_h - -#include "device/generic_sensor/public/interfaces/sensor_provider.mojom-blink.h" -#include "platform/heap/Handle.h" - -namespace blink { - -class SensorProxy; - -// This class encapsulates sensor reading update notification logic. -class SensorReadingUpdater : public GarbageCollected<SensorProxy> { - public: - static SensorReadingUpdater* create(SensorProxy*, - device::mojom::blink::ReportingMode); - - virtual void start(); - - DECLARE_VIRTUAL_TRACE(); - - protected: - explicit SensorReadingUpdater(SensorProxy*); - void enqueueAnimationFrameTask(); - virtual void onAnimationFrameInternal() = 0; - - Member<SensorProxy> m_sensorProxy; - bool m_hasPendingAnimationFrameTask; - - private: - void onAnimationFrame(); -}; - -} // namespace blink - -#endif // SensorReadingUpdater_h
diff --git a/third_party/WebKit/Source/modules/sensor/SensorUpdateNotificationStrategy.cpp b/third_party/WebKit/Source/modules/sensor/SensorUpdateNotificationStrategy.cpp new file mode 100644 index 0000000..1482398 --- /dev/null +++ b/third_party/WebKit/Source/modules/sensor/SensorUpdateNotificationStrategy.cpp
@@ -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. + +#include "modules/sensor/SensorUpdateNotificationStrategy.h" + +#include "device/generic_sensor/public/interfaces/sensor.mojom-blink.h" +#include "platform/Timer.h" +#include "wtf/CurrentTime.h" + +namespace blink { + +// Polls the buffer on signal from platform but not more frequently +// than the given 'pollingPeriod'. +class SensorUpdateNotificationStrategyImpl + : public SensorUpdateNotificationStrategy { + public: + SensorUpdateNotificationStrategyImpl(double frequency, + std::unique_ptr<Function<void()>> func) + : m_pollingPeriod(1 / frequency), + m_func(std::move(func)), + m_timer(this, &SensorUpdateNotificationStrategyImpl::onTimer), + m_lastPollingTimestamp(0.0) { + DCHECK_GT(frequency, 0.0); + DCHECK(m_func); + } + + private: + // SensorUpdateNotificationStrategy overrides. + void onSensorReadingChanged() override; + void cancelPendingNotifications() override; + + void onTimer(TimerBase*); + void notifyUpdate(); + + double m_pollingPeriod; + std::unique_ptr<Function<void()>> m_func; + Timer<SensorUpdateNotificationStrategyImpl> m_timer; + double m_lastPollingTimestamp; +}; + +void SensorUpdateNotificationStrategyImpl::onSensorReadingChanged() { + if (m_timer.isActive()) + return; // Skipping changes if update notification was already sheduled. + + double elapsedTime = + WTF::monotonicallyIncreasingTime() - m_lastPollingTimestamp; + + double waitingTime = m_pollingPeriod - elapsedTime; + const double minInterval = + 1 / device::mojom::blink::SensorConfiguration::kMaxAllowedFrequency; + + // Negative or zero 'waitingTime' means that polling period has elapsed. + // We also avoid scheduling if the elapsed time is slightly behind the + // polling period. + if (waitingTime < minInterval) { + notifyUpdate(); + } else { + m_timer.startOneShot(waitingTime, BLINK_FROM_HERE); + } +} + +void SensorUpdateNotificationStrategyImpl::cancelPendingNotifications() { + m_timer.stop(); +} + +void SensorUpdateNotificationStrategyImpl::notifyUpdate() { + m_lastPollingTimestamp = WTF::monotonicallyIncreasingTime(); + (*m_func)(); +} + +void SensorUpdateNotificationStrategyImpl::onTimer(TimerBase*) { + notifyUpdate(); +} + +// static +std::unique_ptr<SensorUpdateNotificationStrategy> +SensorUpdateNotificationStrategy::create( + double pollingPeriod, + std::unique_ptr<Function<void()>> func) { + return std::unique_ptr<SensorUpdateNotificationStrategy>( + new SensorUpdateNotificationStrategyImpl(pollingPeriod, std::move(func))); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/modules/sensor/SensorUpdateNotificationStrategy.h b/third_party/WebKit/Source/modules/sensor/SensorUpdateNotificationStrategy.h new file mode 100644 index 0000000..720e9a1 --- /dev/null +++ b/third_party/WebKit/Source/modules/sensor/SensorUpdateNotificationStrategy.h
@@ -0,0 +1,29 @@ +// 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 SensorUpdateNotificationStrategy_h +#define SensorUpdateNotificationStrategy_h + +#include "wtf/Functional.h" + +namespace blink { + +// This class encapsulates sensor reading update notification logic: +// the callback is invoked after client calls 'onSensorReadingChanged()' +// however considering the given sample frequency: +// guaranteed not to be called more often than expected. +class SensorUpdateNotificationStrategy { + public: + static std::unique_ptr<SensorUpdateNotificationStrategy> create( + double frequency, + std::unique_ptr<Function<void()>>); + + virtual void onSensorReadingChanged() = 0; + virtual void cancelPendingNotifications() = 0; + virtual ~SensorUpdateNotificationStrategy() {} +}; + +} // namespace blink + +#endif // SensorUpdateNotificationStrategy_h
diff --git a/tools/gn/bootstrap/bootstrap.py b/tools/gn/bootstrap/bootstrap.py index bfdf0fc..c3642e8 100755 --- a/tools/gn/bootstrap/bootstrap.py +++ b/tools/gn/bootstrap/bootstrap.py
@@ -162,7 +162,10 @@ {'USE_EXPERIMENTAL_ALLOCATOR_SHIM': 'true' if is_linux else 'false'}) write_buildflag_header_manually(root_gen_dir, 'base/debug/debugging_flags.h', - {'ENABLE_PROFILING': 'false'}) + { + 'ENABLE_PROFILING': 'false', + 'ENABLE_MEMORY_TASK_PROFILER': 'false' + }) if is_mac: # //base/build_time.cc needs base/generated_build_date.h, @@ -402,6 +405,7 @@ 'base/memory/ref_counted.cc', 'base/memory/ref_counted_memory.cc', 'base/memory/singleton.cc', + 'base/memory/shared_memory_helper.cc', 'base/memory/weak_ptr.cc', 'base/message_loop/incoming_task_queue.cc', 'base/message_loop/message_loop.cc', @@ -456,6 +460,7 @@ 'base/task_scheduler/scheduler_worker_pool_impl.cc', 'base/task_scheduler/scheduler_worker_pool_params.cc', 'base/task_scheduler/scheduler_worker_stack.cc', + 'base/task_scheduler/scoped_set_task_priority_for_current_thread.cc', 'base/task_scheduler/sequence.cc', 'base/task_scheduler/sequence_sort_key.cc', 'base/task_scheduler/task.cc',
diff --git a/ui/gfx/mojo/color_space.typemap b/ui/gfx/mojo/color_space.typemap index 4ab7efa4..36ebf09 100644 --- a/ui/gfx/mojo/color_space.typemap +++ b/ui/gfx/mojo/color_space.typemap
@@ -4,8 +4,9 @@ mojom = "//ui/gfx/mojo/color_space.mojom" public_headers = [ "//ui/gfx/color_space.h" ] -traits_headers = [ "ui/gfx/ipc/color/gfx_param_traits.h" ] +traits_headers = [ "//ui/gfx/ipc/color/gfx_param_traits.h" ] public_deps = [ + "//ipc", "//ui/gfx", ] deps = [
diff --git a/ui/gfx/typemaps.gni b/ui/gfx/typemaps.gni index 5420df3..d28b2cb 100644 --- a/ui/gfx/typemaps.gni +++ b/ui/gfx/typemaps.gni
@@ -6,6 +6,7 @@ "//ui/gfx/geometry/mojo/geometry.typemap", "//ui/gfx/mojo/accelerated_widget.typemap", "//ui/gfx/mojo/buffer_types.typemap", + "//ui/gfx/mojo/color_space.typemap", "//ui/gfx/mojo/icc_profile.typemap", "//ui/gfx/mojo/selection_bound.typemap", "//ui/gfx/mojo/transform.typemap",