diff --git a/DEPS b/DEPS index ac17347..8f80a4f 100644 --- a/DEPS +++ b/DEPS
@@ -105,7 +105,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': '6784ffa78e70e17084e1b30e724a8ded175d6007', + 'skia_revision': 'cdefa23a23cfc411fb50a21d9a06bfaa8d037886', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -161,11 +161,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling HarfBuzz # and whatever else without interference from each other. - 'harfbuzz_revision': '2cb075fe26201f3e370fccfff6c1bc242b5acc79', + 'harfbuzz_revision': '957e7756634a4fdf1654041e20e883cf964ecac9', # 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': '5abd99f5f7685f0e3b18f11e2fcda667a3be83e3', + 'catapult_revision': '82213060d5ce0d376671dda9ca3928b5ad1c9c69', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -346,16 +346,6 @@ 'condition': 'checkout_nacl', }, - 'src/tools/luci-go': { - 'packages': [ - { - 'package': 'infra/tools/luci/isolate/${{platform}}', - 'version': 'git_revision:8b15ba47cbaf07a56f93326e39f0c8e5069c19e8', - }, - ], - 'dep_type': 'cipd', - }, - 'src/third_party/SPIRV-Tools/src': Var('chromium_git') + '/external/github.com/KhronosGroup/SPIRV-Tools.git' + '@' + '9166854ac93ef81b026e943ccd230fed6c8b8d3c', @@ -1718,7 +1708,6 @@ ], }, # Pull luci-go binaries (isolate, swarming) using checked-in hashes. - # TODO(maruel): Remove, https://crbug.com/851596 { 'name': 'luci-go_win', 'pattern': '.',
diff --git a/WATCHLISTS b/WATCHLISTS index 08131aa..90f9c1f2 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -1620,6 +1620,9 @@ '|third_party/blink/public/platform/modules/webshare/'\ '|third_party/blink/renderer/modules/webshare/', }, + 'webgpu': { + 'filepath': 'third_party/blink/renderer/modules/webgpu/', + }, 'webrtc_browser_tests': { 'filepath': 'chrome/browser/media/.*webrtc.*browsertest|'\ 'content/browser/media/.*webrtc.*browsertest', @@ -2081,8 +2084,7 @@ 'dpranke@chromium.org', 'tfarina@chromium.org'], 'gpu': ['piman+watch@chromium.org'], - 'gpu_passthrough_cmd_decoder': ['cwallez+watch@chromium.org', - 'geofflang+watch@chromium.org'], + 'gpu_passthrough_cmd_decoder': ['geofflang+watch@chromium.org'], 'headless': ['headless-reviews@chromium.org'], 'history_ui': ['pam+watch@chromium.org'], 'i18n': ['jshin+watch@chromium.org'], @@ -2344,6 +2346,8 @@ 'wake_lock': ['mattreynolds+watch@chromium.org'], 'web_package': ['twifkak+watch@chromium.org'], 'web_share': ['mgiuca+watch@chromium.org'], + 'webgpu': ['cwallez+watch@chromium.org', + 'kainino+watch@chromium.org'], 'webrtc_browser_tests': ['phoglund+watch@chromium.org'], 'website_settings': ['dullweber+watch@chromium.org', 'markusheintz@chromium.org',
diff --git a/ash/system/unified/feature_pod_button.cc b/ash/system/unified/feature_pod_button.cc index 240d183..3e01314 100644 --- a/ash/system/unified/feature_pod_button.cc +++ b/ash/system/unified/feature_pod_button.cc
@@ -269,7 +269,8 @@ } void FeaturePodButton::SetExpandedAmount(double expanded_amount) { - label_button_->layer()->SetOpacity(expanded_amount); + // TODO(tetsui): Confirm the animation curve with UX. + label_button_->layer()->SetOpacity(std::max(0., 5. * expanded_amount - 4.)); label_button_->SetVisible(expanded_amount > 0.0); }
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc index af76d4f..cc561b4a 100644 --- a/ash/system/unified/unified_system_tray.cc +++ b/ash/system/unified/unified_system_tray.cc
@@ -61,6 +61,7 @@ UnifiedSystemTray::UiDelegate::UiDelegate(UnifiedSystemTray* owner) : owner_(owner) { ui_controller_ = std::make_unique<message_center::UiController>(this); + ui_controller_->set_hide_on_last_notification(false); popup_alignment_delegate_ = std::make_unique<AshPopupAlignmentDelegate>(owner->shelf()); message_popup_collection_ =
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc index 462167c1..6ec855b 100644 --- a/ash/system/unified/unified_system_tray_controller.cc +++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -177,7 +177,22 @@ UpdateExpandedAmount(); } +void UnifiedSystemTrayController::StartAnimation(bool expand) { + if (expand) { + animation_->Show(); + } else { + // To animate to hidden state, first set SlideAnimation::IsShowing() to + // true. + animation_->Show(); + animation_->Hide(); + } +} + void UnifiedSystemTrayController::EndDrag(const gfx::Point& location) { + if (animation_->is_animating()) { + // Prevent overwriting the state right after fling event + return; + } bool expanded = GetDragExpandedAmount(location) > 0.5; if (was_expanded_ != expanded) { UMA_HISTOGRAM_ENUMERATION("ChromeOS.SystemTray.ToggleExpanded", @@ -186,14 +201,12 @@ } // If dragging is finished, animate to closer state. - if (expanded) { - animation_->Show(); - } else { - // To animate to hidden state, first set SlideAnimation::IsShowing() to - // true. - animation_->Show(); - animation_->Hide(); - } + StartAnimation(expanded); +} + +void UnifiedSystemTrayController::Fling(int velocity) { + // Expand when flinging up. Collapse otherwise. + StartAnimation(velocity < 0); } void UnifiedSystemTrayController::ShowUserChooserWidget() {
diff --git a/ash/system/unified/unified_system_tray_controller.h b/ash/system/unified/unified_system_tray_controller.h index 8713455..c6bad93e 100644 --- a/ash/system/unified/unified_system_tray_controller.h +++ b/ash/system/unified/unified_system_tray_controller.h
@@ -65,6 +65,7 @@ void BeginDrag(const gfx::Point& location); void UpdateDrag(const gfx::Point& location); void EndDrag(const gfx::Point& location); + void Fling(int velocity); // Show user selector popup widget. Called from the view. void ShowUserChooserWidget(); @@ -139,6 +140,9 @@ // Return true if UnifiedSystemTray is expanded. bool IsExpanded() const; + // Starts animation to expand or collapse the bubble. + void StartAnimation(bool expand); + // Model that stores UI specific variables. Unowned. UnifiedSystemTrayModel* const model_;
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc index a40bf17..ffbe438 100644 --- a/ash/system/unified/unified_system_tray_view.cc +++ b/ash/system/unified/unified_system_tray_view.cc
@@ -106,15 +106,20 @@ } void UnifiedSlidersContainerView::UpdateOpacity() { + const int height = GetPreferredSize().height(); for (int i = 0; i < child_count(); ++i) { views::View* child = child_at(i); double opacity = 1.0; - if (child->y() > height()) + if (child->y() > height) { opacity = 0.0; - else if (child->bounds().bottom() < height()) + } else if (child->bounds().bottom() < height) { opacity = 1.0; - else - opacity = static_cast<double>(height() - child->y()) / child->height(); + } else { + const double ratio = + static_cast<double>(height - child->y()) / child->height(); + // TODO(tetsui): Confirm the animation curve with UX. + opacity = std::max(0., 2. * ratio - 1.); + } child->layer()->SetOpacity(opacity); } } @@ -243,6 +248,9 @@ controller_->EndDrag(screen_location); event->SetHandled(); break; + case ui::ET_SCROLL_FLING_START: + controller_->Fling(event->details().velocity_y()); + break; default: break; }
diff --git a/base/BUILD.gn b/base/BUILD.gn index 5443d2d..cb84017 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -814,16 +814,35 @@ "task/cancelable_task_tracker.h", "task/sequence_manager/enqueue_order.cc", "task/sequence_manager/enqueue_order.h", + "task/sequence_manager/graceful_queue_shutdown_helper.cc", + "task/sequence_manager/graceful_queue_shutdown_helper.h", "task/sequence_manager/intrusive_heap.h", "task/sequence_manager/lazily_deallocated_deque.h", "task/sequence_manager/lazy_now.cc", "task/sequence_manager/lazy_now.h", + "task/sequence_manager/real_time_domain.cc", + "task/sequence_manager/real_time_domain.h", "task/sequence_manager/sequence_manager.h", + "task/sequence_manager/sequence_manager_impl.cc", + "task/sequence_manager/sequence_manager_impl.h", "task/sequence_manager/sequenced_task_source.h", + "task/sequence_manager/task_queue.cc", "task/sequence_manager/task_queue.h", + "task/sequence_manager/task_queue_impl.cc", + "task/sequence_manager/task_queue_impl.h", + "task/sequence_manager/task_queue_selector.cc", + "task/sequence_manager/task_queue_selector.h", + "task/sequence_manager/task_queue_selector_logic.h", "task/sequence_manager/task_time_observer.h", "task/sequence_manager/thread_controller.h", + "task/sequence_manager/thread_controller_impl.cc", + "task/sequence_manager/thread_controller_impl.h", + "task/sequence_manager/time_domain.cc", "task/sequence_manager/time_domain.h", + "task/sequence_manager/work_queue.cc", + "task/sequence_manager/work_queue.h", + "task/sequence_manager/work_queue_sets.cc", + "task/sequence_manager/work_queue_sets.h", "task_runner.cc", "task_runner.h", "task_runner_util.h",
diff --git a/base/files/important_file_writer_unittest.cc b/base/files/important_file_writer_unittest.cc index 05a3bd2..5dddc71 100644 --- a/base/files/important_file_writer_unittest.cc +++ b/base/files/important_file_writer_unittest.cc
@@ -231,7 +231,7 @@ TEST_F(ImportantFileWriterTest, ScheduleWrite) { constexpr TimeDelta kCommitInterval = TimeDelta::FromSeconds(12345); - MockTimer timer(true, false); + MockOneShotTimer timer; ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get(), kCommitInterval); writer.SetTimerForTesting(&timer); @@ -250,7 +250,7 @@ } TEST_F(ImportantFileWriterTest, DoScheduledWrite) { - MockTimer timer(true, false); + MockOneShotTimer timer; ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); writer.SetTimerForTesting(&timer); EXPECT_FALSE(writer.HasPendingWrite()); @@ -265,7 +265,7 @@ } TEST_F(ImportantFileWriterTest, BatchingWrites) { - MockTimer timer(true, false); + MockOneShotTimer timer; ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); writer.SetTimerForTesting(&timer); DataSerializer foo("foo"), bar("bar"), baz("baz"); @@ -280,7 +280,7 @@ } TEST_F(ImportantFileWriterTest, ScheduleWrite_FailToSerialize) { - MockTimer timer(true, false); + MockOneShotTimer timer; ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); writer.SetTimerForTesting(&timer); EXPECT_FALSE(writer.HasPendingWrite()); @@ -295,7 +295,7 @@ } TEST_F(ImportantFileWriterTest, ScheduleWrite_WriteNow) { - MockTimer timer(true, false); + MockOneShotTimer timer; ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); writer.SetTimerForTesting(&timer); EXPECT_FALSE(writer.HasPendingWrite()); @@ -312,7 +312,7 @@ } TEST_F(ImportantFileWriterTest, DoScheduledWrite_FailToSerialize) { - MockTimer timer(true, false); + MockOneShotTimer timer; ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); writer.SetTimerForTesting(&timer); EXPECT_FALSE(writer.HasPendingWrite());
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index 6f54a63..bd0a568 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc
@@ -428,7 +428,7 @@ bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) { if (!task_execution_allowed_ || !incoming_task_queue_->delayed_tasks().HasTasks()) { - recent_time_ = *next_delayed_work_time = TimeTicks(); + *next_delayed_work_time = TimeTicks(); // It's possible to be woken up by a system event and have it cancel the // upcoming delayed task from under us before DoDelayedWork() -- see comment
diff --git a/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.cc b/base/task/sequence_manager/graceful_queue_shutdown_helper.cc similarity index 85% rename from third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.cc rename to base/task/sequence_manager/graceful_queue_shutdown_helper.cc index 2a2b7da..9a8c893e 100644 --- a/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.cc +++ b/base/task/sequence_manager/graceful_queue_shutdown_helper.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h" +#include "base/task/sequence_manager/graceful_queue_shutdown_helper.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" +#include "base/task/sequence_manager/task_queue_impl.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h b/base/task/sequence_manager/graceful_queue_shutdown_helper.h similarity index 80% rename from third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h rename to base/task/sequence_manager/graceful_queue_shutdown_helper.h index e3ee7c91..108eb82 100644 --- a/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h +++ b/base/task/sequence_manager/graceful_queue_shutdown_helper.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_GRACEFUL_QUEUE_SHUTDOWN_HELPER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_GRACEFUL_QUEUE_SHUTDOWN_HELPER_H_ +#ifndef BASE_TASK_SEQUENCE_MANAGER_GRACEFUL_QUEUE_SHUTDOWN_HELPER_H_ +#define BASE_TASK_SEQUENCE_MANAGER_GRACEFUL_QUEUE_SHUTDOWN_HELPER_H_ #include <memory> #include <vector> @@ -47,4 +47,4 @@ } // namespace sequence_manager } // namespace base -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_GRACEFUL_QUEUE_SHUTDOWN_HELPER_H_ +#endif // BASE_TASK_SEQUENCE_MANAGER_GRACEFUL_QUEUE_SHUTDOWN_HELPER_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/real_time_domain.cc b/base/task/sequence_manager/real_time_domain.cc similarity index 86% rename from third_party/blink/renderer/platform/scheduler/base/real_time_domain.cc rename to base/task/sequence_manager/real_time_domain.cc index 03ef8432..6a6caf0 100644 --- a/third_party/blink/renderer/platform/scheduler/base/real_time_domain.cc +++ b/base/task/sequence_manager/real_time_domain.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h" +#include "base/task/sequence_manager/real_time_domain.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" +#include "base/task/sequence_manager/sequence_manager.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/real_time_domain.h b/base/task/sequence_manager/real_time_domain.h similarity index 60% rename from third_party/blink/renderer/platform/scheduler/base/real_time_domain.h rename to base/task/sequence_manager/real_time_domain.h index 7489b83f..4923ebf0 100644 --- a/third_party/blink/renderer/platform/scheduler/base/real_time_domain.h +++ b/base/task/sequence_manager/real_time_domain.h
@@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_REAL_TIME_DOMAIN_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_REAL_TIME_DOMAIN_H_ +#ifndef BASE_TASK_SEQUENCE_MANAGER_REAL_TIME_DOMAIN_H_ +#define BASE_TASK_SEQUENCE_MANAGER_REAL_TIME_DOMAIN_H_ +#include "base/base_export.h" #include "base/macros.h" -#include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" +#include "base/task/sequence_manager/time_domain.h" namespace base { namespace sequence_manager { namespace internal { -class PLATFORM_EXPORT RealTimeDomain : public TimeDomain { +class BASE_EXPORT RealTimeDomain : public TimeDomain { public: RealTimeDomain(); ~RealTimeDomain() override; @@ -34,4 +34,4 @@ } // namespace sequence_manager } // namespace base -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_REAL_TIME_DOMAIN_H_ +#endif // BASE_TASK_SEQUENCE_MANAGER_REAL_TIME_DOMAIN_H_
diff --git a/base/task/sequence_manager/sequence_manager.h b/base/task/sequence_manager/sequence_manager.h index 47f20cd3b..e9f03fce 100644 --- a/base/task/sequence_manager/sequence_manager.h +++ b/base/task/sequence_manager/sequence_manager.h
@@ -103,6 +103,13 @@ const TaskQueue::Spec& spec) = 0; }; +// Create SequenceManager using MessageLoop on the current thread. +// Implementation is located in sequence_manager_impl.cc. +// TODO(scheduler-dev): Rename to TakeOverCurrentThread when we'll stop using +// MessageLoop and will actually take over a thread. +BASE_EXPORT std::unique_ptr<SequenceManager> +CreateSequenceManagerOnCurrentThread(); + } // namespace sequence_manager } // namespace base
diff --git a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc similarity index 97% rename from third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.cc rename to base/task/sequence_manager/sequence_manager_impl.cc index b3aae71..70c19a4 100644 --- a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.cc +++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h" +#include "base/task/sequence_manager/sequence_manager_impl.h" #include <queue> #include <vector> @@ -13,16 +13,15 @@ #include "base/debug/crash_logging.h" #include "base/memory/ptr_util.h" #include "base/rand_util.h" +#include "base/task/sequence_manager/real_time_domain.h" #include "base/task/sequence_manager/task_time_observer.h" +#include "base/task/sequence_manager/thread_controller_impl.h" +#include "base/task/sequence_manager/work_queue.h" +#include "base/task/sequence_manager/work_queue_sets.h" #include "base/time/default_tick_clock.h" #include "base/time/tick_clock.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" -#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h similarity index 94% rename from third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h rename to base/task/sequence_manager/sequence_manager_impl.h index dd3a94e..b21b501 100644 --- a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h +++ b/base/task/sequence_manager/sequence_manager_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCE_MANAGER_IMPL_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCE_MANAGER_IMPL_H_ +#ifndef BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_IMPL_H_ +#define BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_IMPL_H_ #include <list> #include <map> @@ -26,13 +26,13 @@ #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" #include "base/task/sequence_manager/enqueue_order.h" +#include "base/task/sequence_manager/graceful_queue_shutdown_helper.h" #include "base/task/sequence_manager/moveable_auto_lock.h" +#include "base/task/sequence_manager/sequence_manager.h" +#include "base/task/sequence_manager/task_queue_impl.h" +#include "base/task/sequence_manager/task_queue_selector.h" #include "base/task/sequence_manager/thread_controller.h" #include "base/threading/thread_checker.h" -#include "third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h" namespace base { @@ -68,7 +68,7 @@ // the incoming task queue (if any) are moved here. The work queues are // registered with the selector as input to the scheduling decision. // -class PLATFORM_EXPORT SequenceManagerImpl +class BASE_EXPORT SequenceManagerImpl : public SequenceManager, public internal::SequencedTaskSource, public internal::TaskQueueSelector::Observer, @@ -304,7 +304,7 @@ } // A check to bail out early during memory corruption. - // crbug.com/757940 + // https://crbug.com/757940 bool Validate(); int32_t memory_corruption_sentinel_; @@ -329,4 +329,4 @@ } // namespace sequence_manager } // namespace base -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCE_MANAGER_IMPL_H_ +#endif // BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_IMPL_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue.cc b/base/task/sequence_manager/task_queue.cc similarity index 96% rename from third_party/blink/renderer/platform/scheduler/base/task_queue.cc rename to base/task/sequence_manager/task_queue.cc index 2a7aafb..2f23d072 100644 --- a/third_party/blink/renderer/platform/scheduler/base/task_queue.cc +++ b/base/task/sequence_manager/task_queue.cc
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/bind.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" +#include "base/task/sequence_manager/sequence_manager_impl.h" +#include "base/task/sequence_manager/task_queue_impl.h" namespace base { namespace sequence_manager {
diff --git a/base/task/sequence_manager/task_queue.h b/base/task/sequence_manager/task_queue.h index d6773a0..80dda08b 100644 --- a/base/task/sequence_manager/task_queue.h +++ b/base/task/sequence_manager/task_queue.h
@@ -15,14 +15,6 @@ #include "base/threading/platform_thread.h" #include "base/time/time.h" -// Currently the implementation is located in Blink because scheduler/base move -// is in progress https://crbug.com/783309. -// TODO(kraynov): Remove this hack ASAP. -#ifndef HACKDEF_INCLUDED_FROM_BLINK -// Need PLATFORM_EXPORT macro. -#error Use third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h. -#endif - namespace base { namespace trace_event { @@ -39,7 +31,7 @@ class TimeDomain; -class PLATFORM_EXPORT TaskQueue : public SingleThreadTaskRunner { +class BASE_EXPORT TaskQueue : public SingleThreadTaskRunner { public: class Observer { public: @@ -60,7 +52,7 @@ // A wrapper around OnceClosure with additional metadata to be passed // to PostTask and plumbed until PendingTask is created. - struct PLATFORM_EXPORT PostedTask { + struct BASE_EXPORT PostedTask { PostedTask(OnceClosure callback, Location posted_from, TimeDelta delay = TimeDelta(), @@ -143,7 +135,7 @@ }; // Interface to pass per-task metadata to RendererScheduler. - class PLATFORM_EXPORT Task : public PendingTask { + class BASE_EXPORT Task : public PendingTask { public: Task(PostedTask posted_task, TimeTicks desired_run_time);
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc similarity index 98% rename from third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc rename to base/task/sequence_manager/task_queue_impl.cc index 51dcc1e..2ec63102 100644 --- a/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc +++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" +#include "base/task/sequence_manager/task_queue_impl.h" #include <memory> #include <utility> #include "base/strings/stringprintf.h" +#include "base/task/sequence_manager/sequence_manager_impl.h" +#include "base/task/sequence_manager/time_domain.h" +#include "base/task/sequence_manager/work_queue.h" #include "base/time/time.h" #include "base/trace_event/blame_context.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h" -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" namespace base { namespace sequence_manager {
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h index f21437d..72336fc1 100644 --- a/base/task/sequence_manager/task_queue_impl.h +++ b/base/task/sequence_manager/task_queue_impl.h
@@ -69,7 +69,7 @@ // queue is selected, it round-robins between the |immediate_work_queue| and // |delayed_work_queue|. The reason for this is we want to make sure delayed // tasks (normally the most common type) don't starve out immediate work. -class PLATFORM_EXPORT TaskQueueImpl { +class BASE_EXPORT TaskQueueImpl { public: TaskQueueImpl(SequenceManagerImpl* sequence_manager, TimeDomain* time_domain, @@ -104,7 +104,7 @@ } }; - class PLATFORM_EXPORT Task : public TaskQueue::Task { + class BASE_EXPORT Task : public TaskQueue::Task { public: Task(TaskQueue::PostedTask task, TimeTicks desired_run_time,
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.cc b/base/task/sequence_manager/task_queue_selector.cc similarity index 97% rename from third_party/blink/renderer/platform/scheduler/base/task_queue_selector.cc rename to base/task/sequence_manager/task_queue_selector.cc index 8fe0ba7..30a88bd 100644 --- a/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.cc +++ b/base/task/sequence_manager/task_queue_selector.cc
@@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h" +#include "base/task/sequence_manager/task_queue_selector.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" +#include "base/task/sequence_manager/task_queue_impl.h" +#include "base/task/sequence_manager/work_queue.h" #include "base/trace_event/trace_event_argument.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h b/base/task/sequence_manager/task_queue_selector.h similarity index 93% rename from third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h rename to base/task/sequence_manager/task_queue_selector.h index 3db0a4e5c..182158b 100644 --- a/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h +++ b/base/task/sequence_manager/task_queue_selector.h
@@ -2,16 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_H_ +#ifndef BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_H_ +#define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_H_ #include <stddef.h> +#include "base/base_export.h" #include "base/macros.h" #include "base/pending_task.h" +#include "base/task/sequence_manager/task_queue_selector_logic.h" +#include "base/task/sequence_manager/work_queue_sets.h" #include "base/threading/thread_checker.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector_logic.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h" namespace base { namespace sequence_manager { @@ -19,7 +20,7 @@ // TaskQueueSelector is used by the SchedulerHelper to enable prioritization // of particular task queues. -class PLATFORM_EXPORT TaskQueueSelector { +class BASE_EXPORT TaskQueueSelector { public: TaskQueueSelector(); ~TaskQueueSelector(); @@ -53,7 +54,7 @@ // Serialize the selector state for tracing. void AsValueInto(trace_event::TracedValue* state) const; - class PLATFORM_EXPORT Observer { + class BASE_EXPORT Observer { public: virtual ~Observer() = default; @@ -70,7 +71,7 @@ bool AllEnabledWorkQueuesAreEmpty() const; protected: - class PLATFORM_EXPORT PrioritizingSelector { + class BASE_EXPORT PrioritizingSelector { public: PrioritizingSelector(TaskQueueSelector* task_queue_selector, const char* name); @@ -213,7 +214,7 @@ size_t normal_priority_starvation_score_; size_t low_priority_starvation_score_; - Observer* task_queue_selector_observer_; // NOT OWNED + Observer* task_queue_selector_observer_; // Not owned. DISALLOW_COPY_AND_ASSIGN(TaskQueueSelector); }; @@ -221,4 +222,4 @@ } // namespace sequence_manager } // namespace base -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_H_ +#endif // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_logic.h b/base/task/sequence_manager/task_queue_selector_logic.h similarity index 81% rename from third_party/blink/renderer/platform/scheduler/base/task_queue_selector_logic.h rename to base/task/sequence_manager/task_queue_selector_logic.h index 7c8e91c..8cf8933 100644 --- a/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_logic.h +++ b/base/task/sequence_manager/task_queue_selector_logic.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_LOGIC_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_LOGIC_H_ +#ifndef BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_LOGIC_H_ +#define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_LOGIC_H_ namespace base { namespace sequence_manager { @@ -34,4 +34,4 @@ } // namespace sequence_manager } // namespace base -#endif +#endif // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_LOGIC_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.cc b/base/task/sequence_manager/thread_controller_impl.cc similarity index 91% rename from third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.cc rename to base/task/sequence_manager/thread_controller_impl.cc index 49c4d45..c68672b1 100644 --- a/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.cc +++ b/base/task/sequence_manager/thread_controller_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h" +#include "base/task/sequence_manager/thread_controller_impl.h" #include "base/bind.h" #include "base/memory/ptr_util.h" @@ -170,7 +170,16 @@ sequence_->DidRunTask(); - // TODO(alexclarke): Find out why this is needed. + // NOTE: https://crbug.com/828835. + // When we're running inside a nested RunLoop it may quit anytime, so any + // outstanding pending tasks must run in the outer RunLoop + // (see SequenceManagerTestWithMessageLoop.QuitWhileNested test). + // Unfortunately, it's MessageLoop who's receving that signal and we can't + // know it before we return from DoWork, hence, OnExitNestedRunLoop + // will be called later. Since we must implement ThreadController and + // SequenceManager in conformance with MessageLoop task runners, we need + // to disable this batching optimization while nested. + // Implementing RunLoop::Delegate ourselves will help to resolve this issue. if (main_sequence_only().nesting_depth > 0) break; }
diff --git a/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h b/base/task/sequence_manager/thread_controller_impl.h similarity index 88% rename from third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h rename to base/task/sequence_manager/thread_controller_impl.h index 5e005c2..a2cd61d72 100644 --- a/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h +++ b/base/task/sequence_manager/thread_controller_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_CONTROLLER_IMPL_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_CONTROLLER_IMPL_H_ +#ifndef BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_IMPL_H_ +#define BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_IMPL_H_ #include "base/cancelable_callback.h" #include "base/debug/task_annotator.h" @@ -13,7 +13,6 @@ #include "base/sequence_checker.h" #include "base/single_thread_task_runner.h" #include "base/task/sequence_manager/thread_controller.h" -#include "third_party/blink/renderer/platform/platform_export.h" namespace base { @@ -24,8 +23,8 @@ namespace sequence_manager { namespace internal { -class PLATFORM_EXPORT ThreadControllerImpl : public ThreadController, - public RunLoop::NestingObserver { +class BASE_EXPORT ThreadControllerImpl : public ThreadController, + public RunLoop::NestingObserver { public: ~ThreadControllerImpl() override; @@ -127,4 +126,4 @@ } // namespace sequence_manager } // namespace base -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_CONTROLLER_IMPL_H_ +#endif // BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_IMPL_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/time_domain.cc b/base/task/sequence_manager/time_domain.cc similarity index 92% rename from third_party/blink/renderer/platform/scheduler/base/time_domain.cc rename to base/task/sequence_manager/time_domain.cc index bcf7f0e..8f47eb3 100644 --- a/third_party/blink/renderer/platform/scheduler/base/time_domain.cc +++ b/base/task/sequence_manager/time_domain.cc
@@ -2,13 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" +#include "base/task/sequence_manager/time_domain.h" -#include <set> - -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" +#include "base/task/sequence_manager/sequence_manager_impl.h" +#include "base/task/sequence_manager/task_queue_impl.h" +#include "base/task/sequence_manager/work_queue.h" namespace base { namespace sequence_manager {
diff --git a/base/task/sequence_manager/time_domain.h b/base/task/sequence_manager/time_domain.h index 14bad6a..e9e487b 100644 --- a/base/task/sequence_manager/time_domain.h +++ b/base/task/sequence_manager/time_domain.h
@@ -32,7 +32,7 @@ // TaskQueue maintains its own next wake-up time and communicates it // to the TimeDomain, which aggregates wake-ups across registered TaskQueues // into a global wake-up, which ultimately gets passed to the ThreadController. -class PLATFORM_EXPORT TimeDomain { +class BASE_EXPORT TimeDomain { public: virtual ~TimeDomain();
diff --git a/third_party/blink/renderer/platform/scheduler/base/work_queue.cc b/base/task/sequence_manager/work_queue.cc similarity index 97% rename from third_party/blink/renderer/platform/scheduler/base/work_queue.cc rename to base/task/sequence_manager/work_queue.cc index 10368d4e..4d95f4b 100644 --- a/third_party/blink/renderer/platform/scheduler/base/work_queue.cc +++ b/base/task/sequence_manager/work_queue.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" +#include "base/task/sequence_manager/work_queue.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h" +#include "base/task/sequence_manager/work_queue_sets.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/work_queue.h b/base/task/sequence_manager/work_queue.h similarity index 93% rename from third_party/blink/renderer/platform/scheduler/base/work_queue.h rename to base/task/sequence_manager/work_queue.h index fe1c7d5a..5197949 100644 --- a/third_party/blink/renderer/platform/scheduler/base/work_queue.h +++ b/base/task/sequence_manager/work_queue.h
@@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_H_ +#ifndef BASE_TASK_SEQUENCE_MANAGER_WORK_QUEUE_H_ +#define BASE_TASK_SEQUENCE_MANAGER_WORK_QUEUE_H_ +#include "base/base_export.h" #include "base/task/sequence_manager/enqueue_order.h" #include "base/task/sequence_manager/intrusive_heap.h" #include "base/task/sequence_manager/sequenced_task_source.h" +#include "base/task/sequence_manager/task_queue_impl.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" namespace base { namespace sequence_manager { @@ -27,7 +28,7 @@ // API subset used by WorkQueueSets pretends the WorkQueue is empty until the // fence is removed. This functionality is a primitive intended for use by // throttling mechanisms. -class PLATFORM_EXPORT WorkQueue { +class BASE_EXPORT WorkQueue { public: using QueueType = internal::TaskQueueImpl::WorkQueueType; @@ -148,4 +149,4 @@ } // namespace sequence_manager } // namespace base -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_H_ +#endif // BASE_TASK_SEQUENCE_MANAGER_WORK_QUEUE_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.cc b/base/task/sequence_manager/work_queue_sets.cc similarity index 98% rename from third_party/blink/renderer/platform/scheduler/base/work_queue_sets.cc rename to base/task/sequence_manager/work_queue_sets.cc index 94235c9..e56fc82e 100644 --- a/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.cc +++ b/base/task/sequence_manager/work_queue_sets.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h" +#include "base/task/sequence_manager/work_queue_sets.h" #include "base/logging.h"
diff --git a/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h b/base/task/sequence_manager/work_queue_sets.h similarity index 84% rename from third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h rename to base/task/sequence_manager/work_queue_sets.h index 8e9fb97e..01db040 100644 --- a/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h +++ b/base/task/sequence_manager/work_queue_sets.h
@@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_SETS_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_SETS_H_ +#ifndef BASE_TASK_SEQUENCE_MANAGER_WORK_QUEUE_SETS_H_ +#define BASE_TASK_SEQUENCE_MANAGER_WORK_QUEUE_SETS_H_ #include <map> #include <vector> +#include "base/base_export.h" #include "base/logging.h" #include "base/macros.h" #include "base/task/sequence_manager/intrusive_heap.h" +#include "base/task/sequence_manager/task_queue_impl.h" +#include "base/task/sequence_manager/work_queue.h" #include "base/trace_event/trace_event_argument.h" -#include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" namespace base { namespace sequence_manager { @@ -26,7 +26,7 @@ // TaskQueueSelector chooses to run a task a given priority). The reason this // works is because std::map is a tree based associative container and all the // values are kept in sorted order. -class PLATFORM_EXPORT WorkQueueSets { +class BASE_EXPORT WorkQueueSets { public: WorkQueueSets(size_t num_sets, const char* name); ~WorkQueueSets(); @@ -99,4 +99,4 @@ } // namespace sequence_manager } // namespace base -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_SETS_H_ +#endif // BASE_TASK_SEQUENCE_MANAGER_WORK_QUEUE_SETS_H_
diff --git a/base/threading/thread_perftest.cc b/base/threading/thread_perftest.cc index bf89049..d3fad97 100644 --- a/base/threading/thread_perftest.cc +++ b/base/threading/thread_perftest.cc
@@ -162,7 +162,10 @@ void Init() override { TaskPerfTest::Init(); for (size_t i = 0; i < threads_.size(); i++) { - threads_[i]->message_loop()->AddTaskObserver(&message_loop_observer); + threads_[i]->message_loop()->task_runner()->PostTask( + FROM_HERE, BindOnce(&MessageLoop::AddTaskObserver, + Unretained(threads_[i]->message_loop()), + Unretained(&message_loop_observer))); } } };
diff --git a/base/timer/mock_timer_unittest.cc b/base/timer/mock_timer_unittest.cc index 61716a4..45d0388 100644 --- a/base/timer/mock_timer_unittest.cc +++ b/base/timer/mock_timer_unittest.cc
@@ -15,7 +15,7 @@ TEST(MockTimerTest, FiresOnce) { int calls = 0; - base::MockTimer timer(false, false); + base::MockOneShotTimer timer; base::TimeDelta delay = base::TimeDelta::FromSeconds(2); timer.Start(FROM_HERE, delay, base::Bind(&CallMeMaybe, @@ -29,7 +29,7 @@ TEST(MockTimerTest, FiresRepeatedly) { int calls = 0; - base::MockTimer timer(true, true); + base::MockRepeatingTimer timer; base::TimeDelta delay = base::TimeDelta::FromSeconds(2); timer.Start(FROM_HERE, delay, base::Bind(&CallMeMaybe, @@ -44,7 +44,7 @@ TEST(MockTimerTest, Stops) { int calls = 0; - base::MockTimer timer(true, true); + base::MockRepeatingTimer timer; base::TimeDelta delay = base::TimeDelta::FromSeconds(2); timer.Start(FROM_HERE, delay, base::Bind(&CallMeMaybe, @@ -66,7 +66,7 @@ TEST(MockTimerTest, DoesNotRetainClosure) { HasWeakPtr *has_weak_ptr = new HasWeakPtr(); base::WeakPtr<HasWeakPtr> weak_ptr(has_weak_ptr->AsWeakPtr()); - base::MockTimer timer(false, false); + base::MockOneShotTimer timer; base::TimeDelta delay = base::TimeDelta::FromSeconds(2); ASSERT_TRUE(weak_ptr.get()); timer.Start(FROM_HERE, delay,
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc index 079c9b4f..4ac38bf8 100644 --- a/cc/animation/animation_host.cc +++ b/cc/animation/animation_host.cc
@@ -620,12 +620,14 @@ TRACE_EVENT0("cc", "AnimationHost::SetMutationUpdate"); for (auto& animation_state : output_state->animations) { - int id = animation_state.animation_id; + WorkletAnimationId id = animation_state.worklet_animation_id; // TODO(majidvp): Use a map to make lookup O(1) - auto to_update = - std::find_if(ticking_animations_.begin(), ticking_animations_.end(), - [id](auto& it) { return it->id() == id; }); + auto to_update = std::find_if( + ticking_animations_.begin(), ticking_animations_.end(), [id](auto& it) { + return it->IsWorkletAnimation() && + ToWorkletAnimation(it.get())->worklet_animation_id() == id; + }); if (to_update == ticking_animations_.end()) continue;
diff --git a/cc/animation/worklet_animation.cc b/cc/animation/worklet_animation.cc index 7692880..4381586 100644 --- a/cc/animation/worklet_animation.cc +++ b/cc/animation/worklet_animation.cc
@@ -4,6 +4,7 @@ #include "cc/animation/worklet_animation.h" +#include "cc/animation/animation_id_provider.h" #include "cc/animation/keyframe_effect.h" #include "cc/animation/scroll_timeline.h" #include "cc/trees/animation_options.h" @@ -11,12 +12,14 @@ namespace cc { WorkletAnimation::WorkletAnimation( - int id, + int cc_animation_id, + WorkletAnimationId worklet_animation_id, const std::string& name, std::unique_ptr<ScrollTimeline> scroll_timeline, std::unique_ptr<AnimationOptions> options, bool is_controlling_instance) - : SingleKeyframeEffectAnimation(id), + : SingleKeyframeEffectAnimation(cc_animation_id), + worklet_animation_id_(worklet_animation_id), name_(name), scroll_timeline_(std::move(scroll_timeline)), options_(std::move(options)), @@ -28,12 +31,13 @@ WorkletAnimation::~WorkletAnimation() = default; scoped_refptr<WorkletAnimation> WorkletAnimation::Create( - int id, + WorkletAnimationId worklet_animation_id, const std::string& name, std::unique_ptr<ScrollTimeline> scroll_timeline, std::unique_ptr<AnimationOptions> options) { return WrapRefCounted(new WorkletAnimation( - id, name, std::move(scroll_timeline), std::move(options), false)); + AnimationIdProvider::NextAnimationId(), worklet_animation_id, name, + std::move(scroll_timeline), std::move(options), false)); } scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const { @@ -41,8 +45,9 @@ if (scroll_timeline_) impl_timeline = scroll_timeline_->CreateImplInstance(); - return WrapRefCounted(new WorkletAnimation( - id(), name(), std::move(impl_timeline), CloneOptions(), true)); + return WrapRefCounted(new WorkletAnimation(id(), worklet_animation_id_, + name(), std::move(impl_timeline), + CloneOptions(), true)); } void WorkletAnimation::PushPropertiesTo(Animation* animation_impl) { @@ -89,15 +94,15 @@ switch (state_) { case State::PENDING: - input_state->added_and_updated_animations.push_back( - {id(), name(), current_time, CloneOptions()}); + input_state->Add( + {worklet_animation_id(), name(), current_time, CloneOptions()}); state_ = State::RUNNING; break; case State::RUNNING: - input_state->updated_animations.push_back({id(), current_time}); + input_state->Update({worklet_animation_id(), current_time}); break; case State::REMOVED: - input_state->removed_animations.push_back(id()); + input_state->Remove(worklet_animation_id()); break; } }
diff --git a/cc/animation/worklet_animation.h b/cc/animation/worklet_animation.h index 5a2497a..d30771b 100644 --- a/cc/animation/worklet_animation.h +++ b/cc/animation/worklet_animation.h
@@ -24,18 +24,20 @@ : public SingleKeyframeEffectAnimation { public: enum class State { PENDING, RUNNING, REMOVED }; - WorkletAnimation(int id, + WorkletAnimation(int cc_animation_id, + WorkletAnimationId worklet_animation_id, const std::string& name, std::unique_ptr<ScrollTimeline> scroll_timeline, std::unique_ptr<AnimationOptions> options, bool is_controlling_instance); static scoped_refptr<WorkletAnimation> Create( - int id, + WorkletAnimationId worklet_animation_id, const std::string& name, std::unique_ptr<ScrollTimeline> scroll_timeline, std::unique_ptr<AnimationOptions> options); scoped_refptr<Animation> CreateImplInstance() const override; + WorkletAnimationId worklet_animation_id() { return worklet_animation_id_; } const std::string& name() const { return name_; } const ScrollTimeline* scroll_timeline() const { return scroll_timeline_.get(); @@ -83,6 +85,7 @@ return options_ ? options_->Clone() : nullptr; } + WorkletAnimationId worklet_animation_id_; std::string name_; // The ScrollTimeline associated with the underlying animation. If null, the
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc index 939eecf4..058deac1 100644 --- a/cc/animation/worklet_animation_unittest.cc +++ b/cc/animation/worklet_animation_unittest.cc
@@ -33,20 +33,20 @@ worklet_animation_ = WorkletAnimation::Create( worklet_animation_id_, "test_name", nullptr, nullptr); - + int cc_id = worklet_animation_->id(); worklet_animation_->AttachElement(element_id_); host_->AddAnimationTimeline(timeline_); timeline_->AttachAnimation(worklet_animation_); host_->PushPropertiesTo(host_impl_); timeline_impl_ = host_impl_->GetTimelineById(timeline_id_); - worklet_animation_impl_ = ToWorkletAnimation( - timeline_impl_->GetAnimationById(worklet_animation_id_)); + worklet_animation_impl_ = + ToWorkletAnimation(timeline_impl_->GetAnimationById(cc_id)); } scoped_refptr<WorkletAnimation> worklet_animation_; scoped_refptr<WorkletAnimation> worklet_animation_impl_; - int worklet_animation_id_ = 11; + WorkletAnimationId worklet_animation_id_{11, 12}; }; class MockScrollTimeline : public ScrollTimeline { @@ -73,8 +73,7 @@ host_impl_->ActivateAnimations(); base::TimeDelta local_time = base::TimeDelta::FromSecondsD(duration / 2); - - worklet_animation_impl_->SetOutputState({0, local_time}); + worklet_animation_impl_->SetOutputState({worklet_animation_id_, local_time}); TickAnimationsTransferEvents(base::TimeTicks(), 0u); @@ -157,7 +156,9 @@ std::make_unique<MutatorInputState>(); worklet_animation->UpdateInputState(state.get(), base::TimeTicks::Now(), scroll_tree, true); - EXPECT_EQ(1234, state->added_and_updated_animations[0].current_time); + std::unique_ptr<AnimationWorkletInput> input = + state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(1234, input->added_and_updated_animations[0].current_time); } TEST_F(WorkletAnimationTest, @@ -173,23 +174,25 @@ base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 246.8); ScrollTree scroll_tree; - std::unique_ptr<MutatorInputState> first_state = + std::unique_ptr<MutatorInputState> state = std::make_unique<MutatorInputState>(); - worklet_animation->UpdateInputState(first_state.get(), first_ticks, - scroll_tree, true); + worklet_animation->UpdateInputState(state.get(), first_ticks, scroll_tree, + true); // First state request sets the start time and thus current time should be 0. - EXPECT_EQ(0, first_state->added_and_updated_animations[0].current_time); - std::unique_ptr<MutatorInputState> second_state = - std::make_unique<MutatorInputState>(); - worklet_animation->UpdateInputState(second_state.get(), second_ticks, - scroll_tree, true); - EXPECT_EQ(123.4, second_state->updated_animations[0].current_time); + std::unique_ptr<AnimationWorkletInput> input = + state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(0, input->added_and_updated_animations[0].current_time); + state.reset(new MutatorInputState); + worklet_animation->UpdateInputState(state.get(), second_ticks, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(123.4, input->updated_animations[0].current_time); // Should always offset from start time. - std::unique_ptr<MutatorInputState> third_state = - std::make_unique<MutatorInputState>(); - worklet_animation->UpdateInputState(third_state.get(), third_ticks, - scroll_tree, true); - EXPECT_EQ(246.8, third_state->updated_animations[0].current_time); + state.reset(new MutatorInputState()); + worklet_animation->UpdateInputState(state.get(), third_ticks, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(246.8, input->updated_animations[0].current_time); } // This test verifies that worklet animation state is properly updated. @@ -223,10 +226,12 @@ base::TimeTicks time; worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, true); - EXPECT_EQ(state->added_and_updated_animations.size(), 1u); - EXPECT_EQ("test_name", state->added_and_updated_animations[0].name); - EXPECT_EQ(state->updated_animations.size(), 0u); - EXPECT_EQ(state->removed_animations.size(), 0u); + std::unique_ptr<AnimationWorkletInput> input = + state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 1u); + EXPECT_EQ("test_name", input->added_and_updated_animations[0].name); + EXPECT_EQ(input->updated_animations.size(), 0u); + EXPECT_EQ(input->removed_animations.size(), 0u); // The state of WorkletAnimation is updated to RUNNING after calling // UpdateInputState above. @@ -234,9 +239,10 @@ time += base::TimeDelta::FromSecondsD(0.1); worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, true); - EXPECT_EQ(state->added_and_updated_animations.size(), 0u); - EXPECT_EQ(state->updated_animations.size(), 1u); - EXPECT_EQ(state->removed_animations.size(), 0u); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 0u); + EXPECT_EQ(input->updated_animations.size(), 1u); + EXPECT_EQ(input->removed_animations.size(), 0u); // Operating on individual KeyframeModel doesn't affect the state of // WorkletAnimation. @@ -245,9 +251,10 @@ time += base::TimeDelta::FromSecondsD(0.1); worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, true); - EXPECT_EQ(state->added_and_updated_animations.size(), 0u); - EXPECT_EQ(state->updated_animations.size(), 1u); - EXPECT_EQ(state->removed_animations.size(), 0u); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 0u); + EXPECT_EQ(input->updated_animations.size(), 1u); + EXPECT_EQ(input->removed_animations.size(), 0u); // WorkletAnimation sets state to REMOVED when JavaScript fires cancel() which // leads to RemoveKeyframeModels. @@ -256,10 +263,11 @@ state.reset(new MutatorInputState()); worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, true); - EXPECT_EQ(state->added_and_updated_animations.size(), 0u); - EXPECT_EQ(state->updated_animations.size(), 0u); - EXPECT_EQ(state->removed_animations.size(), 1u); - EXPECT_EQ(state->removed_animations[0], worklet_animation_id_); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 0u); + EXPECT_EQ(input->updated_animations.size(), 0u); + EXPECT_EQ(input->removed_animations.size(), 1u); + EXPECT_EQ(input->removed_animations[0], worklet_animation_id_); } // This test verifies that worklet animation gets skipped properly. @@ -289,21 +297,25 @@ base::TimeTicks time; worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, true); - EXPECT_EQ(state->added_and_updated_animations.size(), 1u); - EXPECT_EQ(state->updated_animations.size(), 0u); + std::unique_ptr<AnimationWorkletInput> input = + state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 1u); + EXPECT_EQ(input->updated_animations.size(), 0u); state.reset(new MutatorInputState()); // No update on the input state if input time stays the same. worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, true); - EXPECT_EQ(state->updated_animations.size(), 0u); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_FALSE(input); state.reset(new MutatorInputState()); // Different input time causes the input state to be updated. time += base::TimeDelta::FromSecondsD(0.1); worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, true); - EXPECT_EQ(state->updated_animations.size(), 1u); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->updated_animations.size(), 1u); state.reset(new MutatorInputState()); // Input state gets updated when the worklet animation is to be removed even @@ -311,8 +323,9 @@ worklet_animation_impl_->RemoveKeyframeModels(); worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, true); - EXPECT_EQ(state->updated_animations.size(), 0u); - EXPECT_EQ(state->removed_animations.size(), 1u); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->updated_animations.size(), 0u); + EXPECT_EQ(input->removed_animations.size(), 1u); } } // namespace
diff --git a/cc/scheduler/compositor_timing_history.cc b/cc/scheduler/compositor_timing_history.cc index 20f2e5b4..d6fe934 100644 --- a/cc/scheduler/compositor_timing_history.cc +++ b/cc/scheduler/compositor_timing_history.cc
@@ -282,10 +282,9 @@ public: ~BrowserUMAReporter() override = default; - void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) override { - UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED( - "Scheduling.Browser.BeginMainFrameIntervalCritical", interval); - } + // BeginMainFrameIntervalCritical is not meaningful to measure on browser + // side because browser rendering fps is not at 60. + void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) override {} void AddBeginMainFrameIntervalNotCritical(base::TimeDelta interval) override { UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED(
diff --git a/cc/trees/layer_tree_mutator.cc b/cc/trees/layer_tree_mutator.cc index 801e3ad..addb471 100644 --- a/cc/trees/layer_tree_mutator.cc +++ b/cc/trees/layer_tree_mutator.cc
@@ -4,25 +4,88 @@ #include "cc/trees/layer_tree_mutator.h" +#include <algorithm> + namespace cc { -MutatorInputState::AddAndUpdateState::AddAndUpdateState( - int animation_id, +AnimationWorkletInput::AddAndUpdateState::AddAndUpdateState( + WorkletAnimationId worklet_animation_id, std::string name, double current_time, std::unique_ptr<AnimationOptions> options) - : animation_id(animation_id), + : worklet_animation_id(worklet_animation_id), name(name), current_time(current_time), options(std::move(options)) {} -MutatorInputState::AddAndUpdateState::AddAndUpdateState(AddAndUpdateState&&) = - default; -MutatorInputState::AddAndUpdateState::~AddAndUpdateState() = default; +AnimationWorkletInput::AddAndUpdateState::AddAndUpdateState( + AddAndUpdateState&&) = default; +AnimationWorkletInput::AddAndUpdateState::~AddAndUpdateState() = default; + +#if DCHECK_IS_ON() +bool AnimationWorkletInput::ValidateScope(int scope_id) const { + return std::all_of(added_and_updated_animations.cbegin(), + added_and_updated_animations.cend(), + [scope_id](auto& it) { + return it.worklet_animation_id.scope_id == scope_id; + }) && + std::all_of(updated_animations.cbegin(), updated_animations.cend(), + [scope_id](auto& it) { + return it.worklet_animation_id.scope_id == scope_id; + }) && + std::all_of(removed_animations.cbegin(), removed_animations.cend(), + [scope_id](auto& it) { return it.scope_id == scope_id; }); +} +#endif + +AnimationWorkletInput::AnimationWorkletInput() = default; +AnimationWorkletInput::~AnimationWorkletInput() = default; MutatorInputState::MutatorInputState() = default; MutatorInputState::~MutatorInputState() = default; -MutatorOutputState::MutatorOutputState() = default; -MutatorOutputState::~MutatorOutputState() = default; +bool MutatorInputState::IsEmpty() const { + // If there is an AnimationWorkletInput entry in the map then that entry is + // guranteed to be non-empty. So checking |inputs_| map emptiness is + // sufficient. + return inputs_.empty(); +} + +AnimationWorkletInput& MutatorInputState::EnsureWorkletEntry(int id) { + auto it = inputs_.find(id); + if (it == inputs_.end()) + it = inputs_.emplace_hint(it, id, new AnimationWorkletInput); + + return *it->second; +} + +void MutatorInputState::Add(AnimationWorkletInput::AddAndUpdateState&& state) { + AnimationWorkletInput& worklet_input = + EnsureWorkletEntry(state.worklet_animation_id.scope_id); + worklet_input.added_and_updated_animations.push_back(std::move(state)); +} +void MutatorInputState::Update(AnimationWorkletInput::UpdateState&& state) { + AnimationWorkletInput& worklet_input = + EnsureWorkletEntry(state.worklet_animation_id.scope_id); + worklet_input.updated_animations.push_back(std::move(state)); +} +void MutatorInputState::Remove(WorkletAnimationId worklet_animation_id) { + AnimationWorkletInput& worklet_input = + EnsureWorkletEntry(worklet_animation_id.scope_id); + worklet_input.removed_animations.push_back(worklet_animation_id); +} + +std::unique_ptr<AnimationWorkletInput> MutatorInputState::TakeWorkletState( + int scope_id) { + auto it = inputs_.find(scope_id); + if (it == inputs_.end()) + return nullptr; + + std::unique_ptr<AnimationWorkletInput> result = std::move(it->second); + inputs_.erase(it); + return result; +} + +AnimationWorkletOutput::AnimationWorkletOutput() = default; +AnimationWorkletOutput::~AnimationWorkletOutput() = default; } // namespace cc
diff --git a/cc/trees/layer_tree_mutator.h b/cc/trees/layer_tree_mutator.h index dec770b..700549f 100644 --- a/cc/trees/layer_tree_mutator.h +++ b/cc/trees/layer_tree_mutator.h
@@ -12,50 +12,96 @@ #include <memory> #include <string> +#include <unordered_map> #include <vector> namespace cc { -struct CC_EXPORT MutatorInputState { +struct CC_EXPORT WorkletAnimationId { + // Uniquely identifies the animation worklet with which this animation is + // associated. + int scope_id; + // Uniquely identifies the animation within its animation worklet. Note that + // animation_id is only guaranteed to be unique per animation worklet. + int animation_id; + + inline bool operator==(const WorkletAnimationId& rhs) const { + return (this->scope_id == rhs.scope_id) && + (this->animation_id == rhs.animation_id); + } +}; + +struct CC_EXPORT AnimationWorkletInput { struct CC_EXPORT AddAndUpdateState { - int animation_id; + WorkletAnimationId worklet_animation_id; // Name associated with worklet animation. std::string name; // Worklet animation's current time, from its associated timeline. double current_time; std::unique_ptr<AnimationOptions> options; - AddAndUpdateState(int animation_id, + AddAndUpdateState(WorkletAnimationId worklet_animation_id, std::string name, double current_time, std::unique_ptr<AnimationOptions> options); + AddAndUpdateState(AddAndUpdateState&&); ~AddAndUpdateState(); }; struct CC_EXPORT UpdateState { - int animation_id = 0; + WorkletAnimationId worklet_animation_id; // Worklet animation's current time, from its associated timeline. double current_time = 0; }; + // Note: When adding any new fields please also update ValidateScope to + // reflect them if necessary. + std::vector<AddAndUpdateState> added_and_updated_animations; + std::vector<UpdateState> updated_animations; + std::vector<WorkletAnimationId> removed_animations; + + AnimationWorkletInput(); + ~AnimationWorkletInput(); + +#if DCHECK_IS_ON() + // Verifies all animation states have the expected scope id. + bool ValidateScope(int scope_id) const; +#endif + DISALLOW_COPY_AND_ASSIGN(AnimationWorkletInput); +}; + +class CC_EXPORT MutatorInputState { + public: MutatorInputState(); ~MutatorInputState(); - bool IsEmpty() { - return added_and_updated_animations.empty() && updated_animations.empty() && - removed_animations.empty(); - } + bool IsEmpty() const; + void Add(AnimationWorkletInput::AddAndUpdateState&& state); + void Update(AnimationWorkletInput::UpdateState&& state); + void Remove(WorkletAnimationId worklet_animation_id); - std::vector<AddAndUpdateState> added_and_updated_animations; - std::vector<UpdateState> updated_animations; - std::vector<int> removed_animations; + // Returns input for animation worklet with the given |scope_id| and nullptr + // if there is no input. + std::unique_ptr<AnimationWorkletInput> TakeWorkletState(int scope_id); + + private: + using InputMap = + std::unordered_map<int, std::unique_ptr<AnimationWorkletInput>>; + + // Maps a scope id to its associated AnimationWorkletInput instance. + // Only contains scope ids for which there is a non-empty input. + InputMap inputs_; + + // Returns iterator pointing to the entry in |inputs_| map whose key is id. It + // inserts a new entry if none exists. + AnimationWorkletInput& EnsureWorkletEntry(int id); DISALLOW_COPY_AND_ASSIGN(MutatorInputState); }; -struct CC_EXPORT MutatorOutputState { +struct CC_EXPORT AnimationWorkletOutput { struct CC_EXPORT AnimationState { - int animation_id = 0; + WorkletAnimationId worklet_animation_id; // The animator effect's local time. // TODO(majidvp): This assumes each animator has a single output effect // which does not hold once we state support group effects. @@ -63,12 +109,16 @@ base::TimeDelta local_time; }; - MutatorOutputState(); - ~MutatorOutputState(); + AnimationWorkletOutput(); + ~AnimationWorkletOutput(); std::vector<AnimationState> animations; }; +// LayerTreeMutatorClient processes worklet outputs individually so we can +// define mutator output to be the same as animation worklet output. +using MutatorOutputState = AnimationWorkletOutput; + class LayerTreeMutatorClient { public: // Called when mutator needs to update its output.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 2a8b891..94114c4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -162,29 +162,25 @@ */ public class ChromeTabbedActivity extends ChromeActivity implements OverviewModeObserver, ScreenshotMonitorDelegate { + @IntDef({BackPressedResult.NOTHING_HAPPENED, BackPressedResult.HELP_URL_CLOSED, + BackPressedResult.MINIMIZED_NO_TAB_CLOSED, BackPressedResult.MINIMIZED_TAB_CLOSED, + BackPressedResult.TAB_CLOSED, BackPressedResult.TAB_IS_NULL, + BackPressedResult.EXITED_TAB_SWITCHER, BackPressedResult.EXITED_FULLSCREEN, + BackPressedResult.NAVIGATED_BACK}) @Retention(RetentionPolicy.SOURCE) - @IntDef({ - BACK_PRESSED_NOTHING_HAPPENED, - BACK_PRESSED_HELP_URL_CLOSED, - BACK_PRESSED_MINIMIZED_NO_TAB_CLOSED, - BACK_PRESSED_MINIMIZED_TAB_CLOSED, - BACK_PRESSED_TAB_CLOSED, - BACK_PRESSED_TAB_IS_NULL, - BACK_PRESSED_EXITED_TAB_SWITCHER, - BACK_PRESSED_EXITED_FULLSCREEN, - BACK_PRESSED_NAVIGATED_BACK - }) - private @interface BackPressedResult {} - private static final int BACK_PRESSED_NOTHING_HAPPENED = 0; - private static final int BACK_PRESSED_HELP_URL_CLOSED = 1; - private static final int BACK_PRESSED_MINIMIZED_NO_TAB_CLOSED = 2; - private static final int BACK_PRESSED_MINIMIZED_TAB_CLOSED = 3; - private static final int BACK_PRESSED_TAB_CLOSED = 4; - private static final int BACK_PRESSED_TAB_IS_NULL = 5; - private static final int BACK_PRESSED_EXITED_TAB_SWITCHER = 6; - private static final int BACK_PRESSED_EXITED_FULLSCREEN = 7; - private static final int BACK_PRESSED_NAVIGATED_BACK = 8; - private static final int BACK_PRESSED_COUNT = 9; + private @interface BackPressedResult { + int NOTHING_HAPPENED = 0; + int HELP_URL_CLOSED = 1; + int MINIMIZED_NO_TAB_CLOSED = 2; + int MINIMIZED_TAB_CLOSED = 3; + int TAB_CLOSED = 4; + int TAB_IS_NULL = 5; + int EXITED_TAB_SWITCHER = 6; + int EXITED_FULLSCREEN = 7; + int NAVIGATED_BACK = 8; + + int NUM_ENTRIES = 9; + } private static final String TAG = "ChromeTabbedActivity"; @@ -1703,8 +1699,8 @@ private void recordBackPressedUma(String logMessage, @BackPressedResult int action) { Log.i(TAG, "Back pressed: " + logMessage); RecordHistogram.recordEnumeratedHistogram( - "Android.Activity.ChromeTabbedActivity.SystemBackAction", - action, BACK_PRESSED_COUNT); + "Android.Activity.ChromeTabbedActivity.SystemBackAction", action, + BackPressedResult.NUM_ENTRIES); } private void recordLauncherShortcutAction(boolean isIncognito) { @@ -1736,7 +1732,7 @@ final Tab currentTab = getActivityTab(); if (exitFullscreenIfShowing()) { - recordBackPressedUma("Exited fullscreen", BACK_PRESSED_EXITED_FULLSCREEN); + recordBackPressedUma("Exited fullscreen", BackPressedResult.EXITED_FULLSCREEN); return true; } @@ -1745,20 +1741,20 @@ if (mTabModalHandler.handleBackPress()) return true; if (currentTab == null) { - recordBackPressedUma("currentTab is null", BACK_PRESSED_TAB_IS_NULL); + recordBackPressedUma("currentTab is null", BackPressedResult.TAB_IS_NULL); moveTaskToBack(true); return true; } // If we are in overview mode and not a tablet, then leave overview mode on back. if (mLayoutManager.overviewVisible() && !isTablet()) { - recordBackPressedUma("Hid overview", BACK_PRESSED_EXITED_TAB_SWITCHER); + recordBackPressedUma("Hid overview", BackPressedResult.EXITED_TAB_SWITCHER); mLayoutManager.hideOverview(true); return true; } if (getToolbarManager().back()) { - recordBackPressedUma("Navigating backward", BACK_PRESSED_NAVIGATED_BACK); + recordBackPressedUma("Navigating backward", BackPressedResult.NAVIGATED_BACK); return true; } @@ -1769,7 +1765,7 @@ final boolean helpUrl = currentTab.getUrl().startsWith(HELP_URL_PREFIX); if (type == TabLaunchType.FROM_CHROME_UI && helpUrl) { getCurrentTabModel().closeTab(currentTab); - recordBackPressedUma("Closed tab for help URL", BACK_PRESSED_HELP_URL_CLOSED); + recordBackPressedUma("Closed tab for help URL", BackPressedResult.HELP_URL_CLOSED); return true; } @@ -1782,24 +1778,26 @@ final boolean minimizeApp = !shouldCloseTab || currentTab.isCreatedForExternalApp(); if (minimizeApp) { if (shouldCloseTab) { - recordBackPressedUma("Minimized and closed tab", BACK_PRESSED_MINIMIZED_TAB_CLOSED); + recordBackPressedUma( + "Minimized and closed tab", BackPressedResult.MINIMIZED_TAB_CLOSED); mActivityStopMetrics.setStopReason(ActivityStopMetrics.STOP_REASON_BACK_BUTTON); sendToBackground(currentTab); return true; } else { - recordBackPressedUma("Minimized, kept tab", BACK_PRESSED_MINIMIZED_NO_TAB_CLOSED); + recordBackPressedUma( + "Minimized, kept tab", BackPressedResult.MINIMIZED_NO_TAB_CLOSED); mActivityStopMetrics.setStopReason(ActivityStopMetrics.STOP_REASON_BACK_BUTTON); sendToBackground(null); return true; } } else if (shouldCloseTab) { - recordBackPressedUma("Tab closed", BACK_PRESSED_TAB_CLOSED); + recordBackPressedUma("Tab closed", BackPressedResult.TAB_CLOSED); getCurrentTabModel().closeTab(currentTab, true, false, false); return true; } assert false : "The back button should have already been handled by this point"; - recordBackPressedUma("Unhandled", BACK_PRESSED_NOTHING_HAPPENED); + recordBackPressedUma("Unhandled", BackPressedResult.NOTHING_HAPPENED); return false; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DevToolsServer.java b/chrome/android/java/src/org/chromium/chrome/browser/DevToolsServer.java index 86ab9e2e..801e50a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/DevToolsServer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/DevToolsServer.java
@@ -5,11 +5,15 @@ package org.chromium.chrome.browser; import android.content.pm.PackageManager; +import android.support.annotation.IntDef; import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ContextUtils; import org.chromium.base.annotations.CalledByNative; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Controller for Remote Web Debugging (Developer Tools). */ @@ -19,13 +23,14 @@ private long mNativeDevToolsServer; // Defines what processes may access to the socket. - public enum Security { + @IntDef({Security.DEFAULT, Security.ALLOW_DEBUG_PERMISSION}) + @Retention(RetentionPolicy.SOURCE) + public @interface Security { // Use content::CanUserConnectToDevTools to authorize access to the socket. - DEFAULT, - + int DEFAULT = 0; // In addition to default authorization allows access to an app with android permission // named chromeAppPackageName + DEBUG_PERMISSION_SIFFIX. - ALLOW_DEBUG_PERMISSION, + int ALLOW_DEBUG_PERMISSION = 1; } public DevToolsServer(String socketNamePrefix) { @@ -41,7 +46,7 @@ return nativeIsRemoteDebuggingEnabled(mNativeDevToolsServer); } - public void setRemoteDebuggingEnabled(boolean enabled, Security security) { + public void setRemoteDebuggingEnabled(boolean enabled, @Security int security) { boolean allowDebugPermission = security == Security.ALLOW_DEBUG_PERMISSION; nativeSetRemoteDebuggingEnabled(mNativeDevToolsServer, enabled, allowDebugPermission); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java index eed0fcf..f1cf7955 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -92,19 +92,18 @@ private static final int EXPIRATION_FIELDS_LENGTH = 2; - public static final int ERROR_TYPE_EXPIRATION_MONTH = 1; - public static final int ERROR_TYPE_EXPIRATION_YEAR = 2; - public static final int ERROR_TYPE_EXPIRATION_DATE = 3; - public static final int ERROR_TYPE_CVC = 4; - public static final int ERROR_TYPE_CVC_AND_EXPIRATION = 5; - public static final int ERROR_TYPE_NOT_ENOUGH_INFO = 6; - public static final int ERROR_TYPE_NONE = 7; - + @IntDef({ErrorType.EXPIRATION_MONTH, ErrorType.EXPIRATION_YEAR, ErrorType.EXPIRATION_DATE, + ErrorType.CVC, ErrorType.CVC_AND_EXPIRATION, ErrorType.NOT_ENOUGH_INFO, ErrorType.NONE}) @Retention(RetentionPolicy.SOURCE) - @IntDef({ERROR_TYPE_EXPIRATION_MONTH, ERROR_TYPE_EXPIRATION_YEAR, ERROR_TYPE_EXPIRATION_DATE, - ERROR_TYPE_CVC, ERROR_TYPE_CVC_AND_EXPIRATION, ERROR_TYPE_NOT_ENOUGH_INFO, - ERROR_TYPE_NONE}) - public @interface ErrorType {} + public @interface ErrorType { + int EXPIRATION_MONTH = 1; + int EXPIRATION_YEAR = 2; + int EXPIRATION_DATE = 3; + int CVC = 4; + int CVC_AND_EXPIRATION = 5; + int NOT_ENOUGH_INFO = 6; + int NONE = 7; + } /** * An interface to handle the interaction with an CardUnmaskPrompt object. @@ -350,7 +349,7 @@ Button positiveButton = mDialog.getButton(ModalDialogView.BUTTON_POSITIVE); @ErrorType int errorType = getExpirationAndCvcErrorType(); - positiveButton.setEnabled(errorType == ERROR_TYPE_NONE); + positiveButton.setEnabled(errorType == ErrorType.NONE); showDetailedErrorMessage(errorType); moveFocus(errorType); @@ -457,7 +456,7 @@ * @param errorType The type of error detected. */ private void moveFocus(@ErrorType int errorType) { - if (errorType == ERROR_TYPE_NOT_ENOUGH_INFO) { + if (errorType == ErrorType.NOT_ENOUGH_INFO) { if (mMonthInput.isFocused() && mMonthInput.getText().length() == EXPIRATION_FIELDS_LENGTH) { // The user just finished typing in the month field and there are no validation @@ -489,28 +488,23 @@ */ private void showDetailedErrorMessage(@ErrorType int errorType) { switch (errorType) { - case ERROR_TYPE_EXPIRATION_MONTH: + case ErrorType.EXPIRATION_MONTH: showErrorMessage(mExpirationMonthErrorMessage); break; - - case ERROR_TYPE_EXPIRATION_YEAR: + case ErrorType.EXPIRATION_YEAR: showErrorMessage(mExpirationYearErrorMessage); break; - - case ERROR_TYPE_EXPIRATION_DATE: + case ErrorType.EXPIRATION_DATE: showErrorMessage(mExpirationDateErrorMessage); break; - - case ERROR_TYPE_CVC: + case ErrorType.CVC: showErrorMessage(mCvcErrorMessage); break; - - case ERROR_TYPE_CVC_AND_EXPIRATION: + case ErrorType.CVC_AND_EXPIRATION: showErrorMessage(mCvcAndExpirationErrorMessage); break; - - case ERROR_TYPE_NONE: - case ERROR_TYPE_NOT_ENOUGH_INFO: + case ErrorType.NONE: + case ErrorType.NOT_ENOUGH_INFO: default: clearInputError(); return; @@ -536,14 +530,13 @@ PorterDuff.Mode.SRC_IN); // Decide on what field(s) to apply the filter. - boolean filterMonth = errorType == ERROR_TYPE_EXPIRATION_MONTH - || errorType == ERROR_TYPE_EXPIRATION_DATE - || errorType == ERROR_TYPE_CVC_AND_EXPIRATION; - boolean filterYear = errorType == ERROR_TYPE_EXPIRATION_YEAR - || errorType == ERROR_TYPE_EXPIRATION_DATE - || errorType == ERROR_TYPE_CVC_AND_EXPIRATION; - boolean filterCvc = - errorType == ERROR_TYPE_CVC || errorType == ERROR_TYPE_CVC_AND_EXPIRATION; + boolean filterMonth = errorType == ErrorType.EXPIRATION_MONTH + || errorType == ErrorType.EXPIRATION_DATE + || errorType == ErrorType.CVC_AND_EXPIRATION; + boolean filterYear = errorType == ErrorType.EXPIRATION_YEAR + || errorType == ErrorType.EXPIRATION_DATE + || errorType == ErrorType.CVC_AND_EXPIRATION; + boolean filterCvc = errorType == ErrorType.CVC || errorType == ErrorType.CVC_AND_EXPIRATION; updateColorForInput(mMonthInput, filterMonth ? filter : null); updateColorForInput(mYearInput, filterYear ? filter : null); @@ -557,7 +550,8 @@ * @return The ErrorType value representing the type of error found for the unmask fields. */ @ErrorType private int getExpirationAndCvcErrorType() { - @ErrorType int errorType = ERROR_TYPE_NONE; + @ErrorType + int errorType = ErrorType.NONE; if (mShouldRequestExpirationDate) errorType = getExpirationDateErrorType(); @@ -567,15 +561,15 @@ if (mDidFocusOnCvc && !mCardUnmaskInput.isFocused()) { // The CVC is invalid and the user has typed in the CVC field, but is not focused on it // now. Add the CVC error to the current error. - if (errorType == ERROR_TYPE_NONE || errorType == ERROR_TYPE_NOT_ENOUGH_INFO) { - errorType = ERROR_TYPE_CVC; + if (errorType == ErrorType.NONE || errorType == ErrorType.NOT_ENOUGH_INFO) { + errorType = ErrorType.CVC; } else { - errorType = ERROR_TYPE_CVC_AND_EXPIRATION; + errorType = ErrorType.CVC_AND_EXPIRATION; } } else { // The CVC is invalid but the user is not done with the field. // If no other errors were detected, set that there is not enough information. - if (errorType == ERROR_TYPE_NONE) errorType = ERROR_TYPE_NOT_ENOUGH_INFO; + if (errorType == ErrorType.NONE) errorType = ErrorType.NOT_ENOUGH_INFO; } return errorType; @@ -591,7 +585,7 @@ @ErrorType private int getExpirationDateErrorType() { if (mThisYear == -1 || mThisMonth == -1) { mValidationWaitsForCalendarTask = true; - return ERROR_TYPE_NOT_ENOUGH_INFO; + return ErrorType.NOT_ENOUGH_INFO; } int month = getMonth(); @@ -599,9 +593,9 @@ if (mMonthInput.getText().length() == EXPIRATION_FIELDS_LENGTH || (!mMonthInput.isFocused() && mDidFocusOnMonth)) { // mFinishedTypingMonth = true; - return ERROR_TYPE_EXPIRATION_MONTH; + return ErrorType.EXPIRATION_MONTH; } - return ERROR_TYPE_NOT_ENOUGH_INFO; + return ErrorType.NOT_ENOUGH_INFO; } int year = getFourDigitYear(); @@ -609,16 +603,16 @@ if (mYearInput.getText().length() == EXPIRATION_FIELDS_LENGTH || (!mYearInput.isFocused() && mDidFocusOnYear)) { // mFinishedTypingYear = true; - return ERROR_TYPE_EXPIRATION_YEAR; + return ErrorType.EXPIRATION_YEAR; } - return ERROR_TYPE_NOT_ENOUGH_INFO; + return ErrorType.NOT_ENOUGH_INFO; } if (year == mThisYear && month < mThisMonth) { - return ERROR_TYPE_EXPIRATION_DATE; + return ErrorType.EXPIRATION_DATE; } - return ERROR_TYPE_NONE; + return ErrorType.NONE; } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java b/chrome/android/java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java new file mode 100644 index 0000000..1a69649f --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java
@@ -0,0 +1,109 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.component_updater; + +import android.os.Build; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; + +import org.chromium.base.ContextUtils; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback; +import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory; +import org.chromium.components.background_task_scheduler.TaskIds; +import org.chromium.components.background_task_scheduler.TaskInfo; + +/** Java-side implementation of the component update scheduler using the BackgroundTaskScheduler. */ +@JNINamespace("component_updater") +public class UpdateScheduler { + private static UpdateScheduler sInstance; + private TaskFinishedCallback mTaskFinishedCallback; + private long mNativeScheduler; + private long mDelayMs; + + @CalledByNative + /* package */ static UpdateScheduler getInstance() { + if (sInstance == null) { + sInstance = new UpdateScheduler(); + } + return sInstance; + } + + @CalledByNative + /* package */ static boolean isAvailable() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + || GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable( + ContextUtils.getApplicationContext()) + == ConnectionResult.SUCCESS; + } + + /* package */ void onStartTaskBeforeNativeLoaded(TaskFinishedCallback callback) { + mTaskFinishedCallback = callback; + } + + /* package */ void onStartTaskWithNative() { + assert mNativeScheduler != 0; + nativeOnStartTask(mNativeScheduler); + } + + /* package */ void onStopTask() { + if (mNativeScheduler != 0) { + nativeOnStopTask(mNativeScheduler); + } + mTaskFinishedCallback = null; + scheduleInternal(mDelayMs); + } + + /* package */ void reschedule() { + scheduleInternal(mDelayMs); + } + + private UpdateScheduler() {} + + private void scheduleInternal(long delayMs) { + // Skip re-scheduling if we are currently running the update task. Otherwise, the current + // update tasks would be cancelled. + if (mTaskFinishedCallback != null) return; + + TaskInfo taskInfo = TaskInfo.createOneOffTask(TaskIds.COMPONENT_UPDATE_JOB_ID, + UpdateTask.class, delayMs, Integer.MAX_VALUE) + .setUpdateCurrent(true) + .setRequiredNetworkType(TaskInfo.NETWORK_TYPE_UNMETERED) + .setIsPersisted(true) + .build(); + BackgroundTaskSchedulerFactory.getScheduler().schedule( + ContextUtils.getApplicationContext(), taskInfo); + } + + @CalledByNative + private void schedule(long initialDelayMs, long delayMs) { + mDelayMs = delayMs; + scheduleInternal(initialDelayMs); + } + + @CalledByNative + private void finishTask() { + assert mTaskFinishedCallback != null; + mTaskFinishedCallback.taskFinished(false); + mTaskFinishedCallback = null; + scheduleInternal(mDelayMs); + } + + @CalledByNative + private void setNativeScheduler(long nativeScheduler) { + mNativeScheduler = nativeScheduler; + } + + @CalledByNative + private void cancelTask() { + BackgroundTaskSchedulerFactory.getScheduler().cancel( + ContextUtils.getApplicationContext(), TaskIds.COMPONENT_UPDATE_JOB_ID); + } + + private native void nativeOnStartTask(long nativeBackgroundTaskUpdateScheduler); + private native void nativeOnStopTask(long nativeBackgroundTaskUpdateScheduler); +} \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/component_updater/UpdateTask.java b/chrome/android/java/src/org/chromium/chrome/browser/component_updater/UpdateTask.java new file mode 100644 index 0000000..9a60f4f --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/component_updater/UpdateTask.java
@@ -0,0 +1,54 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.component_updater; + +import android.content.Context; + +import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask; +import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback; +import org.chromium.components.background_task_scheduler.TaskIds; +import org.chromium.components.background_task_scheduler.TaskParameters; + +/** Task for initiating a component update. */ +public class UpdateTask extends NativeBackgroundTask { + @Override + @StartBeforeNativeResult + protected int onStartTaskBeforeNativeLoaded( + Context context, TaskParameters taskParameters, TaskFinishedCallback callback) { + assert taskParameters.getTaskId() == TaskIds.COMPONENT_UPDATE_JOB_ID; + UpdateScheduler.getInstance().onStartTaskBeforeNativeLoaded(callback); + return LOAD_NATIVE; + } + + @Override + protected void onStartTaskWithNative( + Context context, TaskParameters taskParameters, TaskFinishedCallback callback) { + assert taskParameters.getTaskId() == TaskIds.COMPONENT_UPDATE_JOB_ID; + UpdateScheduler.getInstance().onStartTaskWithNative(); + } + + @Override + protected boolean onStopTaskBeforeNativeLoaded(Context context, TaskParameters taskParameters) { + assert taskParameters.getTaskId() == TaskIds.COMPONENT_UPDATE_JOB_ID; + UpdateScheduler.getInstance().onStopTask(); + + // Don't reschedule task here. We are rescheduling with our parameters. + return false; + } + + @Override + protected boolean onStopTaskWithNative(Context context, TaskParameters taskParameters) { + assert taskParameters.getTaskId() == TaskIds.COMPONENT_UPDATE_JOB_ID; + UpdateScheduler.getInstance().onStopTask(); + + // Don't reschedule task here. We are rescheduling with our parameters. + return false; + } + + @Override + public void reschedule(Context context) { + UpdateScheduler.getInstance().reschedule(); + } +} \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java index 832d1f7..3900b86 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java
@@ -60,14 +60,16 @@ private static final int FAILURE = 0; private static final int SUCCESS = 1; - @StringDef({BROWSER, RENDERER, GPU, OTHER}) - public @interface ProcessType {} - static final String BROWSER = "Browser"; - static final String RENDERER = "Renderer"; - static final String GPU = "GPU"; - static final String OTHER = "Other"; + @StringDef({ProcessType.BROWSER, ProcessType.RENDERER, ProcessType.GPU, ProcessType.OTHER}) + public @interface ProcessType { + String BROWSER = "Browser"; + String RENDERER = "Renderer"; + String GPU = "GPU"; + String OTHER = "Other"; + } - static final String[] TYPES = {BROWSER, RENDERER, GPU, OTHER}; + static final String[] TYPES = { + ProcessType.BROWSER, ProcessType.RENDERER, ProcessType.GPU, ProcessType.OTHER}; public MinidumpUploadService() { super(TAG); @@ -217,23 +219,11 @@ // Crash type is on the line after the next line. fileReader.readLine(); String crashType = fileReader.readLine(); - if (crashType == null) { - return OTHER; - } - - if (crashType.equals("browser")) { - return BROWSER; - } - - if (crashType.equals("renderer")) { - return RENDERER; - } - - if (crashType.equals("gpu-process")) { - return GPU; - } - - return OTHER; + if (crashType == null) return ProcessType.OTHER; + if (crashType.equals("browser")) return ProcessType.BROWSER; + if (crashType.equals("renderer")) return ProcessType.RENDERER; + if (crashType.equals("gpu-process")) return ProcessType.GPU; + return ProcessType.OTHER; } } } catch (IOException e) { @@ -241,7 +231,7 @@ } finally { StreamUtil.closeQuietly(fileReader); } - return OTHER; + return ProcessType.OTHER; } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java index 3dccd16..e1cf797 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -379,6 +379,10 @@ } public void setBottomBarContentView(View view) { + // This method is currently only used by dynamic modules, and all its known uses require + // the shadow to be hidden. If this requirement ever changes, we could introduce an explicit + // API for that. + mBottomBarDelegate.setShowShadow(false); mBottomBarDelegate.setBottomBarContentView(view); mBottomBarDelegate.showBottomBarIfNecessary(); } @@ -391,6 +395,10 @@ addContentView(view, layoutParams); } + public void setBottomBarHeight(int height) { + mBottomBarDelegate.setBottomBarHeight(height); + } + @Override public boolean shouldAllocateChildConnection() { return !mHasCreatedTabEarly && !mHasSpeculated
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java index 1c53fb6..7a31709 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
@@ -52,6 +52,13 @@ private CustomTabIntentDataProvider mDataProvider; private PendingIntent mClickPendingIntent; private int[] mClickableIDs; + private boolean mShowShadow = true; + + /** + * The override height in pixels. A value of -1 is interpreted as "not set" and means it should + * not be used. + */ + private int mBottomBarHeightOverride = -1; private OnClickListener mBottomBarClickListener = new OnClickListener() { @Override @@ -77,6 +84,10 @@ public void showBottomBarIfNecessary() { if (!shouldShowBottomBar()) return; + getBottomBarView() + .findViewById(R.id.bottombar_shadow) + .setVisibility(mShowShadow ? View.VISIBLE : View.GONE); + if (mBottomBarContentView != null) { getBottomBarView().addView(mBottomBarContentView); mBottomBarContentView.addOnLayoutChangeListener(new OnLayoutChangeListener() { @@ -84,7 +95,7 @@ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { mBottomBarContentView.removeOnLayoutChangeListener(this); - mFullscreenManager.setBottomControlsHeight(v.getHeight()); + mFullscreenManager.setBottomControlsHeight(getBottomBarHeight()); } }); return; @@ -161,6 +172,13 @@ } /** + * Sets the visibility of the bottom bar shadow. + */ + public void setShowShadow(boolean show) { + mShowShadow = show; + } + + /** * @return The height of the bottom bar, excluding its top shadow. */ public int getBottomBarHeight() { @@ -168,10 +186,22 @@ || mBottomBarView.getChildCount() < 2) { return 0; } + if (mBottomBarHeightOverride != -1) return mBottomBarHeightOverride; return mBottomBarView.getChildAt(1).getHeight(); } /** + * Sets a height override for the bottom bar. If this value is not set, the height of the + * content is used instead. + * + * @param height The override height in pixels. A value of -1 is interpreted as "not set" and + * means it will not be used. + */ + public void setBottomBarHeight(int height) { + mBottomBarHeightOverride = height; + } + + /** * Gets the {@link ViewGroup} of the bottom bar. If it has not been inflated, inflate it first. */ private ViewGroup getBottomBarView() { @@ -247,7 +277,7 @@ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { inflatedView.removeOnLayoutChangeListener(this); - mFullscreenManager.setBottomControlsHeight(v.getHeight()); + mFullscreenManager.setBottomControlsHeight(getBottomBarHeight()); } }); return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityHostImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityHostImpl.java index e935970..26f2c9f6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityHostImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityHostImpl.java
@@ -32,4 +32,9 @@ public void setOverlayView(IObjectWrapper overlayView) { mActivity.setOverlayView(ObjectWrapper.unwrap(overlayView, View.class)); } + + @Override + public void setBottomBarHeight(int height) { + mActivity.setBottomBarHeight(height); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/IActivityHost.aidl b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/IActivityHost.aidl index abd54d8..17a8f35 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/IActivityHost.aidl +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/IActivityHost.aidl
@@ -12,4 +12,6 @@ void setBottomBarView(in IObjectWrapper /* View */ bottomBarView) = 1; void setOverlayView(in IObjectWrapper /* View */ overlayView) = 2; + + void setBottomBarHeight(int height) = 3; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DirectoryOption.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DirectoryOption.java index d5a821c..08183dc 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DirectoryOption.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DirectoryOption.java
@@ -17,14 +17,16 @@ public class DirectoryOption { // Type to track user's selection of directory option. This enum is used in histogram and must // match DownloadLocationDirectoryType in enums.xml, so don't delete or reuse values. + @IntDef({DownloadLocationDirectoryType.DEFAULT, DownloadLocationDirectoryType.ADDITIONAL, + DownloadLocationDirectoryType.ERROR}) @Retention(RetentionPolicy.SOURCE) - @IntDef({DEFAULT_OPTION, ADDITIONAL_OPTION, ERROR_OPTION, OPTION_COUNT}) - public @interface DownloadLocationDirectoryType {} + public @interface DownloadLocationDirectoryType { + int DEFAULT = 0; + int ADDITIONAL = 1; + int ERROR = 2; - public static final int DEFAULT_OPTION = 0; - public static final int ADDITIONAL_OPTION = 1; - public static final int ERROR_OPTION = 2; - public static final int OPTION_COUNT = 3; + int NUM_ENTRIES = 3; + } /** * Name of the current download directory. @@ -77,6 +79,6 @@ */ public void recordDirectoryOptionType() { RecordHistogram.recordEnumeratedHistogram("MobileDownload.Location.Setting.DirectoryType", - type, DirectoryOption.OPTION_COUNT); + type, DirectoryOption.DownloadLocationDirectoryType.NUM_ENTRIES); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadDirectoryProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadDirectoryProvider.java index 7031905..4e4660f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadDirectoryProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadDirectoryProvider.java
@@ -52,12 +52,13 @@ // If no default directory, return an error option. if (defaultDirectory == null) { - dirs.add(new DirectoryOption(null, 0, 0, DirectoryOption.ERROR_OPTION)); + dirs.add(new DirectoryOption( + null, 0, 0, DirectoryOption.DownloadLocationDirectoryType.ERROR)); return dirs; } - DirectoryOption defaultOption = - toDirectoryOption(defaultDirectory, DirectoryOption.DEFAULT_OPTION); + DirectoryOption defaultOption = toDirectoryOption( + defaultDirectory, DirectoryOption.DownloadLocationDirectoryType.DEFAULT); dirs.add(defaultOption); // Retrieve additional directories, i.e. the external SD card directory. @@ -78,7 +79,8 @@ // Skip primary storage directory. if (files[i].getAbsolutePath().contains(mExternalStorageDirectory)) continue; - dirs.add(toDirectoryOption(files[i], DirectoryOption.ADDITIONAL_OPTION)); + dirs.add(toDirectoryOption( + files[i], DirectoryOption.DownloadLocationDirectoryType.ADDITIONAL)); } return dirs; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/StorageSummary.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/StorageSummary.java index 7e0900a7..513023a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/StorageSummary.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/StorageSummary.java
@@ -33,9 +33,10 @@ protected DirectoryOption doInBackground(Void... params) { File defaultDownloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); - DirectoryOption directoryOption = new DirectoryOption("", - defaultDownloadDir.getAbsolutePath(), defaultDownloadDir.getUsableSpace(), - defaultDownloadDir.getTotalSpace(), DirectoryOption.DEFAULT_OPTION); + DirectoryOption directoryOption = + new DirectoryOption("", defaultDownloadDir.getAbsolutePath(), + defaultDownloadDir.getUsableSpace(), defaultDownloadDir.getTotalSpace(), + DirectoryOption.DownloadLocationDirectoryType.DEFAULT); return directoryOption; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/download/DownloadDirectoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/download/DownloadDirectoryAdapter.java index f29bd57..e029caff 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/download/DownloadDirectoryAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/download/DownloadDirectoryAdapter.java
@@ -81,11 +81,9 @@ return mErrorOptions.get(position); } - if (position < mCanonicalOptions.size()) { - return mCanonicalOptions.get(position); - } else { - return mAdditionalOptions.get(position - mCanonicalOptions.size()); - } + return position < mCanonicalOptions.size() + ? mCanonicalOptions.get(position) + : mAdditionalOptions.get(position - mCanonicalOptions.size()); } @Override @@ -96,10 +94,9 @@ @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - View view = convertView; - if (view == null) { - view = mLayoutInflater.inflate(R.layout.download_location_spinner_item, null); - } + View view = convertView != null + ? convertView + : mLayoutInflater.inflate(R.layout.download_location_spinner_item, null); view.setTag(position); @@ -121,10 +118,9 @@ @Override public View getDropDownView( int position, @Nullable View convertView, @NonNull ViewGroup parent) { - View view = convertView; - if (view == null) { - view = mLayoutInflater.inflate(R.layout.download_location_spinner_dropdown_item, null); - } + View view = convertView != null + ? convertView + : mLayoutInflater.inflate(R.layout.download_location_spinner_dropdown_item, null); view.setTag(position); @@ -232,11 +228,11 @@ for (DirectoryOption dir : dirs) { DirectoryOption directory = (DirectoryOption) dir.clone(); switch (directory.type) { - case DirectoryOption.DEFAULT_OPTION: + case DirectoryOption.DownloadLocationDirectoryType.DEFAULT: directory.name = mContext.getString(R.string.menu_downloads); mCanonicalOptions.add(directory); break; - case DirectoryOption.ADDITIONAL_OPTION: + case DirectoryOption.DownloadLocationDirectoryType.ADDITIONAL: String directoryName = (numOtherAdditionalDirectories > 0) ? mContext.getString(org.chromium.chrome.R.string .downloads_location_sd_card_number, @@ -247,7 +243,7 @@ mAdditionalOptions.add(directory); numOtherAdditionalDirectories++; break; - case DirectoryOption.ERROR_OPTION: + case DirectoryOption.DownloadLocationDirectoryType.ERROR: directory.name = mContext.getString(R.string.download_location_no_available_locations); mErrorOptions.add(directory); @@ -273,7 +269,7 @@ } else { mErrorOptions.add(new DirectoryOption( mContext.getString(R.string.download_location_no_available_locations), null, 0, - 0, DirectoryOption.ERROR_OPTION)); + 0, DirectoryOption.DownloadLocationDirectoryType.ERROR)); } } }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index 55ec569f..eb2458c 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -170,6 +170,8 @@ "java/src/org/chromium/chrome/browser/browsing_data/UrlFilters.java", "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java", "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java", + "java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java", + "java/src/org/chromium/chrome/browser/component_updater/UpdateTask.java", "java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java", "java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerImpl.java", "java/src/org/chromium/chrome/browser/compositor/CompositorView.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceTest.java index 5048b43..1830419f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceTest.java
@@ -4,11 +4,6 @@ package org.chromium.chrome.browser.crash; -import static org.chromium.chrome.browser.crash.MinidumpUploadService.BROWSER; -import static org.chromium.chrome.browser.crash.MinidumpUploadService.GPU; -import static org.chromium.chrome.browser.crash.MinidumpUploadService.OTHER; -import static org.chromium.chrome.browser.crash.MinidumpUploadService.RENDERER; - import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.job.JobInfo; @@ -585,8 +580,8 @@ final File minidumpFile = new File(mTestRule.getCrashDir(), "chromium_renderer-123.dmp.try0"); CrashTestRule.setUpMinidumpFile(minidumpFile, BOUNDARY, "browser"); - Assert.assertEquals( - BROWSER, MinidumpUploadService.getCrashType(minidumpFile.getAbsolutePath())); + Assert.assertEquals(MinidumpUploadService.ProcessType.BROWSER, + MinidumpUploadService.getCrashType(minidumpFile.getAbsolutePath())); } @Test @@ -596,8 +591,8 @@ final File minidumpFile = new File(mTestRule.getCrashDir(), "chromium_renderer-123.dmp.try0"); CrashTestRule.setUpMinidumpFile(minidumpFile, BOUNDARY, "renderer"); - Assert.assertEquals( - RENDERER, MinidumpUploadService.getCrashType(minidumpFile.getAbsolutePath())); + Assert.assertEquals(MinidumpUploadService.ProcessType.RENDERER, + MinidumpUploadService.getCrashType(minidumpFile.getAbsolutePath())); } @Test @@ -607,8 +602,8 @@ final File minidumpFile = new File(mTestRule.getCrashDir(), "chromium_renderer-123.dmp.try0"); CrashTestRule.setUpMinidumpFile(minidumpFile, BOUNDARY, "gpu-process"); - Assert.assertEquals( - GPU, MinidumpUploadService.getCrashType(minidumpFile.getAbsolutePath())); + Assert.assertEquals(MinidumpUploadService.ProcessType.GPU, + MinidumpUploadService.getCrashType(minidumpFile.getAbsolutePath())); } @Test @@ -618,8 +613,8 @@ final File minidumpFile = new File(mTestRule.getCrashDir(), "chromium_renderer-123.dmp.try0"); CrashTestRule.setUpMinidumpFile(minidumpFile, BOUNDARY, "weird test type"); - Assert.assertEquals( - OTHER, MinidumpUploadService.getCrashType(minidumpFile.getAbsolutePath())); + Assert.assertEquals(MinidumpUploadService.ProcessType.OTHER, + MinidumpUploadService.getCrashType(minidumpFile.getAbsolutePath())); } private class MinidumpPreparationContext extends AdvancedMockContext {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java index b4c593d..6183b6c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java
@@ -158,7 +158,7 @@ // First seed the account state (some native classes make the assumption that // a notification that a token was revoked for a given account was preceded by a - // notifictation that that account was available). + // notification that that account was available). mOAuth2TokenService.fireRefreshTokenAvailable(TEST_ACCOUNT1); mOAuth2TokenService.fireRefreshTokenAvailable(TEST_ACCOUNT2);
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt index a9c558e..7733693 100644 --- a/chrome/android/profiles/newest.txt +++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-69.0.3480.0_rc-r1.afdo.bz2 \ No newline at end of file +chromeos-chrome-amd64-69.0.3481.0_rc-r1.afdo.bz2 \ No newline at end of file
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index 3ac2ef1..3cdaf48 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -256,6 +256,9 @@ <message name="IDS_FILE_BROWSER_LINUX_FILES_ROOT_LABEL" desc="A label for the 'Linux Files' root which shows crostini files."> Linux Files </message> + <message name="IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL" desc="A label for the 'My Files' root which is parent of Downloads, Linux and Android files."> + My Files + </message> <message name="IDS_FILE_BROWSER_MEDIA_VIEW_IMAGES_ROOT_LABEL" desc="A label for the 'Images' root of media views."> Images </message> @@ -5025,6 +5028,9 @@ <message name="IDS_CROSTINI_UNINSTALLER_ERROR" desc="Text shown in the Crostini uninstaller dialog when the Linux uninstall process fails."> Error uninstalling Linux. Please try again. </message> + <message name="IDS_CROSTINI_SHUT_DOWN_LINUX_MENU_ITEM" desc="Text shown in the context menu for the Linux terminal app, allowing users to shut down the Linux virtual machine."> + Shut Down Linux + </message> <!-- Time limit notification --> <message name="IDS_SCREEN_TIME_NOTIFICATION_TITLE" desc="The title of the notification when screen usage limit reaches before locking the device.">
diff --git a/chrome/app/chromeos_strings_grdp/IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL.png.sha1 new file mode 100644 index 0000000..a089841 --- /dev/null +++ b/chrome/app/chromeos_strings_grdp/IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL.png.sha1
@@ -0,0 +1 @@ +cef0df144e133cdff7b8997a6b0ecee405e8ec53 \ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index e56441a..57654fac 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -3184,6 +3184,11 @@ <message name="IDS_UTILITY_PROCESS_FILE_UTILITY_NAME" desc="The name of the utility process used for various Chrome specific file operations."> Chrome File Utilities </message> + <if expr="is_linux"> + <message name="IDS_UTILITY_PROCESS_FONT_SERVICE_UTILITY_NAME" desc="The name of the utility process used for FontConfig operations for Chrome on Linux."> + Linux Font Service + </message> + </if> <if expr="not is_android"> <message name="IDS_UTILITY_PROCESS_PROFILE_IMPORTER_NAME" desc="The name of the utility process used for importing profiles."> Profile Importer
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 7f157f0..57025ed 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1954,6 +1954,8 @@ "android/chrome_startup_flags.h", "android/color_helpers.cc", "android/color_helpers.h", + "android/component_updater/background_task_update_scheduler.cc", + "android/component_updater/background_task_update_scheduler.h", "android/compositor/compositor_view.cc", "android/compositor/compositor_view.h", "android/compositor/decoration_title.cc", @@ -4375,6 +4377,7 @@ "../android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilterBridge.java", "../android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java", "../android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java", + "../android/java/src/org/chromium/chrome/browser/component_updater/UpdateScheduler.java", "../android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java", "../android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java", "../android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index f114be4..9fd4eae 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -3374,12 +3374,6 @@ FEATURE_VALUE_TYPE(chrome::android::kPwaPersistentNotification)}, #endif // OS_ANDROID - {"enable-manual-fallbacks-filling", - flag_descriptions::kEnableManualFallbacksFillingName, - flag_descriptions::kEnableManualFallbacksFillingDescription, - kOsDesktop | kOsAndroid, - FEATURE_VALUE_TYPE(password_manager::features::kManualFallbacksFilling)}, - #if !defined(OS_ANDROID) {"voice-search-on-local-ntp", flag_descriptions::kVoiceSearchOnLocalNtpName, flag_descriptions::kVoiceSearchOnLocalNtpDescription, kOsDesktop, @@ -4051,6 +4045,13 @@ FEATURE_VALUE_TYPE(chromeos::features::kDriveFs)}, #endif // OS_CHROMEOS +#if defined(OS_ANDROID) + {"background-task-component-update", + flag_descriptions::kBackgroundTaskComponentUpdateName, + flag_descriptions::kBackgroundTaskComponentUpdateDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kBackgroundTaskComponentUpdate)}, +#endif + // NOTE: Adding a new flag requires adding a corresponding entry to enum // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index d007da9..96a525a 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -188,6 +188,9 @@ const base::Feature kAndroidPaymentApps{"AndroidPaymentApps", base::FEATURE_ENABLED_BY_DEFAULT}; +const base::Feature kBackgroundTaskComponentUpdate{ + "BackgroundTaskComponentUpdate", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kCCTBackgroundTab{"CCTBackgroundTab", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h index d7bc6bad..7d4ee14a 100644 --- a/chrome/browser/android/chrome_feature_list.h +++ b/chrome/browser/android/chrome_feature_list.h
@@ -17,6 +17,7 @@ extern const base::Feature kAndroidPayIntegrationV1; extern const base::Feature kAndroidPayIntegrationV2; extern const base::Feature kAndroidPaymentApps; +extern const base::Feature kBackgroundTaskComponentUpdate; extern const base::Feature kCCTBackgroundTab; extern const base::Feature kCCTExternalLinkHandling; extern const base::Feature kCCTModule;
diff --git a/chrome/browser/android/component_updater/background_task_update_scheduler.cc b/chrome/browser/android/component_updater/background_task_update_scheduler.cc new file mode 100644 index 0000000..5c0c5a0 --- /dev/null +++ b/chrome/browser/android/component_updater/background_task_update_scheduler.cc
@@ -0,0 +1,57 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/android/component_updater/background_task_update_scheduler.h" + +#include "jni/UpdateScheduler_jni.h" + +namespace component_updater { + +// static +bool BackgroundTaskUpdateScheduler::IsAvailable() { + return Java_UpdateScheduler_isAvailable(base::android::AttachCurrentThread()); +} + +BackgroundTaskUpdateScheduler::BackgroundTaskUpdateScheduler() { + DCHECK(IsAvailable()); + JNIEnv* env = base::android::AttachCurrentThread(); + j_update_scheduler_.Reset(Java_UpdateScheduler_getInstance(env)); + Java_UpdateScheduler_setNativeScheduler(env, j_update_scheduler_, + reinterpret_cast<intptr_t>(this)); +} + +BackgroundTaskUpdateScheduler::~BackgroundTaskUpdateScheduler() = default; + +void BackgroundTaskUpdateScheduler::Schedule( + const base::TimeDelta& initial_delay, + const base::TimeDelta& delay, + const UserTask& user_task, + const OnStopTaskCallback& on_stop) { + user_task_ = user_task; + on_stop_ = on_stop; + Java_UpdateScheduler_schedule( + base::android::AttachCurrentThread(), j_update_scheduler_, + initial_delay.InMilliseconds(), delay.InMilliseconds()); +} + +void BackgroundTaskUpdateScheduler::Stop() { + Java_UpdateScheduler_cancelTask(base::android::AttachCurrentThread(), + j_update_scheduler_); +} + +void BackgroundTaskUpdateScheduler::OnStartTask( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj) { + user_task_.Run(base::BindOnce(&Java_UpdateScheduler_finishTask, + base::Unretained(env), j_update_scheduler_)); +} + +void BackgroundTaskUpdateScheduler::OnStopTask( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj) { + DCHECK(on_stop_); + on_stop_.Run(); +} + +} // namespace component_updater
diff --git a/chrome/browser/android/component_updater/background_task_update_scheduler.h b/chrome/browser/android/component_updater/background_task_update_scheduler.h new file mode 100644 index 0000000..2a1b647 --- /dev/null +++ b/chrome/browser/android/component_updater/background_task_update_scheduler.h
@@ -0,0 +1,49 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ANDROID_COMPONENT_UPDATER_BACKGROUND_TASK_UPDATE_SCHEDULER_H_ +#define CHROME_BROWSER_ANDROID_COMPONENT_UPDATER_BACKGROUND_TASK_UPDATE_SCHEDULER_H_ + +#include <jni.h> + +#include "base/android/scoped_java_ref.h" +#include "base/callback.h" +#include "base/macros.h" +#include "components/component_updater/update_scheduler.h" + +namespace component_updater { + +// Native-side implementation of the component update scheduler using the +// BackgroundTaskScheduler. +class BackgroundTaskUpdateScheduler : public UpdateScheduler { + public: + // Returns true if this scheduler can be used. + static bool IsAvailable(); + + BackgroundTaskUpdateScheduler(); + ~BackgroundTaskUpdateScheduler() override; + + // UpdateScheduler: + void Schedule(const base::TimeDelta& initial_delay, + const base::TimeDelta& delay, + const UserTask& user_task, + const OnStopTaskCallback& on_stop) override; + void Stop() override; + + // JNI: + void OnStartTask(JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj); + void OnStopTask(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); + + private: + base::android::ScopedJavaGlobalRef<jobject> j_update_scheduler_; + UserTask user_task_; + OnStopTaskCallback on_stop_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundTaskUpdateScheduler); +}; + +} // namespace component_updater + +#endif // CHROME_BROWSER_ANDROID_COMPONENT_UPDATER_BACKGROUND_TASK_UPDATE_SCHEDULER_H_
diff --git a/chrome/browser/android/vr/android_ui_gesture_target.cc b/chrome/browser/android/vr/android_ui_gesture_target.cc index fef7600..16c92c1 100644 --- a/chrome/browser/android/vr/android_ui_gesture_target.cc +++ b/chrome/browser/android/vr/android_ui_gesture_target.cc
@@ -6,11 +6,8 @@ #include <cmath> +#include "chrome/browser/vr/input_event.h" #include "jni/AndroidUiGestureTarget_jni.h" -#include "third_party/blink/public/platform/web_gesture_event.h" -#include "third_party/blink/public/platform/web_input_event.h" -#include "third_party/blink/public/platform/web_mouse_event.h" -#include "third_party/blink/public/platform/web_touch_event.h" using base::android::JavaParamRef; using base::android::JavaRef; @@ -33,30 +30,16 @@ AndroidUiGestureTarget::~AndroidUiGestureTarget() = default; -void AndroidUiGestureTarget::DispatchWebInputEvent( - std::unique_ptr<blink::WebInputEvent> event) { - blink::WebMouseEvent* mouse; - blink::WebTouchEvent* touch; - blink::WebGestureEvent* gesture; - if (blink::WebInputEvent::IsMouseEventType(event->GetType())) { - mouse = static_cast<blink::WebMouseEvent*>(event.get()); - } else if (blink::WebInputEvent::IsTouchEventType(event->GetType())) { - touch = static_cast<blink::WebTouchEvent*>(event.get()); - } else { - gesture = static_cast<blink::WebGestureEvent*>(event.get()); - } - - int64_t event_time_ms = event->TimeStamp().since_origin().InMilliseconds(); - switch (event->GetType()) { - case blink::WebGestureEvent::kGestureScrollBegin: { - DCHECK(gesture->data.scroll_begin.delta_hint_units == - blink::WebGestureEvent::ScrollUnits::kPrecisePixels); - - SetPointer(gesture->PositionInWidget().x, gesture->PositionInWidget().y); +void AndroidUiGestureTarget::DispatchInputEvent( + std::unique_ptr<InputEvent> event) { + int64_t event_time_ms = event->time_stamp().since_origin().InMilliseconds(); + switch (event->type()) { + case InputEvent::kScrollBegin: { + SetPointer(event->position_in_widget()); Inject(content::MOTION_EVENT_ACTION_START, event_time_ms); - float xdiff = gesture->data.scroll_begin.delta_x_hint; - float ydiff = gesture->data.scroll_begin.delta_y_hint; + float xdiff = event->scroll_data.delta_x; + float ydiff = event->scroll_data.delta_y; if (xdiff == 0 && ydiff == 0) ydiff = touch_slop_; @@ -66,8 +49,8 @@ ydiff *= touch_slop_ / dist; } - float xtarget = xdiff * scroll_ratio_ + gesture->PositionInWidget().x; - float ytarget = ydiff * scroll_ratio_ + gesture->PositionInWidget().y; + float xtarget = xdiff * scroll_ratio_ + event->position_in_widget().x(); + float ytarget = ydiff * scroll_ratio_ + event->position_in_widget().y(); scroll_x_ = xtarget > 0 ? std::ceil(xtarget) : std::floor(xtarget); scroll_y_ = ytarget > 0 ? std::ceil(ytarget) : std::floor(ytarget); @@ -76,61 +59,50 @@ Inject(content::MOTION_EVENT_ACTION_MOVE, event_time_ms); break; } - case blink::WebGestureEvent::kGestureScrollEnd: + case InputEvent::kScrollEnd: SetPointer(scroll_x_, scroll_y_); Inject(content::MOTION_EVENT_ACTION_END, event_time_ms); break; - case blink::WebGestureEvent::kGestureScrollUpdate: { + case InputEvent::kScrollUpdate: { float scale = scroll_ratio_ / kScrollEventsPerFrame; - scroll_x_ += gesture->data.scroll_update.delta_x * scale; - scroll_y_ += gesture->data.scroll_update.delta_y * scale; + scroll_x_ += event->scroll_data.delta_x * scale; + scroll_y_ += event->scroll_data.delta_y * scale; SetPointer(scroll_x_, scroll_y_); Inject(content::MOTION_EVENT_ACTION_MOVE, event_time_ms); - scroll_x_ += gesture->data.scroll_update.delta_x * scale; - scroll_y_ += gesture->data.scroll_update.delta_y * scale; + scroll_x_ += event->scroll_data.delta_x * scale; + scroll_y_ += event->scroll_data.delta_y * scale; SetDelayedEvent(scroll_x_, scroll_y_, content::MOTION_EVENT_ACTION_MOVE, event_time_ms, kFrameDurationMs / kScrollEventsPerFrame); break; } - case blink::WebGestureEvent::kGestureTapDown: - SetPointer(gesture->PositionInWidget().x, gesture->PositionInWidget().y); - Inject(content::MOTION_EVENT_ACTION_START, event_time_ms); - Inject(content::MOTION_EVENT_ACTION_END, event_time_ms); - break; - case blink::WebGestureEvent::kGestureFlingCancel: + case InputEvent::kFlingCancel: Inject(content::MOTION_EVENT_ACTION_START, event_time_ms); Inject(content::MOTION_EVENT_ACTION_CANCEL, event_time_ms); break; - case blink::WebMouseEvent::kMouseEnter: - SetPointer(mouse->PositionInWidget().x, mouse->PositionInWidget().y); + case InputEvent::kHoverEnter: + SetPointer(event->position_in_widget()); Inject(content::MOTION_EVENT_ACTION_HOVER_ENTER, event_time_ms); break; - case blink::WebMouseEvent::kMouseMove: - case blink::WebMouseEvent::kMouseLeave: + case InputEvent::kHoverLeave: + case InputEvent::kHoverMove: // The platform ignores HOVER_EXIT, so we instead send a fixed // out-of-bounds point (http://crbug.com/715114). - SetPointer(mouse->PositionInWidget().x, mouse->PositionInWidget().y); + SetPointer(event->position_in_widget()); Inject(content::MOTION_EVENT_ACTION_HOVER_MOVE, event_time_ms); break; - case blink::WebTouchEvent::kTouchStart: - // Mouse down events are translated into touch events on Android anyways, - // so we can just send touch events. - SetPointer(touch->touches[0].PositionInWidget().x, - touch->touches[0].PositionInWidget().y); + case InputEvent::kButtonDown: + SetPointer(event->position_in_widget()); Inject(content::MOTION_EVENT_ACTION_START, event_time_ms); break; - case blink::WebTouchEvent::kTouchEnd: - SetPointer(touch->touches[0].PositionInWidget().x, - touch->touches[0].PositionInWidget().y); + case InputEvent::kButtonUp: + SetPointer(event->position_in_widget()); Inject(content::MOTION_EVENT_ACTION_END, event_time_ms); break; - case blink::WebTouchEvent::kTouchMove: - DCHECK_EQ(touch->touches_length, 1u); - SetPointer(touch->touches[0].PositionInWidget().x, - touch->touches[0].PositionInWidget().y); + case InputEvent::kMove: + SetPointer(event->position_in_widget()); Inject(content::MOTION_EVENT_ACTION_MOVE, event_time_ms); break; default: @@ -148,18 +120,23 @@ Java_AndroidUiGestureTarget_inject(env, obj, action, time_ms); } -void AndroidUiGestureTarget::SetPointer(int x, int y) { +void AndroidUiGestureTarget::SetPointer(const gfx::PointF& position) { + SetPointer(position.x(), position.y()); +} + +void AndroidUiGestureTarget::SetPointer(float x, float y) { JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); if (obj.is_null()) return; - Java_AndroidUiGestureTarget_setPointer(env, obj, x * scale_factor_, - y * scale_factor_); + Java_AndroidUiGestureTarget_setPointer(env, obj, + static_cast<int>(x * scale_factor_), + static_cast<int>(y * scale_factor_)); } -void AndroidUiGestureTarget::SetDelayedEvent(int x, - int y, +void AndroidUiGestureTarget::SetDelayedEvent(float x, + float y, MotionEventAction action, int64_t time_ms, int delay_ms) { @@ -168,9 +145,9 @@ if (obj.is_null()) return; - Java_AndroidUiGestureTarget_setDelayedEvent(env, obj, x * scale_factor_, - y * scale_factor_, action, - time_ms, delay_ms); + Java_AndroidUiGestureTarget_setDelayedEvent( + env, obj, static_cast<int>(x * scale_factor_), + static_cast<int>(y * scale_factor_), action, time_ms, delay_ms); } // static
diff --git a/chrome/browser/android/vr/android_ui_gesture_target.h b/chrome/browser/android/vr/android_ui_gesture_target.h index 3021564..d1f1a3d 100644 --- a/chrome/browser/android/vr/android_ui_gesture_target.h +++ b/chrome/browser/android/vr/android_ui_gesture_target.h
@@ -10,12 +10,14 @@ #include "base/macros.h" #include "content/public/browser/android/motion_event_action.h" -namespace blink { -class WebInputEvent; +namespace gfx { +class PointF; } namespace vr { +class InputEvent; + // Used to forward events to MotionEventSynthesizer. Owned by VrShell. class AndroidUiGestureTarget { public: @@ -29,13 +31,14 @@ static AndroidUiGestureTarget* FromJavaObject( const base::android::JavaRef<jobject>& obj); - void DispatchWebInputEvent(std::unique_ptr<blink::WebInputEvent> event); + void DispatchInputEvent(std::unique_ptr<InputEvent> event); private: void Inject(content::MotionEventAction action, int64_t time_ms); - void SetPointer(int x, int y); - void SetDelayedEvent(int x, - int y, + void SetPointer(const gfx::PointF& position); + void SetPointer(float x, float y); + void SetDelayedEvent(float x, + float y, content::MotionEventAction action, int64_t time_ms, int delay_ms);
diff --git a/chrome/browser/android/vr/vr_controller.cc b/chrome/browser/android/vr/vr_controller.cc index 87e6ade3..247dfb2 100644 --- a/chrome/browser/android/vr/vr_controller.cc +++ b/chrome/browser/android/vr/vr_controller.cc
@@ -10,8 +10,7 @@ #include "base/logging.h" #include "base/numerics/math_constants.h" #include "base/numerics/ranges.h" -#include "third_party/blink/public/platform/web_gesture_event.h" -#include "third_party/blink/public/platform/web_input_event.h" +#include "chrome/browser/vr/input_event.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_controller.h" @@ -336,9 +335,9 @@ gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos; } -std::unique_ptr<GestureList> VrController::DetectGestures() { +std::unique_ptr<InputEventList> VrController::DetectGestures() { if (controller_state_->GetConnectionState() != gvr::kControllerConnected) { - return std::make_unique<GestureList>(); + return std::make_unique<InputEventList>(); } UpdateCurrentTouchInfo();
diff --git a/chrome/browser/android/vr/vr_controller.h b/chrome/browser/android/vr/vr_controller.h index 7ee3bd9..6146347b 100644 --- a/chrome/browser/android/vr/vr_controller.h +++ b/chrome/browser/android/vr/vr_controller.h
@@ -22,10 +22,6 @@ #include "ui/gfx/geometry/vector3d_f.h" #include "ui/gfx/transform.h" -namespace blink { -class WebGestureEvent; -} - namespace gfx { class Transform; } @@ -39,8 +35,6 @@ // Angle (radians) the beam down from the controller axis, for wrist comfort. constexpr float kErgoAngleOffset = 0.26f; -using GestureList = std::vector<std::unique_ptr<blink::WebGestureEvent>>; - class VrController : public PlatformController { public: // Controller API entry point. @@ -59,7 +53,7 @@ // Called once per frame to update controller state. void UpdateState(const gfx::Transform& head_pose); - std::unique_ptr<GestureList> DetectGestures(); + std::unique_ptr<InputEventList> DetectGestures(); bool IsTouching();
diff --git a/chrome/browser/android/vr/vr_gl_thread.cc b/chrome/browser/android/vr/vr_gl_thread.cc index 802ddec..d5a3c30 100644 --- a/chrome/browser/android/vr/vr_gl_thread.cc +++ b/chrome/browser/android/vr/vr_gl_thread.cc
@@ -159,9 +159,8 @@ base::BindOnce(&VrShell::UpdateGamepadData, weak_vr_shell_, pad)); } -void VrGLThread::ForwardEventToContent( - std::unique_ptr<blink::WebInputEvent> event, - int content_id) { +void VrGLThread::ForwardEventToContent(std::unique_ptr<InputEvent> event, + int content_id) { DCHECK(OnGlThread()); main_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(&VrShell::ProcessContentGesture, weak_vr_shell_, @@ -192,8 +191,7 @@ input_connection_->RequestTextState(std::move(callback)); } -void VrGLThread::ForwardEventToPlatformUi( - std::unique_ptr<blink::WebInputEvent> event) { +void VrGLThread::ForwardEventToPlatformUi(std::unique_ptr<InputEvent> event) { DCHECK(OnGlThread()); main_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(&VrShell::ProcessDialogGesture, weak_vr_shell_,
diff --git a/chrome/browser/android/vr/vr_gl_thread.h b/chrome/browser/android/vr/vr_gl_thread.h index 8aaa416..4dc2f7fc 100644 --- a/chrome/browser/android/vr/vr_gl_thread.h +++ b/chrome/browser/android/vr/vr_gl_thread.h
@@ -77,9 +77,8 @@ void ToggleCardboardGamepad(bool enabled) override; // PlatformInputHandler - void ForwardEventToPlatformUi( - std::unique_ptr<blink::WebInputEvent> event) override; - void ForwardEventToContent(std::unique_ptr<blink::WebInputEvent> event, + void ForwardEventToPlatformUi(std::unique_ptr<InputEvent> event) override; + void ForwardEventToContent(std::unique_ptr<InputEvent> event, int content_id) override; void ClearFocusedElement() override; void OnWebInputEdited(const TextEdits& edits) override;
diff --git a/chrome/browser/android/vr/vr_shell.cc b/chrome/browser/android/vr/vr_shell.cc index 694849cc..8ff6090 100644 --- a/chrome/browser/android/vr/vr_shell.cc +++ b/chrome/browser/android/vr/vr_shell.cc
@@ -69,7 +69,6 @@ #include "services/device/public/mojom/constants.mojom.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/service_manager/public/cpp/connector.h" -#include "third_party/blink/public/platform/web_input_event.h" #include "ui/android/window_android.h" #include "ui/base/page_transition_types.h" #include "ui/display/display.h" @@ -1112,7 +1111,7 @@ web_contents_->ClearFocusedElement(); } -void VrShell::ProcessContentGesture(std::unique_ptr<blink::WebInputEvent> event, +void VrShell::ProcessContentGesture(std::unique_ptr<InputEvent> event, int content_id) { // Block the events if they don't belong to the current content if (content_id_ != content_id) @@ -1121,15 +1120,14 @@ if (!android_ui_gesture_target_) return; - android_ui_gesture_target_->DispatchWebInputEvent(std::move(event)); + android_ui_gesture_target_->DispatchInputEvent(std::move(event)); } -void VrShell::ProcessDialogGesture( - std::unique_ptr<blink::WebInputEvent> event) { +void VrShell::ProcessDialogGesture(std::unique_ptr<InputEvent> event) { if (!dialog_gesture_target_) return; - dialog_gesture_target_->DispatchWebInputEvent(std::move(event)); + dialog_gesture_target_->DispatchInputEvent(std::move(event)); } void VrShell::UpdateGamepadData(device::GvrGamepadData pad) {
diff --git a/chrome/browser/android/vr/vr_shell.h b/chrome/browser/android/vr/vr_shell.h index 509b67fe..929274f 100644 --- a/chrome/browser/android/vr/vr_shell.h +++ b/chrome/browser/android/vr/vr_shell.h
@@ -38,10 +38,6 @@ class Version; } // namespace base -namespace blink { -class WebInputEvent; -} // namespace blink - namespace content { class WebContents; } // namespace content @@ -216,10 +212,9 @@ bool HasAudioPermission(); void ClearFocusedElement(); - void ProcessContentGesture(std::unique_ptr<blink::WebInputEvent> event, - int content_id); + void ProcessContentGesture(std::unique_ptr<InputEvent> event, int content_id); - void ProcessDialogGesture(std::unique_ptr<blink::WebInputEvent> event); + void ProcessDialogGesture(std::unique_ptr<InputEvent> event); void SetAlertDialog(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/android/vr/vr_shell_gl.cc b/chrome/browser/android/vr/vr_shell_gl.cc index 4d9c3d3..785d0bf 100644 --- a/chrome/browser/android/vr/vr_shell_gl.cc +++ b/chrome/browser/android/vr/vr_shell_gl.cc
@@ -49,7 +49,6 @@ #include "device/vr/android/gvr/gvr_gamepad_data_provider.h" #include "gpu/config/gpu_driver_bug_workaround_type.h" #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" -#include "third_party/blink/public/platform/web_gesture_event.h" #include "ui/gfx/geometry/angle_conversions.h" #include "ui/gfx/gpu_fence.h" #include "ui/gl/android/scoped_java_surface.h" @@ -1253,8 +1252,8 @@ ControllerModel controller_model; controller_->GetTransform(&controller_model.transform); - std::unique_ptr<GestureList> gesture_list_ptr = controller_->DetectGestures(); - GestureList& gesture_list = *gesture_list_ptr; + std::unique_ptr<InputEventList> input_event_list = + controller_->DetectGestures(); controller_model.touchpad_button_state = UiInputManager::ButtonState::UP; DCHECK(!(controller_->ButtonUpHappened(gvr::kControllerButtonClick) && controller_->ButtonDownHappened(gvr::kControllerButtonClick))) @@ -1304,7 +1303,7 @@ ReticleModel reticle_model; ui_->input_manager()->HandleInput(current_time, render_info, controller_model, - &reticle_model, &gesture_list); + &reticle_model, input_event_list.get()); ui_->OnControllerUpdated(controller_model, reticle_model); }
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index 496e7aa..ebe7c241 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc
@@ -151,7 +151,10 @@ #include "ui/message_center/message_center.h" #endif -#if !defined(OS_ANDROID) +#if defined(OS_ANDROID) +#include "chrome/browser/android/chrome_feature_list.h" +#include "chrome/browser/android/component_updater/background_task_update_scheduler.h" +#else #include "chrome/browser/gcm/gcm_product_util.h" #include "components/gcm_driver/gcm_client_factory.h" #include "components/gcm_driver/gcm_desktop_utils.h" @@ -1011,11 +1014,23 @@ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) return nullptr; + std::unique_ptr<component_updater::UpdateScheduler> scheduler; +#if defined(OS_ANDROID) + if (base::FeatureList::IsEnabled( + chrome::android::kBackgroundTaskComponentUpdate) && + component_updater::BackgroundTaskUpdateScheduler::IsAvailable()) { + scheduler = + std::make_unique<component_updater::BackgroundTaskUpdateScheduler>(); + } +#endif + if (!scheduler) + scheduler = std::make_unique<component_updater::TimerUpdateScheduler>(); + component_updater_ = component_updater::ComponentUpdateServiceFactory( component_updater::MakeChromeComponentUpdaterConfigurator( base::CommandLine::ForCurrentProcess(), g_browser_process->local_state()), - std::make_unique<component_updater::TimerUpdateScheduler>()); + std::move(scheduler)); return component_updater_.get(); }
diff --git a/chrome/browser/chrome_browser_main_linux.cc b/chrome/browser/chrome_browser_main_linux.cc index 3a26947e..be63d8b 100644 --- a/chrome/browser/chrome_browser_main_linux.cc +++ b/chrome/browser/chrome_browser_main_linux.cc
@@ -64,8 +64,6 @@ l10n_util::GetStringUTF8(IDS_SHORT_PRODUCT_NAME)); #if !defined(OS_CHROMEOS) - // Set up crypt config. This should be kept in sync with the OSCrypt parts of - // SystemNetworkContextManager::OnNetworkServiceCreated. std::unique_ptr<os_crypt::Config> config(new os_crypt::Config()); // Forward to os_crypt the flag to use a specific password store. config->store =
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index e21d6c4..0d167f1d 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -497,6 +497,10 @@ #include "chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.h" #endif +#if defined(OS_LINUX) +#include "components/services/font/public/interfaces/constants.mojom.h" +#endif + using base::FileDescriptor; using content::BrowserThread; using content::BrowserURLHandler; @@ -3640,6 +3644,12 @@ &l10n_util::GetStringUTF16, IDS_UTILITY_PROCESS_FILE_UTILITY_NAME); #endif +#if defined(OS_LINUX) + (*services)[font_service::mojom::kServiceName] = + base::BindRepeating(&l10n_util::GetStringUTF16, + IDS_UTILITY_PROCESS_FONT_SERVICE_UTILITY_NAME); +#endif + (*services)[patch::mojom::kServiceName] = base::BindRepeating( &l10n_util::GetStringUTF16, IDS_UTILITY_PROCESS_PATCH_NAME);
diff --git a/chrome/browser/chrome_network_service_browsertest.cc b/chrome/browser/chrome_network_service_browsertest.cc deleted file mode 100644 index 2da89e4..0000000 --- a/chrome/browser/chrome_network_service_browsertest.cc +++ /dev/null
@@ -1,115 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/test/bind_test_util.h" -#include "base/test/scoped_feature_list.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "components/cookie_config/cookie_store_util.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/network_service_instance.h" -#include "content/public/test/browser_test.h" -#include "net/extras/sqlite/cookie_crypto_delegate.h" -#include "services/network/public/cpp/features.h" - -namespace content { -namespace { - -constexpr char kCookieName[] = "Name"; -constexpr char kCookieValue[] = "Value"; - -net::CookieList GetCookies( - const network::mojom::CookieManagerPtr& cookie_manager) { - base::RunLoop run_loop; - net::CookieList cookies_out; - cookie_manager->GetAllCookies( - base::BindLambdaForTesting([&](const net::CookieList& cookies) { - cookies_out = cookies; - run_loop.Quit(); - })); - run_loop.Run(); - return cookies_out; -} - -void SetCookie(const network::mojom::CookieManagerPtr& cookie_manager) { - base::Time t = base::Time::Now(); - net::CanonicalCookie cookie(kCookieName, kCookieValue, "www.test.com", "/", t, - t + base::TimeDelta::FromDays(1), base::Time(), - false, false, net::CookieSameSite::DEFAULT_MODE, - net::COOKIE_PRIORITY_DEFAULT); - base::RunLoop run_loop; - cookie_manager->SetCanonicalCookie( - cookie, false, false, - base::BindLambdaForTesting([&](bool success) { run_loop.Quit(); })); - run_loop.Run(); -} - -// See |NetworkServiceBrowserTest| for content's version of tests. -class ChromeNetworkServiceBrowserTest : public InProcessBrowserTest { - public: - ChromeNetworkServiceBrowserTest() { - scoped_feature_list_.InitAndEnableFeature( - network::features::kNetworkService); - } - - protected: - network::mojom::NetworkContextPtr CreateNetworkContext( - bool enable_encrypted_cookies) { - network::mojom::NetworkContextPtr network_context; - network::mojom::NetworkContextParamsPtr context_params = - network::mojom::NetworkContextParams::New(); - context_params->enable_encrypted_cookies = enable_encrypted_cookies; - context_params->cookie_path = - browser()->profile()->GetPath().Append(FILE_PATH_LITERAL("cookies")); - GetNetworkService()->CreateNetworkContext( - mojo::MakeRequest(&network_context), std::move(context_params)); - return network_context; - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; - - DISALLOW_COPY_AND_ASSIGN(ChromeNetworkServiceBrowserTest); -}; - -IN_PROC_BROWSER_TEST_F(ChromeNetworkServiceBrowserTest, PRE_EncryptedCookies) { - // First set a cookie with cookie encryption enabled. - network::mojom::NetworkContextPtr context = - CreateNetworkContext(/*enable_encrypted_cookies=*/true); - network::mojom::CookieManagerPtr cookie_manager; - context->GetCookieManager(mojo::MakeRequest(&cookie_manager)); - - SetCookie(cookie_manager); - - net::CookieList cookies = GetCookies(cookie_manager); - ASSERT_EQ(1u, cookies.size()); - EXPECT_EQ(kCookieName, cookies[0].Name()); - EXPECT_EQ(kCookieValue, cookies[0].Value()); -} - -IN_PROC_BROWSER_TEST_F(ChromeNetworkServiceBrowserTest, EncryptedCookies) { - net::CookieCryptoDelegate* crypto_delegate = - cookie_config::GetCookieCryptoDelegate(); - std::string ciphertext; - crypto_delegate->EncryptString(kCookieValue, &ciphertext); - // These checks are only valid if crypto is enabled on the platform. - if (!crypto_delegate->ShouldEncrypt() || ciphertext == kCookieValue) - return; - - // Now attempt to read the cookie with encryption disabled. - network::mojom::NetworkContextPtr context = - CreateNetworkContext(/*enable_encrypted_cookies=*/false); - network::mojom::CookieManagerPtr cookie_manager; - context->GetCookieManager(mojo::MakeRequest(&cookie_manager)); - - net::CookieList cookies = GetCookies(cookie_manager); - ASSERT_EQ(1u, cookies.size()); - EXPECT_EQ(kCookieName, cookies[0].Name()); - EXPECT_EQ("", cookies[0].Value()); -} - -} // namespace -} // namespace content
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc index 9c38c258..4a458dfc 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager.cc +++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -414,6 +414,12 @@ return base::Singleton<CrostiniManager>::get(); } +bool CrostiniManager::IsVmRunning(Profile* profile, std::string vm_name) { + return running_vms_.find(std::make_pair(CryptohomeIdForProfile(profile), + std::move(vm_name))) != + running_vms_.end(); +} + CrostiniManager::CrostiniManager() : weak_ptr_factory_(this) { // Cicerone/ConciergeClient and its observer_list_ will be destroyed together. // We add, but don't need to remove the observer. (Doing so would force a @@ -632,7 +638,8 @@ GetConciergeClient()->StartTerminaVm( request, base::BindOnce(&CrostiniManager::OnStartTerminaVm, - weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + weak_ptr_factory_.GetWeakPtr(), request.owner_id(), + request.name(), std::move(callback))); } void CrostiniManager::StopVm(Profile* profile, @@ -644,13 +651,16 @@ return; } + std::string owner_id = CryptohomeIdForProfile(profile); + vm_tools::concierge::StopVmRequest request; - request.set_owner_id(CryptohomeIdForProfile(profile)); - request.set_name(std::move(name)); + request.set_owner_id(owner_id); + request.set_name(name); GetConciergeClient()->StopVm( std::move(request), base::BindOnce(&CrostiniManager::OnStopVm, weak_ptr_factory_.GetWeakPtr(), + std::move(owner_id), std::move(name), std::move(callback))); } @@ -920,6 +930,8 @@ } void CrostiniManager::OnStartTerminaVm( + std::string owner_id, + std::string vm_name, StartTerminaVmCallback callback, base::Optional<vm_tools::concierge::StartVmResponse> reply) { if (!reply.has_value()) { @@ -934,10 +946,13 @@ std::move(callback).Run(ConciergeClientResult::VM_START_FAILED); return; } + running_vms_.emplace(std::move(owner_id), std::move(vm_name)); std::move(callback).Run(ConciergeClientResult::SUCCESS); } void CrostiniManager::OnStopVm( + std::string owner_id, + std::string vm_name, StopVmCallback callback, base::Optional<vm_tools::concierge::StopVmResponse> reply) { if (!reply.has_value()) { @@ -960,6 +975,8 @@ return; } } + // Remove from running_vms_. + running_vms_.erase(std::make_pair(std::move(owner_id), std::move(vm_name))); std::move(callback).Run(ConciergeClientResult::SUCCESS); }
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h index 67ff755..cd7c675 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager.h +++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_MANAGER_H_ #include <map> +#include <set> #include <string> #include <tuple> #include <utility> @@ -276,6 +277,8 @@ // Returns the singleton instance of CrostiniManager. static CrostiniManager* GetInstance(); + bool IsVmRunning(Profile* profile, std::string vm_name); + private: friend struct base::DefaultSingletonTraits<CrostiniManager>; @@ -303,12 +306,16 @@ // Callback for ConciergeClient::StartTerminaVm. Called after the Concierge // service method finishes. void OnStartTerminaVm( + std::string owner_id, + std::string vm_name, StartTerminaVmCallback callback, base::Optional<vm_tools::concierge::StartVmResponse> reply); // Callback for ConciergeClient::StopVm. Called after the Concierge // service method finishes. - void OnStopVm(StopVmCallback callback, + void OnStopVm(std::string owner_id, + std::string vm_name, + StopVmCallback callback, base::Optional<vm_tools::concierge::StopVmResponse> reply); // Callback for CrostiniClient::StartConcierge. Called after the @@ -367,6 +374,9 @@ ShutdownContainerCallback> shutdown_container_callbacks_; + // Running vms as <owner_id, vm_name> pairs. + std::set<std::pair<std::string, std::string>> running_vms_; + // Note: This should remain the last member so it'll be destroyed and // invalidate its weak pointers before any other members are destroyed. base::WeakPtrFactory<CrostiniManager> weak_ptr_factory_;
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc index c28536f9..4ae257a 100644 --- a/chrome/browser/chromeos/crostini/crostini_util.cc +++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -149,6 +149,11 @@ return profile->GetPrefs()->GetBoolean(crostini::prefs::kCrostiniEnabled); } +bool IsCrostiniRunning(Profile* profile) { + return crostini::CrostiniManager::GetInstance()->IsVmRunning( + profile, kCrostiniDefaultVmName); +} + void LaunchCrostiniApp(Profile* profile, const std::string& app_id, int64_t display_id) {
diff --git a/chrome/browser/chromeos/crostini/crostini_util.h b/chrome/browser/chromeos/crostini/crostini_util.h index fc56148..971ffdd 100644 --- a/chrome/browser/chromeos/crostini/crostini_util.h +++ b/chrome/browser/chromeos/crostini/crostini_util.h
@@ -32,6 +32,9 @@ // at least once and not deleted it. bool IsCrostiniEnabled(Profile* profile); +// Returns whether the default Crostini VM is running for the user. +bool IsCrostiniRunning(Profile* profile); + // Launches the Crostini app with ID of |app_id| on the display with ID of // |display_id|. |app_id| should be a valid Crostini app list id. void LaunchCrostiniApp(Profile* profile,
diff --git a/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service.cc b/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service.cc index 55d76987..d0fe8473 100644 --- a/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service.cc +++ b/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service.cc
@@ -16,6 +16,7 @@ #include "chrome/browser/chromeos/cryptauth/gcm_device_info_provider_impl.h" #include "chrome/browser/gcm/gcm_profile_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/common/pref_names.h" @@ -46,8 +47,7 @@ std::unique_ptr<cryptauth::CryptAuthClientFactory> CreateCryptAuthClientFactoryImpl(Profile* profile) { return std::make_unique<cryptauth::CryptAuthClientFactoryImpl>( - ProfileOAuth2TokenServiceFactory::GetForProfile(profile), - SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedAccountId(), + IdentityManagerFactory::GetForProfile(profile), profile->GetRequestContext(), cryptauth::device_classifier_util::GetDeviceClassifier()); }
diff --git a/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service_factory.cc b/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service_factory.cc index 22bfe39..7365c90 100644 --- a/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service_factory.cc +++ b/chrome/browser/chromeos/cryptauth/chrome_cryptauth_service_factory.cc
@@ -7,6 +7,7 @@ #include "chrome/browser/chromeos/cryptauth/chrome_cryptauth_service.h" #include "chrome/browser/gcm/gcm_profile_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chromeos/chromeos_features.h" @@ -32,6 +33,7 @@ : BrowserContextKeyedServiceFactory( "CryptAuthService", BrowserContextDependencyManager::GetInstance()) { + DependsOn(IdentityManagerFactory::GetInstance()); DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance()); DependsOn(SigninManagerFactory::GetInstance()); DependsOn(gcm::GCMProfileServiceFactory::GetInstance());
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc index 5ddb988..6134a90 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -737,6 +737,7 @@ SET_STRING("SHOW_ALL_ANDROID_FOLDERS_OPTION", IDS_FILE_BROWSER_SHOW_ALL_ANDROID_FOLDERS_OPTION); SET_STRING("LINUX_FILES_ROOT_LABEL", IDS_FILE_BROWSER_LINUX_FILES_ROOT_LABEL); + SET_STRING("MY_FILES_ROOT_LABEL", IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL); SET_STRING("MEDIA_ARTIST_COLUMN_LABEL", IDS_FILE_BROWSER_MEDIA_ARTIST_COLUMN_LABEL); SET_STRING("MEDIA_TITLE_COLUMN_LABEL",
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc index 080dcafc..ffabcba 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -42,11 +42,17 @@ return *this; } + TestCase& EnableMyFiles() { + enable_new_navigation = true; + return *this; + } + const char* test_case_name = nullptr; GuestMode guest_mode = NOT_IN_GUEST_MODE; bool trusted_events = false; bool tablet_mode = false; bool enable_drivefs = false; + bool enable_new_navigation = false; }; // EventCase: FilesAppBrowserTest with trusted JS Events. @@ -77,6 +83,11 @@ if (GetParam().tablet_mode) { command_line->AppendSwitchASCII("force-tablet-mode", "touch_view"); } + + // If requested, enable the new-files-app-navigation flag. + if (GetParam().enable_new_navigation) { + command_line->AppendSwitchASCII("new-files-app-navigation", ""); + } } GuestMode GetGuestMode() const override { return GetParam().guest_mode; } @@ -122,6 +133,9 @@ if (test.param.enable_drivefs) name.append("_DriveFs"); + if (test.param.enable_new_navigation) + name.append("_MyFiles"); + return name; } @@ -218,6 +232,11 @@ TestCase("checkRenameDisabledForReadOnlyDocument"), TestCase("checkRenameDisabledForReadOnlyFile"), TestCase("checkRenameDisabledForReadOnlyFolder"), + TestCase("checkShareEnabledForReadWriteFile"), + TestCase("checkShareEnabledForReadOnlyDocument"), + TestCase("checkShareDisabledForStrictReadOnlyDocument"), + TestCase("checkShareEnabledForReadOnlyFile"), + TestCase("checkShareEnabledForReadOnlyFolder"), TestCase("checkCopyEnabledForReadWriteFile"), TestCase("checkCopyEnabledForReadOnlyDocument"), TestCase("checkCopyDisabledForStrictReadOnlyDocument"), @@ -456,6 +475,11 @@ FilesAppBrowserTest, ::testing::Values(TestCase("mountCrostiniContainer"))); +WRAPPED_INSTANTIATE_TEST_CASE_P( + MyFiles, /* my_files.js */ + FilesAppBrowserTest, + ::testing::Values(TestCase("showMyFiles").EnableMyFiles())); + // Structure to describe an account info. struct TestAccountInfo { const char* const gaia_id;
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc index 4d0c56f..5b1fd37 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
@@ -209,3 +209,8 @@ RunTest(base::FilePath(FILE_PATH_LITERAL( "foreground/js/ui/file_list_selection_model_unittest.html"))); } + +IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FilesAppEntryTypes) { + RunTest(base::FilePath( + FILE_PATH_LITERAL("common/js/files_app_entry_types_unittest.html"))); +}
diff --git a/chrome/browser/chromeos/file_manager/gallery_browsertest.cc b/chrome/browser/chromeos/file_manager/gallery_browsertest.cc index 2b5cf46..485422c 100644 --- a/chrome/browser/chromeos/file_manager/gallery_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/gallery_browsertest.cc
@@ -71,9 +71,8 @@ StartTest(); } -// Flaky in RELEASE crbug.com/857021 IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - DISABLED_CheckAvailabilityOfEditAndPrintButtons) { + CheckAvailabilityOfEditAndPrintButtons) { set_test_case_name("checkAvailabilityOfEditAndPrintButtons"); StartTest(); }
diff --git a/chrome/browser/consent_auditor/consent_auditor_factory.cc b/chrome/browser/consent_auditor/consent_auditor_factory.cc index ebaf91c..4c16c5d 100644 --- a/chrome/browser/consent_auditor/consent_auditor_factory.cc +++ b/chrome/browser/consent_auditor/consent_auditor_factory.cc
@@ -51,7 +51,8 @@ content::BrowserContext* context) const { Profile* profile = static_cast<Profile*>(context); - std::unique_ptr<syncer::ConsentSyncBridge> bridge; + std::unique_ptr<syncer::ConsentSyncBridge> consent_sync_bridge; + syncer::UserEventService* user_event_service = nullptr; if (base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType)) { syncer::OnceModelTypeStoreFactory store_factory = browser_sync::ProfileSyncService::GetModelTypeStoreFactory( @@ -61,13 +62,15 @@ syncer::USER_CONSENTS, base::BindRepeating(&syncer::ReportUnrecoverableError, chrome::GetChannel())); - bridge = std::make_unique<syncer::ConsentSyncBridgeImpl>( + consent_sync_bridge = std::make_unique<syncer::ConsentSyncBridgeImpl>( std::move(store_factory), std::move(change_processor)); + } else { + user_event_service = + browser_sync::UserEventServiceFactory::GetForProfile(profile); } - // TODO(vitaliii): Don't create UserEventService when it won't be used. + return new consent_auditor::ConsentAuditor( - profile->GetPrefs(), std::move(bridge), - browser_sync::UserEventServiceFactory::GetForProfile(profile), + profile->GetPrefs(), std::move(consent_sync_bridge), user_event_service, // The browser version and locale do not change runtime, so we can pass // them directly. version_info::GetVersionNumber(),
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc index c8c6ac5..9658230e 100644 --- a/chrome/browser/extensions/api/settings_private/prefs_util.cc +++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -200,7 +200,7 @@ settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[::prefs::kSearchSuggestEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; - (*s_whitelist)[::prefs::kUnifiedConsentGiven] = + (*s_whitelist)[::unified_consent::prefs::kUnifiedConsentGiven] = settings_api::PrefType::PREF_TYPE_BOOLEAN; // Languages page
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index f384d7b..999656cae 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -542,13 +542,6 @@ const char kEnableMacMaterialDesignDownloadShelfDescription[] = "If enabled, the download shelf uses Material Design."; -const char kEnableManualFallbacksFillingName[] = - "Manual fallbacks for password manager forms filling"; -const char kEnableManualFallbacksFillingDescription[] = - "If enabled, then if user clicks on the password field on a form, popup " - "might contain generation fallbacks or 'Show all saved passwords' " - "fallback."; - const char kEnablePolicyToolName[] = "Enable policy management page"; const char kEnablePolicyToolDescription[] = "If enabled, the chrome://policy-tool URL loads a page for managing " @@ -2085,6 +2078,11 @@ "Enables downloading pages in the background in case page is not yet " "loaded in current tab."; +const char kBackgroundTaskComponentUpdateName[] = + "Background Task Component Updates"; +const char kBackgroundTaskComponentUpdateDescription[] = + "Schedule component updates with BackgroundTaskScheduler"; + const char kCCTModuleName[] = "Chrome Custom Tabs Module"; const char kCCTModuleDescription[] = "Enables a dynamically loaded module in Chrome Custom Tabs, on Android.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 5ec8d3a..6505620 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -359,9 +359,6 @@ extern const char kEnableMacMaterialDesignDownloadShelfName[]; extern const char kEnableMacMaterialDesignDownloadShelfDescription[]; -extern const char kEnableManualFallbacksFillingName[]; -extern const char kEnableManualFallbacksFillingDescription[]; - extern const char kEnableMaterialDesignBookmarksName[]; extern const char kEnableMaterialDesignBookmarksDescription[]; @@ -647,9 +644,6 @@ extern const char kMacViewsAutofillPopupName[]; extern const char kMacViewsAutofillPopupDescription[]; -extern const char kManualPasswordGenerationName[]; -extern const char kManualPasswordGenerationDescription[]; - extern const char kMarkHttpAsName[]; extern const char kMarkHttpAsDescription[]; extern const char kMarkHttpAsDangerous[]; @@ -1271,6 +1265,9 @@ extern const char kBackgroundLoaderForDownloadsName[]; extern const char kBackgroundLoaderForDownloadsDescription[]; +extern const char kBackgroundTaskComponentUpdateName[]; +extern const char kBackgroundTaskComponentUpdateDescription[]; + extern const char kCCTModuleName[]; extern const char kCCTModuleDescription[];
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc index 7aeef806..fe59b997 100644 --- a/chrome/browser/net/system_network_context_manager.cc +++ b/chrome/browser/net/system_network_context_manager.cc
@@ -49,13 +49,6 @@ #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #endif // defined(OS_CHROMEOS) -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -#include "chrome/common/chrome_paths_internal.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/grit/chromium_strings.h" -#include "ui/base/l10n/l10n_util.h" -#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) - namespace { // Called on IOThread to disable QUIC for HttpNetworkSessions not using the @@ -438,21 +431,6 @@ GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers); content::GetNetworkService()->ConfigureStubHostResolver( stub_resolver_enabled, std::move(dns_over_https_servers)); - -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - - // Set up crypt config. This should be kept in sync with the OSCrypt parts of - // ChromeBrowserMainPartsLinux::PreProfileInit. - network::mojom::CryptConfigPtr config = network::mojom::CryptConfig::New(); - config->store = command_line.GetSwitchValueASCII(switches::kPasswordStore); - config->product_name = l10n_util::GetStringUTF8(IDS_PRODUCT_NAME); - config->should_use_preference = - command_line.HasSwitch(switches::kEnableEncryptionSelection); - chrome::GetDefaultUserDataDirectory(&config->user_data_path); - content::GetNetworkService()->SetCryptConfig(std::move(config)); -#endif } void SystemNetworkContextManager::DisableQuic() {
diff --git a/chrome/browser/notifications/notification_common.h b/chrome/browser/notifications/notification_common.h index 29d91600..b02a5dc 100644 --- a/chrome/browser/notifications/notification_common.h +++ b/chrome/browser/notifications/notification_common.h
@@ -17,13 +17,12 @@ public: // Things as user can do to a notification. Keep in sync with the // NotificationOperation enumeration in notification_response_builder_mac.h. - // TODO(peter): Prefix these options with OPERATION_. enum Operation { - CLICK = 0, - CLOSE = 1, - DISABLE_PERMISSION = 2, - SETTINGS = 3, - OPERATION_MAX = SETTINGS + OPERATION_CLICK = 0, + OPERATION_CLOSE = 1, + OPERATION_DISABLE_PERMISSION = 2, + OPERATION_SETTINGS = 3, + OPERATION_MAX = OPERATION_SETTINGS }; // A struct that contains extra data about a notification specific to one of
diff --git a/chrome/browser/notifications/notification_display_service_impl.cc b/chrome/browser/notifications/notification_display_service_impl.cc index 741a087..cef2157 100644 --- a/chrome/browser/notifications/notification_display_service_impl.cc +++ b/chrome/browser/notifications/notification_display_service_impl.cc
@@ -158,19 +158,19 @@ base::OnceClosure completed_closure = base::BindOnce(&OperationCompleted); switch (operation) { - case NotificationCommon::CLICK: + case NotificationCommon::OPERATION_CLICK: handler->OnClick(profile_, origin, notification_id, action_index, reply, std::move(completed_closure)); break; - case NotificationCommon::CLOSE: + case NotificationCommon::OPERATION_CLOSE: DCHECK(by_user.has_value()); handler->OnClose(profile_, origin, notification_id, by_user.value(), std::move(completed_closure)); break; - case NotificationCommon::DISABLE_PERMISSION: + case NotificationCommon::OPERATION_DISABLE_PERMISSION: handler->DisableNotifications(profile_, origin); break; - case NotificationCommon::SETTINGS: + case NotificationCommon::OPERATION_SETTINGS: handler->OpenSettings(profile_, origin); break; }
diff --git a/chrome/browser/notifications/notification_platform_bridge_android.cc b/chrome/browser/notifications/notification_platform_bridge_android.cc index 258d1f4..1adaea5 100644 --- a/chrome/browser/notifications/notification_platform_bridge_android.cc +++ b/chrome/browser/notifications/notification_platform_bridge_android.cc
@@ -167,7 +167,7 @@ profile_manager->LoadProfile( profile_id, incognito, base::Bind(&NotificationDisplayServiceImpl::ProfileLoadedCallback, - NotificationCommon::CLICK, + NotificationCommon::OPERATION_CLICK, NotificationHandler::Type::WEB_PERSISTENT, origin, notification_id, std::move(action_index), std::move(reply), base::nullopt /* by_user */)); @@ -213,7 +213,7 @@ profile_manager->LoadProfile( profile_id, incognito, base::Bind(&NotificationDisplayServiceImpl::ProfileLoadedCallback, - NotificationCommon::CLOSE, + NotificationCommon::OPERATION_CLOSE, NotificationHandler::Type::WEB_PERSISTENT, GURL(ConvertJavaStringToUTF8(env, java_origin)), notification_id, base::nullopt /* action index */,
diff --git a/chrome/browser/notifications/notification_platform_bridge_chromeos.cc b/chrome/browser/notifications/notification_platform_bridge_chromeos.cc index 672c6c5..e08d0db2 100644 --- a/chrome/browser/notifications/notification_platform_bridge_chromeos.cc +++ b/chrome/browser/notifications/notification_platform_bridge_chromeos.cc
@@ -126,7 +126,7 @@ } else { NotificationDisplayServiceImpl::GetForProfile(notification->profile()) ->ProcessNotificationOperation( - NotificationCommon::CLOSE, notification->type(), + NotificationCommon::OPERATION_CLOSE, notification->type(), notification->notification().origin_url(), notification->original_id(), base::nullopt, base::nullopt, by_user); } @@ -145,7 +145,7 @@ } else { NotificationDisplayServiceImpl::GetForProfile(notification->profile()) ->ProcessNotificationOperation( - NotificationCommon::CLICK, notification->type(), + NotificationCommon::OPERATION_CLICK, notification->type(), notification->notification().origin_url(), notification->original_id(), base::nullopt, base::nullopt, base::nullopt); @@ -165,7 +165,7 @@ } else { NotificationDisplayServiceImpl::GetForProfile(notification->profile()) ->ProcessNotificationOperation( - NotificationCommon::CLICK, notification->type(), + NotificationCommon::OPERATION_CLICK, notification->type(), notification->notification().origin_url(), notification->original_id(), button_index, reply, base::nullopt); } @@ -182,7 +182,7 @@ } else { NotificationDisplayServiceImpl::GetForProfile(notification->profile()) ->ProcessNotificationOperation( - NotificationCommon::SETTINGS, notification->type(), + NotificationCommon::OPERATION_SETTINGS, notification->type(), notification->notification().origin_url(), notification->original_id(), base::nullopt, base::nullopt, base::nullopt); @@ -197,11 +197,11 @@ DCHECK_NE(NotificationHandler::Type::TRANSIENT, notification->type()); NotificationDisplayServiceImpl::GetForProfile(notification->profile()) - ->ProcessNotificationOperation(NotificationCommon::DISABLE_PERMISSION, - notification->type(), - notification->notification().origin_url(), - notification->original_id(), base::nullopt, - base::nullopt, base::nullopt); + ->ProcessNotificationOperation( + NotificationCommon::OPERATION_DISABLE_PERMISSION, + notification->type(), notification->notification().origin_url(), + notification->original_id(), base::nullopt, base::nullopt, + base::nullopt); } ProfileNotification* NotificationPlatformBridgeChromeOs::GetProfileNotification(
diff --git a/chrome/browser/notifications/notification_platform_bridge_linux.cc b/chrome/browser/notifications/notification_platform_bridge_linux.cc index 1ab3d22..a5fc6e5 100644 --- a/chrome/browser/notifications/notification_platform_bridge_linux.cc +++ b/chrome/browser/notifications/notification_platform_bridge_linux.cc
@@ -787,12 +787,12 @@ return; if (action == kDefaultButtonId) { - ForwardNotificationOperation(FROM_HERE, data, NotificationCommon::CLICK, - base::nullopt /* action_index */, - base::nullopt /* by_user */); + ForwardNotificationOperation( + FROM_HERE, data, NotificationCommon::OPERATION_CLICK, + base::nullopt /* action_index */, base::nullopt /* by_user */); } else if (action == kSettingsButtonId) { ForwardNotificationOperation( - FROM_HERE, data, NotificationCommon::SETTINGS, + FROM_HERE, data, NotificationCommon::OPERATION_SETTINGS, base::nullopt /* action_index */, base::nullopt /* by_user */); } else if (action == kCloseButtonId) { CloseOnTaskRunner(data->profile_id, data->notification_id); @@ -804,7 +804,8 @@ size_t id_zero_based = id - data->action_start; if (id_zero_based >= n_buttons) return; - ForwardNotificationOperation(FROM_HERE, data, NotificationCommon::CLICK, + ForwardNotificationOperation(FROM_HERE, data, + NotificationCommon::OPERATION_CLICK, id_zero_based, base::nullopt /* by_user */); } } @@ -821,9 +822,9 @@ return; // TODO(peter): Can we support |by_user| appropriately here? - ForwardNotificationOperation(FROM_HERE, data, NotificationCommon::CLOSE, - base::nullopt /* action_index */, - true /* by_user */); + ForwardNotificationOperation( + FROM_HERE, data, NotificationCommon::OPERATION_CLOSE, + base::nullopt /* action_index */, true /* by_user */); notifications_.erase(data); }
diff --git a/chrome/browser/notifications/notification_platform_bridge_message_center.cc b/chrome/browser/notifications/notification_platform_bridge_message_center.cc index d7707b71..3e06f3e 100644 --- a/chrome/browser/notifications/notification_platform_bridge_message_center.cc +++ b/chrome/browser/notifications/notification_platform_bridge_message_center.cc
@@ -34,7 +34,7 @@ void SettingsClick() override { NotificationDisplayServiceImpl::GetForProfile(profile_) ->ProcessNotificationOperation( - NotificationCommon::SETTINGS, notification_type_, + NotificationCommon::OPERATION_SETTINGS, notification_type_, notification_.origin_url(), notification_.id(), base::nullopt, base::nullopt, base::nullopt /* by_user */); } @@ -42,8 +42,8 @@ void DisableNotification() override { NotificationDisplayServiceImpl::GetForProfile(profile_) ->ProcessNotificationOperation( - NotificationCommon::DISABLE_PERMISSION, notification_type_, - notification_.origin_url(), notification_.id(), + NotificationCommon::OPERATION_DISABLE_PERMISSION, + notification_type_, notification_.origin_url(), notification_.id(), base::nullopt /* action_index */, base::nullopt /* reply */, base::nullopt /* by_user */); } @@ -51,7 +51,7 @@ void Close(bool by_user) override { NotificationDisplayServiceImpl::GetForProfile(profile_) ->ProcessNotificationOperation( - NotificationCommon::CLOSE, notification_type_, + NotificationCommon::OPERATION_CLOSE, notification_type_, notification_.origin_url(), notification_.id(), base::nullopt /* action_index */, base::nullopt /* reply */, by_user); @@ -61,7 +61,7 @@ const base::Optional<base::string16>& reply) override { NotificationDisplayServiceImpl::GetForProfile(profile_) ->ProcessNotificationOperation( - NotificationCommon::CLICK, notification_type_, + NotificationCommon::OPERATION_CLICK, notification_type_, notification_.origin_url(), notification_.id(), button_index, reply, base::nullopt /* by_user */); }
diff --git a/chrome/browser/notifications/notification_platform_bridge_win.cc b/chrome/browser/notifications/notification_platform_bridge_win.cc index 63318e2..56ddb52 100644 --- a/chrome/browser/notifications/notification_platform_bridge_win.cc +++ b/chrome/browser/notifications/notification_platform_bridge_win.cc
@@ -691,7 +691,7 @@ DLOG(ERROR) << "Failed to get toast dismissal reason: " << std::hex << hr; } - HandleEvent(notification, NotificationCommon::CLOSE, + HandleEvent(notification, NotificationCommon::OPERATION_CLOSE, /*action_index=*/base::nullopt, by_user); return S_OK; } @@ -839,9 +839,9 @@ if (!inline_reply.empty()) reply = inline_reply; - NotificationCommon::Operation operation = launch_id.is_for_context_menu() - ? NotificationCommon::SETTINGS - : NotificationCommon::CLICK; + NotificationCommon::Operation operation = + launch_id.is_for_context_menu() ? NotificationCommon::OPERATION_SETTINGS + : NotificationCommon::OPERATION_CLICK; ForwardNotificationOperationOnUiThread( operation, launch_id.notification_type(), launch_id.origin_url(),
diff --git a/chrome/browser/notifications/notification_platform_bridge_win_interactive_uitest.cc b/chrome/browser/notifications/notification_platform_bridge_win_interactive_uitest.cc index 4a18e5a..15fca7e5 100644 --- a/chrome/browser/notifications/notification_platform_bridge_win_interactive_uitest.cc +++ b/chrome/browser/notifications/notification_platform_bridge_win_interactive_uitest.cc
@@ -249,12 +249,12 @@ // Simulate clicks on the toast. NotificationPlatformBridgeWin* bridge = GetBridge(); ASSERT_TRUE(bridge); - bridge->ForwardHandleEventForTesting(NotificationCommon::CLICK, &toast, &args, - base::nullopt); + bridge->ForwardHandleEventForTesting(NotificationCommon::OPERATION_CLICK, + &toast, &args, base::nullopt); run_loop.Run(); // Validate the click values. - EXPECT_EQ(NotificationCommon::CLICK, last_operation_); + EXPECT_EQ(NotificationCommon::OPERATION_CLICK, last_operation_); EXPECT_EQ(NotificationHandler::Type::WEB_PERSISTENT, last_notification_type_); EXPECT_EQ(GURL("https://example.com/"), last_origin_); EXPECT_EQ("notification_id", last_notification_id_); @@ -281,7 +281,7 @@ run_loop.Run(); // Validate the values. - EXPECT_EQ(NotificationCommon::CLICK, last_operation_); + EXPECT_EQ(NotificationCommon::OPERATION_CLICK, last_operation_); EXPECT_EQ(NotificationHandler::Type::WEB_PERSISTENT, last_notification_type_); EXPECT_EQ(GURL("https://example.com/"), last_origin_); EXPECT_EQ("notification_id", last_notification_id_); @@ -320,12 +320,12 @@ // Simulate clicks on the toast. NotificationPlatformBridgeWin* bridge = GetBridge(); ASSERT_TRUE(bridge); - bridge->ForwardHandleEventForTesting(NotificationCommon::SETTINGS, &toast, - &args, base::nullopt); + bridge->ForwardHandleEventForTesting(NotificationCommon::OPERATION_SETTINGS, + &toast, &args, base::nullopt); run_loop.Run(); // Validate the click values. - EXPECT_EQ(NotificationCommon::SETTINGS, last_operation_); + EXPECT_EQ(NotificationCommon::OPERATION_SETTINGS, last_operation_); EXPECT_EQ(NotificationHandler::Type::WEB_PERSISTENT, last_notification_type_); EXPECT_EQ(GURL("https://example.com/"), last_origin_); EXPECT_EQ("notification_id", last_notification_id_); @@ -476,7 +476,7 @@ ASSERT_NO_FATAL_FAILURE(ProcessLaunchIdViaCmdLine(kLaunchId, /*reply=*/"")); // Validate the click values. - EXPECT_EQ(NotificationCommon::CLICK, last_operation_); + EXPECT_EQ(NotificationCommon::OPERATION_CLICK, last_operation_); EXPECT_EQ(NotificationHandler::Type::WEB_PERSISTENT, last_notification_type_); EXPECT_EQ(GURL("https://example.com/"), last_origin_); EXPECT_EQ("notification_id", last_notification_id_); @@ -494,7 +494,7 @@ ProcessLaunchIdViaCmdLine(kLaunchIdButtonClick, "Inline reply")); // Validate the click values. - EXPECT_EQ(NotificationCommon::CLICK, last_operation_); + EXPECT_EQ(NotificationCommon::OPERATION_CLICK, last_operation_); EXPECT_EQ(NotificationHandler::Type::WEB_PERSISTENT, last_notification_type_); EXPECT_EQ(GURL("https://example.com/"), last_origin_); EXPECT_EQ("notification_id", last_notification_id_); @@ -511,7 +511,7 @@ ProcessLaunchIdViaCmdLine(kLaunchIdButtonClick, /*reply=*/"")); // Validate the click values. - EXPECT_EQ(NotificationCommon::CLICK, last_operation_); + EXPECT_EQ(NotificationCommon::OPERATION_CLICK, last_operation_); EXPECT_EQ(NotificationHandler::Type::WEB_PERSISTENT, last_notification_type_); EXPECT_EQ(GURL("https://example.com/"), last_origin_); EXPECT_EQ("notification_id", last_notification_id_); @@ -528,7 +528,7 @@ ProcessLaunchIdViaCmdLine(kLaunchIdSettings, /*reply=*/"")); // Validate the click values. - EXPECT_EQ(NotificationCommon::SETTINGS, last_operation_); + EXPECT_EQ(NotificationCommon::OPERATION_SETTINGS, last_operation_); EXPECT_EQ(NotificationHandler::Type::WEB_PERSISTENT, last_notification_type_); EXPECT_EQ(GURL("https://example.com/"), last_origin_); EXPECT_EQ("notification_id", last_notification_id_);
diff --git a/chrome/browser/resources/chromeos/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/chromevox/BUILD.gn index 0ecda6b..840635e5 100644 --- a/chrome/browser/resources/chromeos/chromevox/BUILD.gn +++ b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
@@ -18,7 +18,7 @@ } closure_library_dir = - "//chrome/third_party/chromevox/third_party/closure-library/closure/goog" + "//third_party/chromevox/third_party/closure-library/closure/goog" # List of all modules that are included in one or more of the production # chromevox scripts. @@ -239,7 +239,7 @@ ":chromevox_manifest", "//chrome/browser/resources/chromeos/braille_ime:braille_ime_manifest", "//chrome/browser/resources/chromeos/chromevox/strings:chromevox_strings", - "//chrome/third_party/chromevox:chromevox_third_party_resources", + "//third_party/chromevox:chromevox_third_party_resources", ] if (enable_nacl) { deps += [ "//third_party/liblouis" ] @@ -571,7 +571,7 @@ # copied one. "//chrome/browser/resources/chromeos/chromevox/", "//chrome/test/data/webui/test_api.js", - "//chrome/third_party/chromevox/", + "//third_party/chromevox/", "//chrome/third_party/mock4js/", "//third_party/accessibility-audit/axs_testing.js", "//third_party/chaijs/chai.js",
diff --git a/chrome/browser/resources/chromeos/chromevox/run_jsbundler.gni b/chrome/browser/resources/chromeos/chromevox/run_jsbundler.gni index 8a5cfe6..9f309395 100644 --- a/chrome/browser/resources/chromeos/chromevox/run_jsbundler.gni +++ b/chrome/browser/resources/chromeos/chromevox/run_jsbundler.gni
@@ -5,7 +5,7 @@ assert(is_chromeos) closure_library_dir = - "//chrome/third_party/chromevox/third_party/closure-library/closure/goog" + "//third_party/chromevox/third_party/closure-library/closure/goog" jsbundler_modules = rebase_path([ "depstree.py",
diff --git a/chrome/browser/resources/chromeos/chromevox/tools/check_chromevox.py b/chrome/browser/resources/chromeos/chromevox/tools/check_chromevox.py index bbf36b3..8434198 100755 --- a/chrome/browser/resources/chromeos/chromevox/tools/check_chromevox.py +++ b/chrome/browser/resources/chromeos/chromevox/tools/check_chromevox.py
@@ -108,7 +108,7 @@ os.path.relpath( os.path.join( _CHROME_SOURCE_DIR, - 'chrome/third_party/chromevox/third_party/closure-library/' + 'third_party/chromevox/third_party/closure-library/' 'closure/goog'))] sources = ReadSources(roots, need_source_text=True, exclude=[re.compile('testing')])
diff --git a/chrome/browser/resources/chromeos/chromevox/tools/find_js_files.py b/chrome/browser/resources/chromeos/chromevox/tools/find_js_files.py index 6d459a3..246d254 100755 --- a/chrome/browser/resources/chromeos/chromevox/tools/find_js_files.py +++ b/chrome/browser/resources/chromeos/chromevox/tools/find_js_files.py
@@ -17,7 +17,7 @@ os.path.join(_SCRIPT_DIR, *[os.path.pardir] * 6)) sys.path.insert( 0, os.path.join( - _CHROME_SOURCE, ('chrome/third_party/chromevox/third_party/' + + _CHROME_SOURCE, ('third_party/chromevox/third_party/' + 'closure-library/closure/bin/build'))) import treescan
diff --git a/chrome/browser/resources/chromeos/chromevox/tools/generate_deps.py b/chrome/browser/resources/chromeos/chromevox/tools/generate_deps.py index 4468b7f9..068131b 100755 --- a/chrome/browser/resources/chromeos/chromevox/tools/generate_deps.py +++ b/chrome/browser/resources/chromeos/chromevox/tools/generate_deps.py
@@ -18,7 +18,7 @@ _CHROME_SOURCE = os.path.realpath( os.path.join(_SCRIPT_DIR, *[os.path.pardir] * 6)) sys.path.insert(0, os.path.join( - _CHROME_SOURCE, ('chrome/third_party/chromevox/third_party/' + + _CHROME_SOURCE, ('third_party/chromevox/third_party/' + 'closure-library/closure/bin/build'))) import source
diff --git a/chrome/browser/resources/chromeos/chromevox/tools/jsbundler.py b/chrome/browser/resources/chromeos/chromevox/tools/jsbundler.py index 9de4564..38c1a84 100755 --- a/chrome/browser/resources/chromeos/chromevox/tools/jsbundler.py +++ b/chrome/browser/resources/chromeos/chromevox/tools/jsbundler.py
@@ -43,7 +43,7 @@ sys.path.insert(0, os.path.join( _CHROME_SOURCE, 'third_party/blink/renderer/devtools/scripts/build')) sys.path.insert(0, os.path.join( - _CHROME_SOURCE, ('chrome/third_party/chromevox/third_party/' + + _CHROME_SOURCE, ('third_party/chromevox/third_party/' + 'closure-library/closure/bin/build'))) import depstree import rjsmin
diff --git a/chrome/browser/resources/chromeos/drive_internals.html b/chrome/browser/resources/chromeos/drive_internals.html index 3a1ef6f..ee0b852 100644 --- a/chrome/browser/resources/chromeos/drive_internals.html +++ b/chrome/browser/resources/chromeos/drive_internals.html
@@ -44,9 +44,6 @@ <span id="reset-status-text"></span> </div> <ul> - <li>Local Start Page Token: - <span id="account-start-page-token-local"></span> - </li> <li>Local Free Space: <span id="local-storage-freespace"></span> MB </li> @@ -56,11 +53,18 @@ <ul> <li>Push notification is enabled: <span id="push-notification-enabled"></span></li> - <li>Last update check time: - <span id="last-update-check-time"></span></li> - <li>Last update check result: - <span id="last-update-check-error"></span></li> </ul> + <table> + <tbody id="delta-update-status"> + <tr> + <th>Source</th> + <th>Start Page Token</th> + <th>Last Update Check Time</th> + <th>Last Update Check Result</th> + <th>Refreshing</th> + </tr> + </tbody> + </table> <h2 id="in-flight-operations-section">In-flight Operations</h2> <table>
diff --git a/chrome/browser/resources/chromeos/drive_internals.js b/chrome/browser/resources/chromeos/drive_internals.js index bab1c00..b606fab 100644 --- a/chrome/browser/resources/chromeos/drive_internals.js +++ b/chrome/browser/resources/chromeos/drive_internals.js
@@ -185,29 +185,30 @@ } } -/** - * Updates the local cache information about account metadata. - * @param {Object} localMetadata Dictionary describing account metadata. - */ -function updateLocalMetadata(localMetadata) { - var startPageToken = localMetadata['account-start-page-token-local']; - - $('account-start-page-token-local').textContent = startPageToken + - (startPageToken ? ' (loaded)' : ' (not loaded)') + - (localMetadata['account-metadata-refreshing'] ? ' (refreshing)' : ''); -} - -/** +/* * Updates the summary about delta update status. * @param {Object} deltaUpdateStatus Dictionary describing delta update status. */ function updateDeltaUpdateStatus(deltaUpdateStatus) { $('push-notification-enabled').textContent = deltaUpdateStatus['push-notification-enabled']; - $('last-update-check-time').textContent = - deltaUpdateStatus['last-update-check-time']; - $('last-update-check-error').textContent = - deltaUpdateStatus['last-update-check-error']; + + var itemContainer = $('delta-update-status'); + for (var i = 0; i < deltaUpdateStatus['items'].length; i++) { + var update = deltaUpdateStatus['items'][i]; + var tr = document.createElement('tr'); + tr.className = 'delta-update'; + tr.appendChild(createElementFromText('td', update.id)); + var startPageToken = update.start_page_token; + tr.appendChild(createElementFromText( + 'td', + startPageToken + (startPageToken ? ' (loaded)' : ' (not loaded)'))); + tr.appendChild(createElementFromText('td', update.last_check_time)); + tr.appendChild(createElementFromText('td', update.last_check_result)); + tr.appendChild(createElementFromText('td', update.refreshing)); + + itemContainer.appendChild(tr); + } } /**
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css index 0f46e25..bac407f 100644 --- a/chrome/browser/resources/local_ntp/local_ntp.css +++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -297,6 +297,11 @@ box-shadow: none; margin: 0 auto; max-width: 560px; + transition: background-color 300ms ease-in-out; +} + +.md #fakebox:hover { + background-color: rgb(232, 234, 237); } .non-google-page #fakebox-container {
diff --git a/chrome/browser/resources/print_preview/new/destination_list_item.js b/chrome/browser/resources/print_preview/new/destination_list_item.js index 1b644f4..a118f06f 100644 --- a/chrome/browser/resources/print_preview/new/destination_list_item.js +++ b/chrome/browser/resources/print_preview/new/destination_list_item.js
@@ -50,6 +50,10 @@ // </if> }, + hostAttributes: { + tabindex: 0, + }, + observers: [ 'onDestinationPropertiesChange_(' + 'destination.displayName, destination.isOfflineOrInvalid, ' +
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc index 0a75ec1..7632f4e4 100644 --- a/chrome/browser/search/local_ntp_source.cc +++ b/chrome/browser/search/local_ntp_source.cc
@@ -134,7 +134,9 @@ auto translated_strings = std::make_unique<base::DictionaryValue>(); AddString(translated_strings.get(), "thumbnailRemovedNotification", - IDS_NEW_TAB_THUMBNAIL_REMOVED_NOTIFICATION); + features::IsMDIconsEnabled() + ? IDS_NTP_CONFIRM_MSG_SHORTCUT_REMOVED + : IDS_NEW_TAB_THUMBNAIL_REMOVED_NOTIFICATION); AddString(translated_strings.get(), "removeThumbnailTooltip", IDS_NEW_TAB_REMOVE_THUMBNAIL_TOOLTIP); AddString(translated_strings.get(), "undoThumbnailRemove",
diff --git a/chrome/browser/signin/signin_ui_util.cc b/chrome/browser/signin/signin_ui_util.cc index e5fa40e..0359ed9 100644 --- a/chrome/browser/signin/signin_ui_util.cc +++ b/chrome/browser/signin/signin_ui_util.cc
@@ -224,11 +224,11 @@ GaiaCookieManagerService* cookie_manager_service = GaiaCookieManagerServiceFactory::GetForProfile(profile); std::vector<gaia::ListedAccount> cookie_accounts; - bool gaia_accounts_stale = !cookie_manager_service->ListAccounts( + bool cookie_accounts_valid = cookie_manager_service->ListAccounts( &cookie_accounts, nullptr, "ProfileChooserView"); UMA_HISTOGRAM_BOOLEAN("Profile.DiceUI.GaiaAccountsStale", - gaia_accounts_stale); - if (!cookie_accounts.empty()) + !cookie_accounts_valid); + if (cookie_accounts_valid && !cookie_accounts.empty()) default_account_id = cookie_accounts[0].id; }
diff --git a/chrome/browser/sync/test/integration/sync_auth_test.cc b/chrome/browser/sync/test/integration/sync_auth_test.cc index bd243ec5..fb9a746 100644 --- a/chrome/browser/sync/test/integration/sync_auth_test.cc +++ b/chrome/browser/sync/test/integration/sync_auth_test.cc
@@ -264,7 +264,8 @@ } // Verify that ProfileSyncService fetches a new token when an old token expires. -IN_PROC_BROWSER_TEST_F(SyncAuthTest, TokenExpiry) { +// Disabled due to flakiness: https://crbug.com/860200 +IN_PROC_BROWSER_TEST_F(SyncAuthTest, DISABLED_TokenExpiry) { // Initial sync succeeds with a short lived OAuth2 Token. ASSERT_TRUE(SetupClients()); GetFakeServer()->SetAuthenticated();
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc b/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc index 5cc8ed2..4e0a278 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc
@@ -29,6 +29,8 @@ AddContextMenuOption(menu_model, ash::UNINSTALL, IDS_APP_LIST_UNINSTALL_ITEM); + AddContextMenuOption(menu_model, ash::MENU_CLOSE, + IDS_CROSTINI_SHUT_DOWN_LINUX_MENU_ITEM); } } @@ -37,6 +39,10 @@ if (app_id() == kCrostiniTerminalId) { return IsCrostiniEnabled(profile()); } + } else if (command_id == ash::MENU_CLOSE) { + if (app_id() == kCrostiniTerminalId) { + return IsCrostiniRunning(profile()); + } } return app_list::AppContextMenu::IsCommandIdEnabled(command_id); } @@ -49,6 +55,14 @@ return; } break; + + case ash::MENU_CLOSE: + if (app_id() == kCrostiniTerminalId) { + crostini::CrostiniManager::GetInstance()->StopVm( + profile(), kCrostiniDefaultVmName, base::DoNothing()); + return; + } + break; } app_list::AppContextMenu::ExecuteCommand(command_id, event_flags); }
diff --git a/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.cc b/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.cc index 56624c8..a1aa1554 100644 --- a/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.cc +++ b/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.cc
@@ -8,6 +8,7 @@ #include "chrome/browser/infobars/infobar_service.h" #include "chrome/grit/generated_resources.h" #include "components/infobars/core/simple_alert_infobar_delegate.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/page_importance_signals.h" #include "ui/base/l10n/l10n_util.h" @@ -41,13 +42,21 @@ } } +void BloatedRendererTabHelper::DidStartNavigation( + content::NavigationHandle* navigation_handle) { + if (state_ == State::kRequestingReload) { + saved_navigation_id_ = navigation_handle->GetNavigationId(); + state_ = State::kStartedNavigation; + } +} + void BloatedRendererTabHelper::DidFinishNavigation( content::NavigationHandle* navigation_handle) { - // TODO(ulan): Use nagivation_handle to ensure that the finished navigation - // is the same nagivation started by reloading the bloated tab. - if (reloading_bloated_renderer_) { + if (state_ == State::kStartedNavigation && + saved_navigation_id_ == navigation_handle->GetNavigationId()) { ShowInfoBar(InfoBarService::FromWebContents(web_contents())); - reloading_bloated_renderer_ = false; + state_ = State::kInactive; + saved_navigation_id_ = 0; } } @@ -116,9 +125,12 @@ if (renderer->FastShutdownIfPossible(expected_page_count, skip_unload_handlers)) { const bool check_for_repost = true; - reloading_bloated_renderer_ = true; + // Clear the state and the saved navigation id. + state_ = State::kRequestingReload; + saved_navigation_id_ = 0; web_contents()->GetController().Reload(content::ReloadType::NORMAL, check_for_repost); + DCHECK_EQ(State::kStartedNavigation, state_); RecordBloatedRendererHandling( BloatedRendererHandlingInBrowser::kReloaded); } else {
diff --git a/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.h b/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.h index ca160fa0..290591de 100644 --- a/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.h +++ b/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.h
@@ -28,6 +28,8 @@ ~BloatedRendererTabHelper() override = default; // content::WebContentsObserver: + void DidStartNavigation( + content::NavigationHandle* navigation_handle) override; void DidFinishNavigation( content::NavigationHandle* navigation_handle) override; void WebContentsDestroyed() override; @@ -42,6 +44,8 @@ private: friend class content::WebContentsUserData<BloatedRendererTabHelper>; FRIEND_TEST_ALL_PREFIXES(BloatedRendererTabHelperTest, DetectReload); + FRIEND_TEST_ALL_PREFIXES(BloatedRendererTabHelperTest, + IgnoreUnrelatedNavigation); FRIEND_TEST_ALL_PREFIXES(BloatedRendererTabHelperTest, CanReloadBloatedTab); FRIEND_TEST_ALL_PREFIXES(BloatedRendererTabHelperTest, CannotReloadBloatedTabCrashed); @@ -49,12 +53,31 @@ CannotReloadBloatedTabInvalidURL); FRIEND_TEST_ALL_PREFIXES(BloatedRendererTabHelperTest, CannotReloadBloatedTabPendingUserInteraction); + enum class State { kInactive, kRequestingReload, kStartedNavigation }; explicit BloatedRendererTabHelper(content::WebContents* contents); bool CanReloadBloatedTab(); - bool reloading_bloated_renderer_ = false; + // The state transitions as follows: + // - kInactive is the initial state. + // + // - any state => kRequestingReload transition happens in + // OnRendererIsBloated before invoking NavigationController::Reload. + // + // - kRequestingReload => kStartedNavigation transition happens in + // NavigationController::Reload when it invokes DidStartNavigation. + // + // - kStartedNavigation => kInactive transitions happens in + // DidFinishNavigation. + State state_ = State::kInactive; + + // The navigation id is saved on DidStartNavigation event when the state is + // kRequestingReload. The infobar is shown on the subsequent + // DidFinishNavigation only if its navigation id matches the saved id. This + // ensures that the infobar is shown only for the reload that was requested + // in OnRendererIsBloated event. + int64_t saved_navigation_id_ = 0; DISALLOW_COPY_AND_ASSIGN(BloatedRendererTabHelper); };
diff --git a/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper_unittest.cc b/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper_unittest.cc index bea4fe3..123abbc3 100644 --- a/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper_unittest.cc +++ b/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper_unittest.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/common/page_importance_signals.h" #include "content/public/test/web_contents_tester.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,10 +22,39 @@ }; TEST_F(BloatedRendererTabHelperTest, DetectReload) { - EXPECT_FALSE(tab_helper_->reloading_bloated_renderer_); - tab_helper_->reloading_bloated_renderer_ = true; - tab_helper_->DidFinishNavigation(nullptr); - EXPECT_FALSE(tab_helper_->reloading_bloated_renderer_); + EXPECT_EQ(BloatedRendererTabHelper::State::kInactive, tab_helper_->state_); + tab_helper_->state_ = BloatedRendererTabHelper::State::kRequestingReload; + auto reload_navigation = + content::NavigationHandle::CreateNavigationHandleForTesting( + GURL(), web_contents()->GetMainFrame()); + tab_helper_->DidStartNavigation(reload_navigation.get()); + EXPECT_EQ(BloatedRendererTabHelper::State::kStartedNavigation, + tab_helper_->state_); + EXPECT_EQ(reload_navigation->GetNavigationId(), + tab_helper_->saved_navigation_id_); + tab_helper_->DidFinishNavigation(reload_navigation.get()); + EXPECT_EQ(BloatedRendererTabHelper::State::kInactive, tab_helper_->state_); +} + +TEST_F(BloatedRendererTabHelperTest, IgnoreUnrelatedNavigation) { + EXPECT_EQ(BloatedRendererTabHelper::State::kInactive, tab_helper_->state_); + tab_helper_->state_ = BloatedRendererTabHelper::State::kRequestingReload; + auto reload_navigation = + content::NavigationHandle::CreateNavigationHandleForTesting( + GURL(), web_contents()->GetMainFrame()); + tab_helper_->DidStartNavigation(reload_navigation.get()); + EXPECT_EQ(BloatedRendererTabHelper::State::kStartedNavigation, + tab_helper_->state_); + EXPECT_EQ(reload_navigation->GetNavigationId(), + tab_helper_->saved_navigation_id_); + auto unrelated_navigation = + content::NavigationHandle::CreateNavigationHandleForTesting( + GURL(), web_contents()->GetMainFrame()); + tab_helper_->DidFinishNavigation(unrelated_navigation.get()); + EXPECT_EQ(BloatedRendererTabHelper::State::kStartedNavigation, + tab_helper_->state_); + EXPECT_EQ(reload_navigation->GetNavigationId(), + tab_helper_->saved_navigation_id_); } TEST_F(BloatedRendererTabHelperTest, CanReloadBloatedTab) {
diff --git a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm index 768b8e7..5392dbf3 100644 --- a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm +++ b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm
@@ -16,11 +16,12 @@ static_assert(static_cast<int>(a) == static_cast<int>(b), \ "mismatching enums: " #a) -STATIC_ASSERT_ENUM(NOTIFICATION_CLICK, NotificationCommon::CLICK); -STATIC_ASSERT_ENUM(NOTIFICATION_CLOSE, NotificationCommon::CLOSE); +STATIC_ASSERT_ENUM(NOTIFICATION_CLICK, NotificationCommon::OPERATION_CLICK); +STATIC_ASSERT_ENUM(NOTIFICATION_CLOSE, NotificationCommon::OPERATION_CLOSE); STATIC_ASSERT_ENUM(NOTIFICATION_DISABLE_PERMISSION, - NotificationCommon::DISABLE_PERMISSION); -STATIC_ASSERT_ENUM(NOTIFICATION_SETTINGS, NotificationCommon::SETTINGS); + NotificationCommon::OPERATION_DISABLE_PERMISSION); +STATIC_ASSERT_ENUM(NOTIFICATION_SETTINGS, + NotificationCommon::OPERATION_SETTINGS); STATIC_ASSERT_ENUM(NOTIFICATION_OPERATION_MAX, NotificationCommon::OPERATION_MAX);
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc index 04ac214a..f544d303 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -490,7 +490,8 @@ item_id == autofill::PopupItemId::POPUP_ITEM_ID_AUTOFILL_OPTIONS || item_id == autofill::PopupItemId::POPUP_ITEM_ID_SCAN_CREDIT_CARD || item_id == - autofill::PopupItemId::POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO) { + autofill::PopupItemId::POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO || + item_id == POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY) { // This is a footer, so this suggestion will be processed later. Don't // increment |line_number|, or else it will be skipped when adding footer // rows below.
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view.cc b/chrome/browser/ui/views/crostini/crostini_installer_view.cc index 4036337..f81006f 100644 --- a/chrome/browser/ui/views/crostini/crostini_installer_view.cc +++ b/chrome/browser/ui/views/crostini/crostini_installer_view.cc
@@ -248,8 +248,24 @@ constexpr int kDialogSpacingVertical = 32; constexpr gfx::Size kLogoImageSize(32, 32); - SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout* layout = + SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::kVertical, gfx::Insets(), kDialogSpacingVertical)); + + views::View* upper_container_view = new views::View(); + upper_container_view->SetSize(gfx::Size( + kOOBEWindowWidth, kOOBEWindowHeight - kLinuxIllustrationHeight)); + upper_container_view->SetLayoutManager(std::make_unique<views::BoxLayout>( views::BoxLayout::kVertical, kDialogInsets, kDialogSpacingVertical)); + AddChildView(upper_container_view); + + views::View* lower_container_view = new views::View(); + lower_container_view->SetSize( + gfx::Size(kOOBEWindowWidth, kLinuxIllustrationHeight)); + views::BoxLayout* lower_container_layout = + lower_container_view->SetLayoutManager( + std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical)); + AddChildView(lower_container_view); logo_image_ = new views::ImageView(); logo_image_->SetImageSize(kLogoImageSize); @@ -257,7 +273,7 @@ ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( IDR_LOGO_CROSTINI_DEFAULT)); logo_image_->SetHorizontalAlignment(views::ImageView::LEADING); - AddChildView(logo_image_); + upper_container_view->AddChildView(logo_image_); const base::string16 device_type = ui::GetChromeOSDeviceName(); @@ -266,7 +282,7 @@ ash::AshTextContext::CONTEXT_HEADLINE_OVERSIZED); big_message_label_->SetMultiLine(true); big_message_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); - AddChildView(big_message_label_); + upper_container_view->AddChildView(big_message_label_); // TODO(timloh): Descenders in the message appear to be clipped, re-visit once // the UI has been fleshed out more. @@ -278,11 +294,11 @@ message_label_ = new views::Label(message); message_label_->SetMultiLine(true); message_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); - AddChildView(message_label_); + upper_container_view->AddChildView(message_label_); // Make a slot for the progress bar, but it's not initially visible. progress_bar_ = new views::ProgressBar(); - AddChildView(progress_bar_); + upper_container_view->AddChildView(progress_bar_); progress_bar_->SetVisible(false); big_image_ = new views::ImageView(); @@ -291,7 +307,12 @@ big_image_->SetImage( ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( IDR_LINUX_ILLUSTRATION)); - AddChildView(big_image_); + lower_container_view->AddChildView(big_image_); + + // Make sure the lower_container_view is pinned to the bottom of the dialog. + lower_container_layout->set_main_axis_alignment( + views::BoxLayout::MAIN_AXIS_ALIGNMENT_END); + layout->SetFlexForView(lower_container_view, 1, true); chrome::RecordDialogCreation(chrome::DialogIdentifier::CROSTINI_INSTALLER); }
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc index 501a5a5..7bce001 100644 --- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc +++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -267,7 +267,6 @@ // Cancel any pending tab transition. hover_tab_selector_.CancelTabTransition(); - tabstrip_->PrepareForCloseAt(model_index, source); model_->CloseWebContentsAt(model_index, TabStripModel::CLOSE_USER_GESTURE | TabStripModel::CLOSE_CREATE_HISTORICAL_TAB); @@ -427,7 +426,7 @@ // Cancel any pending tab transition. hover_tab_selector_.CancelTabTransition(); - tabstrip_->RemoveTabAt(contents, model_index); + tabstrip_->RemoveTabAt(contents, model_index, was_active); } void BrowserTabStripController::ActiveTabChanged(
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc index fac0e0f2..3abace6 100644 --- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc +++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
@@ -33,12 +33,14 @@ void FakeBaseTabStripController::RemoveTab(int index) { num_tabs_--; - tab_strip_->RemoveTabAt(nullptr, index); + // RemoveTabAt() expects the controller state to have been updated already. + const bool was_active = index == active_index_; if (active_index_ > index) { --active_index_; } else if (active_index_ == index) { SetActiveIndex(std::min(active_index_, num_tabs_ - 1)); } + tab_strip_->RemoveTabAt(nullptr, index, was_active); } const ui::ListSelectionModel& @@ -89,7 +91,6 @@ } void FakeBaseTabStripController::CloseTab(int index, CloseTabSource source) { - tab_strip_->PrepareForCloseAt(index, source); RemoveTab(index); }
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index 5cb9858..7f4c489 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -545,7 +545,44 @@ observer.OnTabMoved(from_model_index, to_model_index); } -void TabStrip::RemoveTabAt(content::WebContents* contents, int model_index) { +void TabStrip::RemoveTabAt(content::WebContents* contents, + int model_index, + bool was_active) { + const int model_count = GetModelCount(); + if (in_tab_close_ && model_count > 0 && model_index != model_count) { + // The user closed a tab other than the last tab. Set + // available_width_for_tabs_ so that as the user closes tabs with the mouse + // a tab continues to fall under the mouse. + int next_active_index = controller_->GetActiveIndex(); + DCHECK(IsValidModelIndex(next_active_index)); + if (model_index <= next_active_index) { + // At this point, model's internal state has already been updated. + // |contents| has been detached from model and the active index has been + // updated. But the tab for |contents| isn't removed yet. Thus, we need to + // fix up next_active_index based on it. + next_active_index++; + } + Tab* next_active_tab = tab_at(next_active_index); + Tab* tab_being_removed = tab_at(model_index); + + int size_delta = tab_being_removed->width(); + if (!tab_being_removed->data().pinned && was_active && + current_active_width_ > current_inactive_width_) { + // When removing an active, non-pinned tab, an inactive tab will be made + // active and thus given the active width. Thus the width being removed + // from the strip is really the current width of whichever inactive tab + // will be made active. + size_delta = next_active_tab->width(); + } + + available_width_for_tabs_ = ideal_bounds(model_count).right() - + TabStartX() - size_delta + Tab::GetOverlap(); + if (model_index == 0 && tab_being_removed->data().pinned && + !tab_at(1)->data().pinned) { + available_width_for_tabs_ -= GetPinnedToNonPinnedOffset(); + } + } + const bool first_unpinned_tab = model_index == GetPinnedTabCount(); if (!touch_layout_) @@ -696,54 +733,6 @@ tabstrip_right; } -void TabStrip::PrepareForCloseAt(int model_index, CloseTabSource source) { - if (!in_tab_close_ && IsAnimating()) { - // Cancel any current animations. We do this as remove uses the current - // ideal bounds and we need to know ideal bounds is in a good state. - StopAnimating(true); - } - - if (!GetWidget()) - return; - - int model_count = GetModelCount(); - if (model_count > 1 && model_index != model_count - 1) { - // The user is about to close a tab other than the last tab. Set - // available_width_for_tabs_ so that as the user closes tabs with the mouse - // a tab continues to fall under the mouse. - Tab* tab_being_removed = tab_at(model_index); - int size_delta = tab_being_removed->width(); - if (!tab_being_removed->data().pinned && tab_being_removed->IsActive()) { - // When removing an active, non-pinned tab, an inactive tab will be made - // active and thus given the active width. Thus the width being removed - // from the strip is really the current width of whichever inactive tab - // will be made active. This could be either |current_inactive_width_| or - // (current_inactive_width_ + 1) (if layout has apportioned extra space to - // the initial tabs in the strip). Unfortunately, the next active tab has - // not yet been chosen, so it's impossible to know which of these is - // correct; using the narrower value is more conservative. - // TODO(sky): https://crbug.com/856289 Refactor so this happens after - // another tab has been activated. - size_delta = current_inactive_width_; - } - - available_width_for_tabs_ = ideal_bounds(model_count - 1).right() - - TabStartX() - size_delta + Tab::GetOverlap(); - if (model_index == 0 && tab_being_removed->data().pinned && - !tab_at(1)->data().pinned) { - available_width_for_tabs_ -= GetPinnedToNonPinnedOffset(); - } - } - - in_tab_close_ = true; - resize_layout_timer_.Stop(); - if (source == CLOSE_TAB_FROM_TOUCH) { - StartResizeLayoutTabsFromTouchTimer(); - } else { - AddMessageLoopObserver(); - } -} - void TabStrip::SetSelection(const ui::ListSelectionModel& new_selection) { if (selected_tabs_.active() != new_selection.active()) { if (selected_tabs_.active() >= 0) @@ -923,18 +912,33 @@ } void TabStrip::CloseTab(Tab* tab, CloseTabSource source) { + int model_index = GetModelIndexOfTab(tab); if (tab->closing()) { // If the tab is already closing, close the next tab. We do this so that the // user can rapidly close tabs by clicking the close button and not have // the animations interfere with that. - const int closed_tab_index = FindClosingTab(tab).first->first; - if (closed_tab_index < GetModelCount()) - controller_->CloseTab(closed_tab_index, source); - return; + model_index = FindClosingTab(tab).first->first; } - int model_index = GetModelIndexOfTab(tab); - if (IsValidModelIndex(model_index)) - controller_->CloseTab(model_index, source); + + if (!IsValidModelIndex(model_index)) + return; + + if (!in_tab_close_ && IsAnimating()) { + // Cancel any current animations. We do this as remove uses the current + // ideal bounds and we need to know ideal bounds is in a good state. + StopAnimating(true); + } + + if (GetWidget()) { + in_tab_close_ = true; + resize_layout_timer_.Stop(); + if (source == CLOSE_TAB_FROM_TOUCH) + StartResizeLayoutTabsFromTouchTimer(); + else + AddMessageLoopObserver(); + } + + controller_->CloseTab(model_index, source); } void TabStrip::ToggleTabAudioMute(Tab* tab) {
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h index db3b95a..21cfc30 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.h +++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -155,7 +155,9 @@ // Removes a tab at the specified index. If the tab with |contents| is being // dragged then the drag is completed. - void RemoveTabAt(content::WebContents* contents, int model_index); + void RemoveTabAt(content::WebContents* contents, + int model_index, + bool was_active); // Sets the tab data at the specified model index. void SetTabData(int model_index, TabRendererData data); @@ -167,12 +169,6 @@ // to clip). bool ShouldTabBeVisible(const Tab* tab) const; - // Invoked from the controller when the close initiates from the TabController - // (the user clicked the tab close button or middle clicked the tab). This is - // invoked from Close. Because of unload handlers Close is not always - // immediately followed by RemoveTabAt. - void PrepareForCloseAt(int model_index, CloseTabSource source); - // Invoked when the selection is updated. void SetSelection(const ui::ListSelectionModel& new_selection);
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc index 2c21697..96bb982 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc +++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -252,7 +252,7 @@ } TEST_P(TabStripTest, AccessibilityEvents) { - // When adding tabs, SetSelection() is called after RemoveTabAt(), as + // When adding tabs, SetSelection() is called after AddTabAt(), as // otherwise the index would not be meaningful. tab_strip_->AddTabAt(0, TabRendererData(), false); tab_strip_->AddTabAt(1, TabRendererData(), true); @@ -266,7 +266,7 @@ // otherwise the index would not be meaningful. selection.SetSelectedIndex(0); tab_strip_->SetSelection(selection); - tab_strip_->RemoveTabAt(nullptr, 1); + tab_strip_->RemoveTabAt(nullptr, 1, true); EXPECT_EQ(2, test_views_delegate_->add_count()); EXPECT_EQ(1, test_views_delegate_->remove_count()); }
diff --git a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc index ea895ff..1eb51520 100644 --- a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc +++ b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -246,8 +246,6 @@ drive::DriveServiceInterface* drive_service); void UpdateAppListSection( drive::DriveServiceInterface* drive_service); - void UpdateLocalMetadataSection( - drive::DebugInfoCollector* debug_info_collector); void UpdateDeltaUpdateStatusSection( drive::DebugInfoCollector* debug_info_collector); void UpdateInFlightOperationsSection(drive::JobListInterface* job_list); @@ -290,10 +288,6 @@ void OnGetAppList(google_apis::DriveApiErrorCode status, std::unique_ptr<google_apis::AppList> app_list); - // Callback for DebugInfoCollector::GetMetadata for local update. - void OnGetFilesystemMetadataForLocal( - const drive::FileSystemMetadata& metadata); - // Callback for DebugInfoCollector::GetMetadata for delta update. void OnGetFilesystemMetadataForDeltaUpdate( const drive::FileSystemMetadata& metadata); @@ -450,7 +444,6 @@ drive::DebugInfoCollector* debug_info_collector = integration_service->debug_info_collector(); if (debug_info_collector) { - UpdateLocalMetadataSection(debug_info_collector); UpdateDeltaUpdateStatusSection(debug_info_collector); UpdateCacheContentsSection(debug_info_collector); } @@ -547,27 +540,6 @@ weak_ptr_factory_.GetWeakPtr())); } -void DriveInternalsWebUIHandler::UpdateLocalMetadataSection( - drive::DebugInfoCollector* debug_info_collector) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(debug_info_collector); - - debug_info_collector->GetMetadata( - base::Bind(&DriveInternalsWebUIHandler::OnGetFilesystemMetadataForLocal, - weak_ptr_factory_.GetWeakPtr())); -} - -void DriveInternalsWebUIHandler::OnGetFilesystemMetadataForLocal( - const drive::FileSystemMetadata& metadata) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - base::DictionaryValue local_metadata; - local_metadata.SetString("account-start-page-token-local", - metadata.start_page_token); - local_metadata.SetBoolean("account-metadata-refreshing", metadata.refreshing); - web_ui()->CallJavascriptFunctionUnsafe("updateLocalMetadata", local_metadata); -} - void DriveInternalsWebUIHandler::ClearAccessToken(const base::ListValue* args) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -636,13 +608,24 @@ delta_update_status.SetBoolean( "push-notification-enabled", drive_notification_manager->push_notification_enabled()); - delta_update_status.SetString( - "last-update-check-time", - google_apis::util::FormatTimeAsStringLocaltime( - metadata.last_update_check_time)); - delta_update_status.SetString( - "last-update-check-error", + + auto items = std::make_unique<base::ListValue>(); + // Users default corpus first. + auto app_data = std::make_unique<base::DictionaryValue>(); + app_data->SetString("id", "default corpus"); + app_data->SetString("start_page_token", metadata.start_page_token); + app_data->SetString("last_check_time", + google_apis::util::FormatTimeAsStringLocaltime( + metadata.last_update_check_time)); + app_data->SetString( + "last_check_result", drive::FileErrorToString(metadata.last_update_check_error)); + app_data->SetString("refreshing", metadata.refreshing ? "Yes" : "No"); + + items->Append(std::move(app_data)); + + // TODO(slangley): Add data for each team drive. + delta_update_status.Set("items", std::move(items)); web_ui()->CallJavascriptFunctionUnsafe("updateDeltaUpdateStatus", delta_update_status);
diff --git a/chrome/browser/ui/webui/signin/login_ui_service_unittest.cc b/chrome/browser/ui/webui/signin/login_ui_service_unittest.cc index 9948ae4..23cd90f 100644 --- a/chrome/browser/ui/webui/signin/login_ui_service_unittest.cc +++ b/chrome/browser/ui/webui/signin/login_ui_service_unittest.cc
@@ -16,12 +16,15 @@ #if !defined(OS_CHROMEOS) #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/test_extension_system.h" #include "chrome/browser/profiles/profile_attributes_entry.h" #include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/browser_with_test_window_test.h" #include "content/public/browser/web_contents.h" +#include "extensions/common/extension_builder.h" #endif class LoginUIServiceTest : public testing::Test { @@ -112,6 +115,11 @@ }; TEST_F(LoginUIServiceLoginPopupTest, ShowLoginPop) { + extensions::TestExtensionSystem* extension_system = + static_cast<extensions::TestExtensionSystem*>( + extensions::ExtensionSystem::Get(profile())); + extension_system->CreateExtensionService( + base::CommandLine::ForCurrentProcess(), base::FilePath(), false); service_->ShowLoginPopup(); EXPECT_EQ(1, model_->count()); }
diff --git a/chrome/browser/unified_consent/chrome_unified_consent_service_client.h b/chrome/browser/unified_consent/chrome_unified_consent_service_client.h index fb212f4..58848294 100644 --- a/chrome/browser/unified_consent/chrome_unified_consent_service_client.h +++ b/chrome/browser/unified_consent/chrome_unified_consent_service_client.h
@@ -10,7 +10,8 @@ class PrefService; -class ChromeUnifiedConsentServiceClient : public UnifiedConsentServiceClient { +class ChromeUnifiedConsentServiceClient + : public unified_consent::UnifiedConsentServiceClient { public: explicit ChromeUnifiedConsentServiceClient(PrefService* pref_service); ~ChromeUnifiedConsentServiceClient() override = default;
diff --git a/chrome/browser/unified_consent/unified_consent_service_factory.cc b/chrome/browser/unified_consent/unified_consent_service_factory.cc index 2c61a60..b47208c 100644 --- a/chrome/browser/unified_consent/unified_consent_service_factory.cc +++ b/chrome/browser/unified_consent/unified_consent_service_factory.cc
@@ -27,9 +27,9 @@ UnifiedConsentServiceFactory::~UnifiedConsentServiceFactory() = default; // static -UnifiedConsentService* UnifiedConsentServiceFactory::GetForProfile( - Profile* profile) { - return static_cast<UnifiedConsentService*>( +unified_consent::UnifiedConsentService* +UnifiedConsentServiceFactory::GetForProfile(Profile* profile) { + return static_cast<unified_consent::UnifiedConsentService*>( GetInstance()->GetServiceForBrowserContext(profile, true)); } @@ -40,7 +40,7 @@ void UnifiedConsentServiceFactory::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { - UnifiedConsentService::RegisterPrefs(registry); + unified_consent::UnifiedConsentService::RegisterPrefs(registry); } KeyedService* UnifiedConsentServiceFactory::BuildServiceInstanceFor( @@ -50,7 +50,7 @@ if (!IsUnifiedConsentEnabled(profile)) return nullptr; - return new UnifiedConsentService( + return new unified_consent::UnifiedConsentService( new ChromeUnifiedConsentServiceClient(profile->GetPrefs()), profile->GetPrefs(), IdentityManagerFactory::GetForProfile(profile), ProfileSyncServiceFactory::GetSyncServiceForBrowserContext(profile));
diff --git a/chrome/browser/unified_consent/unified_consent_service_factory.h b/chrome/browser/unified_consent/unified_consent_service_factory.h index 57507521..1f453f6 100644 --- a/chrome/browser/unified_consent/unified_consent_service_factory.h +++ b/chrome/browser/unified_consent/unified_consent_service_factory.h
@@ -10,7 +10,9 @@ #include "components/keyed_service/content/browser_context_keyed_service_factory.h" class Profile; +namespace unified_consent { class UnifiedConsentService; +} class UnifiedConsentServiceFactory : public BrowserContextKeyedServiceFactory { public: @@ -18,7 +20,8 @@ // (creating one if none exists). Returns nullptr if this profile cannot have // a UnifiedConsentService (e.g. UnifiedConsent is not enabled for |profile| // or |profile| is incognito). - static UnifiedConsentService* GetForProfile(Profile* profile); + static unified_consent::UnifiedConsentService* GetForProfile( + Profile* profile); // Returns an instance of the factory singleton. static UnifiedConsentServiceFactory* GetInstance();
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn index 55c4d1a..df2aafaf 100644 --- a/chrome/browser/vr/BUILD.gn +++ b/chrome/browser/vr/BUILD.gn
@@ -136,6 +136,8 @@ "gesture_detector.h", "graphics_delegate.cc", "graphics_delegate.h", + "input_event.cc", + "input_event.h", "keyboard_delegate.h", "keyboard_ui_interface.h", "macros.h", @@ -308,6 +310,7 @@ "fps_meter_unittest.cc", "gesture_detector_unittest.cc", "model/text_input_info_unittest.cc", + "platform_ui_input_delegate_unittest.cc", "pose_util_unittest.cc", "service/vr_device_manager_unittest.cc", "sliding_average_unittest.cc",
diff --git a/chrome/browser/vr/content_input_delegate.cc b/chrome/browser/vr/content_input_delegate.cc index c5dbdfd..fc144651c 100644 --- a/chrome/browser/vr/content_input_delegate.cc +++ b/chrome/browser/vr/content_input_delegate.cc
@@ -8,8 +8,6 @@ #include "base/time/time.h" #include "chrome/browser/vr/platform_controller.h" #include "chrome/browser/vr/platform_input_handler.h" -#include "third_party/blink/public/platform/web_gesture_event.h" -#include "third_party/blink/public/platform/web_mouse_event.h" namespace vr { @@ -48,26 +46,21 @@ } void ContentInputDelegate::SendGestureToTarget( - std::unique_ptr<blink::WebInputEvent> event) { - if (!event || !input_handler() || ContentGestureIsLocked(event->GetType())) + std::unique_ptr<InputEvent> event) { + if (!event || !input_handler() || ContentGestureIsLocked(event->type())) return; input_handler()->ForwardEventToContent(std::move(event), content_id_); } -bool ContentInputDelegate::ContentGestureIsLocked( - blink::WebInputEvent::Type type) { - // TODO (asimjour) create a new MouseEnter event when we swap webcontents and +bool ContentInputDelegate::ContentGestureIsLocked(InputEvent::Type type) { + // TODO (asimjour) create a new HoverEnter event when we swap webcontents and // pointer is on the content quad. - if (type == blink::WebInputEvent::kGestureScrollBegin || - type == blink::WebInputEvent::kMouseMove || - type == blink::WebInputEvent::kMouseDown || - type == blink::WebInputEvent::kMouseEnter) + if (type == InputEvent::kScrollBegin || type == InputEvent::kHoverMove || + type == InputEvent::kButtonDown || type == InputEvent::kHoverEnter) locked_content_id_ = content_id_; - if (locked_content_id_ != content_id_) - return true; - return false; + return locked_content_id_ != content_id_; } void ContentInputDelegate::OnWebInputIndicesChanged(
diff --git a/chrome/browser/vr/content_input_delegate.h b/chrome/browser/vr/content_input_delegate.h index eca2763..3f2ad8a 100644 --- a/chrome/browser/vr/content_input_delegate.h +++ b/chrome/browser/vr/content_input_delegate.h
@@ -16,7 +16,6 @@ #include "chrome/browser/vr/platform_ui_input_delegate.h" #include "chrome/browser/vr/text_edit_action.h" #include "chrome/browser/vr/vr_export.h" -#include "third_party/blink/public/platform/web_input_event.h" namespace vr { @@ -54,8 +53,7 @@ void ClearTextInputState(); protected: - void SendGestureToTarget( - std::unique_ptr<blink::WebInputEvent> event) override; + void SendGestureToTarget(std::unique_ptr<InputEvent> event) override; private: enum TextRequestState { @@ -63,7 +61,7 @@ kRequested, kResponseReceived, }; - bool ContentGestureIsLocked(blink::WebInputEvent::Type type); + bool ContentGestureIsLocked(InputEvent::Type type); void OnWebInputTextChanged(const base::string16& text); int content_id_ = 0;
diff --git a/chrome/browser/vr/elements/content_element.cc b/chrome/browser/vr/elements/content_element.cc index e41b43b5..01bbf156 100644 --- a/chrome/browser/vr/elements/content_element.cc +++ b/chrome/browser/vr/elements/content_element.cc
@@ -9,7 +9,6 @@ #include "chrome/browser/vr/text_input_delegate.h" #include "chrome/browser/vr/ui_scene_constants.h" #include "chrome/browser/vr/vr_gl_util.h" -#include "third_party/blink/public/platform/web_gesture_event.h" #include "ui/gfx/geometry/rect_f.h" namespace vr {
diff --git a/chrome/browser/vr/elements/content_element_unittest.cc b/chrome/browser/vr/elements/content_element_unittest.cc index df73ba4..7326748 100644 --- a/chrome/browser/vr/elements/content_element_unittest.cc +++ b/chrome/browser/vr/elements/content_element_unittest.cc
@@ -17,7 +17,6 @@ #include "chrome/browser/vr/text_edit_action.h" #include "chrome/browser/vr/ui_scene.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/public/platform/web_input_event.h" using ::testing::_; using ::testing::InSequence; @@ -53,10 +52,8 @@ TestPlatformInputHandler() {} ~TestPlatformInputHandler() override {} - void ForwardEventToPlatformUi( - std::unique_ptr<blink::WebInputEvent>) override {} - void ForwardEventToContent(std::unique_ptr<blink::WebInputEvent>, - int) override {} + void ForwardEventToPlatformUi(std::unique_ptr<InputEvent>) override {} + void ForwardEventToContent(std::unique_ptr<InputEvent>, int) override {} void ClearFocusedElement() override { clear_focus_called_ = true; } void OnWebInputEdited(const TextEdits& edits) override { edits_ = edits; }
diff --git a/chrome/browser/vr/elements/paged_scroll_view.cc b/chrome/browser/vr/elements/paged_scroll_view.cc index ace45fa4..1cca5fe 100644 --- a/chrome/browser/vr/elements/paged_scroll_view.cc +++ b/chrome/browser/vr/elements/paged_scroll_view.cc
@@ -4,8 +4,8 @@ #include "chrome/browser/vr/elements/paged_scroll_view.h" +#include "chrome/browser/vr/input_event.h" #include "chrome/browser/vr/target_property.h" -#include "third_party/blink/public/platform/web_gesture_event.h" namespace vr { @@ -23,23 +23,19 @@ PagedScrollView::~PagedScrollView() {} -void PagedScrollView::OnScrollBegin( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { +void PagedScrollView::OnScrollBegin(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { animation().RemoveKeyframeModelsWithProperty(SCROLL_OFFSET); scroll_drag_delta_ = 0.0f; } -void PagedScrollView::OnScrollUpdate( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { - scroll_drag_delta_ += - gesture->data.scroll_update.delta_x * kScrollScaleFactor; +void PagedScrollView::OnScrollUpdate(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { + scroll_drag_delta_ += gesture->scroll_data.delta_x * kScrollScaleFactor; } -void PagedScrollView::OnScrollEnd( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { +void PagedScrollView::OnScrollEnd(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { size_t next_page = current_page_; if (next_page + 1 < NumPages() && scroll_drag_delta_ < -kScrollThreshold) { next_page++;
diff --git a/chrome/browser/vr/elements/paged_scroll_view.h b/chrome/browser/vr/elements/paged_scroll_view.h index d6102ef..0c2506b9 100644 --- a/chrome/browser/vr/elements/paged_scroll_view.h +++ b/chrome/browser/vr/elements/paged_scroll_view.h
@@ -19,11 +19,11 @@ ~PagedScrollView() override; // UiElement overrides. - void OnScrollBegin(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnScrollBegin(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; - void OnScrollUpdate(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnScrollUpdate(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; - void OnScrollEnd(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnScrollEnd(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; void NotifyClientFloatAnimated(float value, int target_property_id,
diff --git a/chrome/browser/vr/elements/platform_ui_element.cc b/chrome/browser/vr/elements/platform_ui_element.cc index b0a3fdb..19dd704b 100644 --- a/chrome/browser/vr/elements/platform_ui_element.cc +++ b/chrome/browser/vr/elements/platform_ui_element.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/vr/elements/content_element.h" #include "chrome/browser/vr/platform_ui_input_delegate.h" -#include "third_party/blink/public/platform/web_gesture_event.h" #include "ui/gfx/geometry/rect_f.h" namespace vr { @@ -61,32 +60,28 @@ delegate_->OnTouchMove(position, timestamp); } -void PlatformUiElement::OnFlingCancel( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { +void PlatformUiElement::OnFlingCancel(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { if (delegate_) - delegate_->OnFlingCancel(std::move(gesture), position); + delegate_->OnInputEvent(std::move(gesture), position); } -void PlatformUiElement::OnScrollBegin( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { +void PlatformUiElement::OnScrollBegin(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { if (delegate_) - delegate_->OnScrollBegin(std::move(gesture), position); + delegate_->OnInputEvent(std::move(gesture), position); } -void PlatformUiElement::OnScrollUpdate( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { +void PlatformUiElement::OnScrollUpdate(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { if (delegate_) - delegate_->OnScrollUpdate(std::move(gesture), position); + delegate_->OnInputEvent(std::move(gesture), position); } -void PlatformUiElement::OnScrollEnd( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { +void PlatformUiElement::OnScrollEnd(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { if (delegate_) - delegate_->OnScrollEnd(std::move(gesture), position); + delegate_->OnInputEvent(std::move(gesture), position); } void PlatformUiElement::SetTextureId(unsigned int texture_id) {
diff --git a/chrome/browser/vr/elements/platform_ui_element.h b/chrome/browser/vr/elements/platform_ui_element.h index 425bf0d..62d24140 100644 --- a/chrome/browser/vr/elements/platform_ui_element.h +++ b/chrome/browser/vr/elements/platform_ui_element.h
@@ -33,13 +33,13 @@ base::TimeTicks timestamp) override; void OnTouchMove(const gfx::PointF& position, base::TimeTicks timestamp) override; - void OnFlingCancel(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnFlingCancel(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; - void OnScrollBegin(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnScrollBegin(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; - void OnScrollUpdate(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnScrollUpdate(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; - void OnScrollEnd(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnScrollEnd(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; void Render(UiElementRenderer* renderer,
diff --git a/chrome/browser/vr/elements/scrollable_element.cc b/chrome/browser/vr/elements/scrollable_element.cc index 14858924..60be192 100644 --- a/chrome/browser/vr/elements/scrollable_element.cc +++ b/chrome/browser/vr/elements/scrollable_element.cc
@@ -5,7 +5,7 @@ #include "chrome/browser/vr/elements/scrollable_element.h" #include "base/numerics/ranges.h" -#include "third_party/blink/public/platform/web_gesture_event.h" +#include "chrome/browser/vr/input_event.h" namespace vr { @@ -98,30 +98,26 @@ inner_element_->AddChild(std::move(child)); } -void ScrollableElement::OnScrollBegin( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { +void ScrollableElement::OnScrollBegin(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { cached_transition_ = animation().transition(); animation().set_transition(Transition()); } -void ScrollableElement::OnScrollUpdate( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { - const auto& update = gesture->data.scroll_update; +void ScrollableElement::OnScrollUpdate(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { float half_scroll_span = ComputeScrollSpan() / 2.0f; if (orientation_ == kHorizontal) { - scroll_offset_ -= update.delta_x * kScrollScaleFactor; + scroll_offset_ -= gesture->scroll_data.delta_x * kScrollScaleFactor; } else { - scroll_offset_ -= update.delta_y * kScrollScaleFactor; + scroll_offset_ -= gesture->scroll_data.delta_y * kScrollScaleFactor; } scroll_offset_ = base::ClampToRange(scroll_offset_, -half_scroll_span, half_scroll_span); } -void ScrollableElement::OnScrollEnd( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& position) { +void ScrollableElement::OnScrollEnd(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& position) { animation().set_transition(cached_transition_); }
diff --git a/chrome/browser/vr/elements/scrollable_element.h b/chrome/browser/vr/elements/scrollable_element.h index 0c2b5ba..5141a36 100644 --- a/chrome/browser/vr/elements/scrollable_element.h +++ b/chrome/browser/vr/elements/scrollable_element.h
@@ -39,11 +39,11 @@ void SetScrollAnchoring(LayoutAlignment anchoring); - void OnScrollBegin(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnScrollBegin(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; - void OnScrollUpdate(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnScrollUpdate(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; - void OnScrollEnd(std::unique_ptr<blink::WebGestureEvent> gesture, + void OnScrollEnd(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) override; private:
diff --git a/chrome/browser/vr/elements/scrollable_element_unittest.cc b/chrome/browser/vr/elements/scrollable_element_unittest.cc index 16ee6c4..afaacb79 100644 --- a/chrome/browser/vr/elements/scrollable_element_unittest.cc +++ b/chrome/browser/vr/elements/scrollable_element_unittest.cc
@@ -6,19 +6,17 @@ #include "base/stl_util.h" #include "cc/test/geometry_test_utils.h" +#include "chrome/browser/vr/input_event.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/web_gesture_event.h" namespace vr { namespace { -std::unique_ptr<blink::WebGestureEvent> CreateScrollUpdate(float delta_x, - float delta_y) { - auto gesture = std::make_unique<blink::WebGestureEvent>(); - gesture->SetType(blink::WebGestureEvent::kGestureScrollBegin); - gesture->data.scroll_update.delta_x = delta_x; - gesture->data.scroll_update.delta_y = delta_y; +std::unique_ptr<InputEvent> CreateScrollUpdate(float delta_x, float delta_y) { + auto gesture = std::make_unique<InputEvent>(InputEvent::kScrollBegin); + gesture->scroll_data.delta_x = delta_x; + gesture->scroll_data.delta_y = delta_y; return gesture; }
diff --git a/chrome/browser/vr/elements/text_input_unittest.cc b/chrome/browser/vr/elements/text_input_unittest.cc index b2d16695..99a6871 100644 --- a/chrome/browser/vr/elements/text_input_unittest.cc +++ b/chrome/browser/vr/elements/text_input_unittest.cc
@@ -22,7 +22,6 @@ #include "chrome/browser/vr/ui_scene_constants.h" #include "chrome/browser/vr/ui_scene_creator.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/public/platform/web_gesture_event.h" #include "ui/gfx/render_text.h" using ::testing::_;
diff --git a/chrome/browser/vr/elements/ui_element.cc b/chrome/browser/vr/elements/ui_element.cc index d97e8f1..59efded 100644 --- a/chrome/browser/vr/elements/ui_element.cc +++ b/chrome/browser/vr/elements/ui_element.cc
@@ -12,9 +12,9 @@ #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" +#include "chrome/browser/vr/input_event.h" #include "chrome/browser/vr/model/camera_model.h" #include "chrome/browser/vr/vr_gl_util.h" -#include "third_party/blink/public/platform/web_gesture_event.h" #include "third_party/skia/include/core/SkRRect.h" #include "third_party/skia/include/core/SkRect.h" #include "ui/gfx/geometry/angle_conversions.h" @@ -225,13 +225,13 @@ } } -void UiElement::OnFlingCancel(std::unique_ptr<blink::WebGestureEvent> gesture, +void UiElement::OnFlingCancel(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) {} -void UiElement::OnScrollBegin(std::unique_ptr<blink::WebGestureEvent> gesture, +void UiElement::OnScrollBegin(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) {} -void UiElement::OnScrollUpdate(std::unique_ptr<blink::WebGestureEvent> gesture, +void UiElement::OnScrollUpdate(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) {} -void UiElement::OnScrollEnd(std::unique_ptr<blink::WebGestureEvent> gesture, +void UiElement::OnScrollEnd(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position) {} void UiElement::OnFocusChanged(bool focused) {
diff --git a/chrome/browser/vr/elements/ui_element.h b/chrome/browser/vr/elements/ui_element.h index 877e0066..b121e2ff 100644 --- a/chrome/browser/vr/elements/ui_element.h +++ b/chrome/browser/vr/elements/ui_element.h
@@ -38,15 +38,12 @@ class TimeTicks; } -namespace blink { -class WebGestureEvent; -} - namespace vr { class KeyframeModel; class SkiaSurfaceProvider; class UiElementRenderer; +class InputEvent; struct CameraModel; struct EditedText; @@ -160,13 +157,13 @@ base::TimeTicks timestamp); virtual void OnTouchMove(const gfx::PointF& position, base::TimeTicks timestamp); - virtual void OnFlingCancel(std::unique_ptr<blink::WebGestureEvent> gesture, + virtual void OnFlingCancel(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position); - virtual void OnScrollBegin(std::unique_ptr<blink::WebGestureEvent> gesture, + virtual void OnScrollBegin(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position); - virtual void OnScrollUpdate(std::unique_ptr<blink::WebGestureEvent> gesture, + virtual void OnScrollUpdate(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position); - virtual void OnScrollEnd(std::unique_ptr<blink::WebGestureEvent> gesture, + virtual void OnScrollEnd(std::unique_ptr<InputEvent> gesture, const gfx::PointF& position); // Whether the point (relative to the origin of the element), should be
diff --git a/chrome/browser/vr/gesture_detector.cc b/chrome/browser/vr/gesture_detector.cc index acaa914e..ee7a321 100644 --- a/chrome/browser/vr/gesture_detector.cc +++ b/chrome/browser/vr/gesture_detector.cc
@@ -5,7 +5,7 @@ #include "chrome/browser/vr/gesture_detector.h" #include "base/numerics/math_constants.h" -#include "third_party/blink/public/platform/web_gesture_event.h" +#include "chrome/browser/vr/input_event.h" namespace vr { @@ -39,7 +39,7 @@ } GestureDetector::~GestureDetector() = default; -std::unique_ptr<GestureList> GestureDetector::DetectGestures( +std::unique_ptr<InputEventList> GestureDetector::DetectGestures( const TouchInfo& input_touch_info, base::TimeTicks current_timestamp, bool force_cancel) { @@ -49,51 +49,57 @@ if (touch_position_changed_) UpdateOverallVelocity(touch_info); - auto gesture_list = std::make_unique<GestureList>(); + auto gesture_list = std::make_unique<InputEventList>(); auto gesture = GetGestureFromTouchInfo(touch_info, force_cancel); - gesture->SetSourceDevice(blink::kWebGestureDeviceTouchpad); - if (gesture->GetType() == blink::WebInputEvent::kGestureScrollEnd) + if (!gesture) + return gesture_list; + + if (gesture->type() == InputEvent::kScrollEnd) Reset(); - if (gesture->GetType() != blink::WebInputEvent::kUndefined) + if (gesture->type() != InputEvent::kTypeUndefined) gesture_list->push_back(std::move(gesture)); + return gesture_list; } -std::unique_ptr<blink::WebGestureEvent> -GestureDetector::GetGestureFromTouchInfo(const TouchInfo& touch_info, - bool force_cancel) { - auto gesture = std::make_unique<blink::WebGestureEvent>(); - gesture->SetTimeStamp(touch_info.touch_point.timestamp); +std::unique_ptr<InputEvent> GestureDetector::GetGestureFromTouchInfo( + const TouchInfo& touch_info, + bool force_cancel) { + std::unique_ptr<InputEvent> gesture; switch (state_->label) { // User has not put finger on touch pad. case WAITING: - HandleWaitingState(touch_info, gesture.get()); + gesture = HandleWaitingState(touch_info); break; // User has not started a gesture (by moving out of slop). case TOUCHING: - HandleDetectingState(touch_info, force_cancel, gesture.get()); + gesture = HandleDetectingState(touch_info, force_cancel); break; // User is scrolling on touchpad case SCROLLING: - HandleScrollingState(touch_info, force_cancel, gesture.get()); + gesture = HandleScrollingState(touch_info, force_cancel); break; // The user has finished scrolling, but we'll hallucinate a few points // before really finishing. case POST_SCROLL: - HandlePostScrollingState(touch_info, force_cancel, gesture.get()); + gesture = HandlePostScrollingState(touch_info, force_cancel); break; default: NOTREACHED(); break; } + + if (gesture) + gesture->set_time_stamp(touch_info.touch_point.timestamp); + return gesture; } -void GestureDetector::HandleWaitingState(const TouchInfo& touch_info, - blink::WebGestureEvent* gesture) { +std::unique_ptr<InputEvent> GestureDetector::HandleWaitingState( + const TouchInfo& touch_info) { // User puts finger on touch pad (or when the touch down for current gesture // is missed, initiate gesture from current touch point). if (touch_info.touch_down || touch_info.is_touching) { @@ -103,18 +109,18 @@ state_->cur_touch_point = touch_info.touch_point; state_->label = TOUCHING; - gesture->SetType(blink::WebInputEvent::kGestureFlingCancel); - gesture->data.fling_cancel.prevent_boosting = false; + return std::make_unique<InputEvent>(InputEvent::kFlingCancel); } + return nullptr; } -void GestureDetector::HandleDetectingState(const TouchInfo& touch_info, - bool force_cancel, - blink::WebGestureEvent* gesture) { +std::unique_ptr<InputEvent> GestureDetector::HandleDetectingState( + const TouchInfo& touch_info, + bool force_cancel) { // User lifts up finger from touch pad. if (touch_info.touch_up || !touch_info.is_touching) { Reset(); - return; + return nullptr; } // Touch position is changed, the touch point moves outside of slop, @@ -122,54 +128,51 @@ if (touch_position_changed_ && touch_info.is_touching && !InSlop(touch_info.touch_point.position) && !force_cancel) { state_->label = SCROLLING; - gesture->SetType(blink::WebInputEvent::kGestureScrollBegin); + auto gesture = std::make_unique<InputEvent>(InputEvent::kScrollBegin); UpdateGestureParameters(touch_info); - gesture->data.scroll_begin.delta_x_hint = - state_->displacement.x() * kDisplacementScaleFactor; - gesture->data.scroll_begin.delta_y_hint = - state_->displacement.y() * kDisplacementScaleFactor; - gesture->data.scroll_begin.delta_hint_units = - blink::WebGestureEvent::ScrollUnits::kPrecisePixels; + UpdateGestureWithScrollDelta(gesture.get()); + return gesture; } + return nullptr; } -void GestureDetector::HandleScrollingState(const TouchInfo& touch_info, - bool force_cancel, - blink::WebGestureEvent* gesture) { +std::unique_ptr<InputEvent> GestureDetector::HandleScrollingState( + const TouchInfo& touch_info, + bool force_cancel) { if (force_cancel) { - gesture->SetType(blink::WebInputEvent::kGestureScrollEnd); UpdateGestureParameters(touch_info); - return; + return std::make_unique<InputEvent>(InputEvent::kScrollEnd); } if (touch_info.touch_up || !(touch_info.is_touching)) { state_->label = POST_SCROLL; } if (touch_position_changed_) { - gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate); + auto gesture = std::make_unique<InputEvent>(InputEvent::kScrollUpdate); UpdateGestureParameters(touch_info); - UpdateGestureWithScrollDelta(gesture); + UpdateGestureWithScrollDelta(gesture.get()); + return gesture; } + return nullptr; } -void GestureDetector::HandlePostScrollingState( +std::unique_ptr<InputEvent> GestureDetector::HandlePostScrollingState( const TouchInfo& touch_info, - bool force_cancel, - blink::WebGestureEvent* gesture) { + bool force_cancel) { if (extrapolated_touch_ == 0 || force_cancel) { - gesture->SetType(blink::WebInputEvent::kGestureScrollEnd); UpdateGestureParameters(touch_info); + return std::make_unique<InputEvent>(InputEvent::kScrollEnd); } else { - gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate); + auto gesture = std::make_unique<InputEvent>(InputEvent::kScrollUpdate); UpdateGestureParameters(touch_info); - UpdateGestureWithScrollDelta(gesture); + UpdateGestureWithScrollDelta(gesture.get()); + return gesture; } } -void GestureDetector::UpdateGestureWithScrollDelta( - blink::WebGestureEvent* gesture) { - gesture->data.scroll_update.delta_x = +void GestureDetector::UpdateGestureWithScrollDelta(InputEvent* gesture) { + gesture->scroll_data.delta_x = state_->displacement.x() * kDisplacementScaleFactor; - gesture->data.scroll_update.delta_y = + gesture->scroll_data.delta_y = state_->displacement.y() * kDisplacementScaleFactor; }
diff --git a/chrome/browser/vr/gesture_detector.h b/chrome/browser/vr/gesture_detector.h index 07df080..16579452 100644 --- a/chrome/browser/vr/gesture_detector.h +++ b/chrome/browser/vr/gesture_detector.h
@@ -12,13 +12,11 @@ #include "chrome/browser/vr/vr_export.h" #include "ui/gfx/geometry/vector2d_f.h" -namespace blink { -class WebGestureEvent; -} - namespace vr { -using GestureList = std::vector<std::unique_ptr<blink::WebGestureEvent>>; +class InputEvent; + +using InputEventList = std::vector<std::unique_ptr<InputEvent>>; struct TouchPoint { gfx::Vector2dF position; @@ -37,9 +35,10 @@ GestureDetector(); virtual ~GestureDetector(); - std::unique_ptr<GestureList> DetectGestures(const TouchInfo& touch_info, - base::TimeTicks current_timestamp, - bool force_cancel); + std::unique_ptr<InputEventList> DetectGestures( + const TouchInfo& touch_info, + base::TimeTicks current_timestamp, + bool force_cancel); private: enum GestureDetectorStateLabel { @@ -60,23 +59,20 @@ gfx::Vector2dF displacement; }; - std::unique_ptr<blink::WebGestureEvent> GetGestureFromTouchInfo( + std::unique_ptr<InputEvent> GetGestureFromTouchInfo( const TouchInfo& input_touch_info, bool force_cancel); - void HandleWaitingState(const TouchInfo& touch_info, - blink::WebGestureEvent* gesture); - void HandleDetectingState(const TouchInfo& touch_info, - bool force_cancel, - blink::WebGestureEvent* gesture); - void HandleScrollingState(const TouchInfo& touch_info, - bool force_cancel, - blink::WebGestureEvent* gesture); - void HandlePostScrollingState(const TouchInfo& touch_info, - bool force_cancel, - blink::WebGestureEvent* gesture); + std::unique_ptr<InputEvent> HandleWaitingState(const TouchInfo& touch_info); + std::unique_ptr<InputEvent> HandleDetectingState(const TouchInfo& touch_info, + bool force_cancel); + std::unique_ptr<InputEvent> HandleScrollingState(const TouchInfo& touch_info, + bool force_cancel); + std::unique_ptr<InputEvent> HandlePostScrollingState( + const TouchInfo& touch_info, + bool force_cancel); - void UpdateGestureWithScrollDelta(blink::WebGestureEvent* gesture); + void UpdateGestureWithScrollDelta(InputEvent* gesture); // If the user is touching the touch pad and the touch point is different from // before, update the touch point and return true. Otherwise, return false.
diff --git a/chrome/browser/vr/gesture_detector_unittest.cc b/chrome/browser/vr/gesture_detector_unittest.cc index d7ea291..4de4c2cc0 100644 --- a/chrome/browser/vr/gesture_detector_unittest.cc +++ b/chrome/browser/vr/gesture_detector_unittest.cc
@@ -4,8 +4,8 @@ #include "chrome/browser/vr/gesture_detector.h" +#include "chrome/browser/vr/input_event.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/web_gesture_event.h" namespace { constexpr float kDelta = 0.001f; @@ -34,8 +34,7 @@ .is_touching = true, }; auto gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureFlingCancel); + EXPECT_EQ(gestures->front()->type(), InputEvent::kFlingCancel); // A small move doesn't trigger scrolling yet. timestamp += base::TimeDelta::FromMilliseconds(1); @@ -70,11 +69,10 @@ .is_touching = true, }; auto gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollBegin); - auto* gesture = gestures->front().get(); - EXPECT_GT(gesture->data.scroll_update.delta_x, 0.0f); - EXPECT_EQ(gesture->data.scroll_update.delta_y, 0.0f); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollBegin); + auto* gesture = static_cast<InputEvent*>(gestures->front().get()); + EXPECT_GT(gesture->scroll_data.delta_x, 0.0f); + EXPECT_EQ(gesture->scroll_data.delta_y, 0.0f); // Move slightly up. timestamp += base::TimeDelta::FromMilliseconds(1); @@ -85,31 +83,27 @@ .is_touching = true, }; gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollUpdate); - gesture = gestures->front().get(); - EXPECT_EQ(gesture->data.scroll_update.delta_x, 0.0f); - EXPECT_GT(gesture->data.scroll_update.delta_y, 0.0f); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollUpdate); + gesture = static_cast<InputEvent*>(gestures->front().get()); + EXPECT_EQ(gesture->scroll_data.delta_x, 0.0f); + EXPECT_GT(gesture->scroll_data.delta_y, 0.0f); // Release touch. Scroll is extrapolated for 2 frames. touch_info.touch_up = true; touch_info.is_touching = false; timestamp += base::TimeDelta::FromMilliseconds(1); gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollUpdate); - gesture = gestures->front().get(); - EXPECT_GT(gesture->data.scroll_update.delta_x, 0.0f); - EXPECT_GT(gesture->data.scroll_update.delta_y, 0.0f); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollUpdate); + gesture = static_cast<InputEvent*>(gestures->front().get()); + EXPECT_GT(gesture->scroll_data.delta_x, 0.0f); + EXPECT_GT(gesture->scroll_data.delta_y, 0.0f); touch_info.touch_up = false; timestamp += base::TimeDelta::FromMilliseconds(1); gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollUpdate); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollUpdate); timestamp += base::TimeDelta::FromMilliseconds(1); gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollEnd); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollEnd); } TEST(GestureDetector, CancelDuringScrolling) { @@ -133,13 +127,11 @@ .is_touching = true, }; auto gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollBegin); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollBegin); // Cancel. gestures = detector.DetectGestures(touch_info, timestamp, true); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollEnd); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollEnd); } TEST(GestureDetector, CancelDuringPostScrolling) { @@ -163,21 +155,18 @@ .is_touching = true, }; auto gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollBegin); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollBegin); // Release touch. We should see extrapolated scrolling. touch_info.touch_up = true; touch_info.is_touching = false; gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollUpdate); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollUpdate); // Cancel. touch_info.touch_up = false; gestures = detector.DetectGestures(touch_info, timestamp, true); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollEnd); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollEnd); } TEST(GestureDetector, CancelAndTouchDuringPostScrolling) { @@ -201,16 +190,14 @@ .is_touching = true, }; auto gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollBegin); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollBegin); // Release touch. We should see extrapolated scrolling. timestamp += base::TimeDelta::FromMilliseconds(1); touch_info.touch_up = true; touch_info.is_touching = false; gestures = detector.DetectGestures(touch_info, timestamp, false); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollUpdate); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollUpdate); // Cancel and touch. timestamp += base::TimeDelta::FromMilliseconds(1); @@ -218,8 +205,7 @@ touch_info.touch_down = true; touch_info.is_touching = true; gestures = detector.DetectGestures(touch_info, timestamp, true); - EXPECT_EQ(gestures->front()->GetType(), - blink::WebInputEvent::kGestureScrollEnd); + EXPECT_EQ(gestures->front()->type(), InputEvent::kScrollEnd); } } // namespace vr
diff --git a/chrome/browser/vr/input_event.cc b/chrome/browser/vr/input_event.cc new file mode 100644 index 0000000..3832caa --- /dev/null +++ b/chrome/browser/vr/input_event.cc
@@ -0,0 +1,13 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/vr/input_event.h" + +namespace vr { + +InputEvent::InputEvent(Type type) : type_(type) {} + +InputEvent::~InputEvent() = default; + +} // namespace vr
diff --git a/chrome/browser/vr/input_event.h b/chrome/browser/vr/input_event.h new file mode 100644 index 0000000..f76ba26 --- /dev/null +++ b/chrome/browser/vr/input_event.h
@@ -0,0 +1,72 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VR_INPUT_EVENT_H_ +#define CHROME_BROWSER_VR_INPUT_EVENT_H_ + +#include "base/time/time.h" +#include "chrome/browser/vr/vr_export.h" +#include "ui/gfx/geometry/point_f.h" + +namespace vr { + +class VR_EXPORT InputEvent { + public: + enum Type { + kTypeUndefined = -1, + + kHoverEnter, + kTypeFirst = kHoverEnter, + kHoverLeave, + kHoverMove, + kButtonDown, + kButtonUp, + kMove, + kFlingCancel, + kScrollBegin, + kScrollTypeFirst = kScrollBegin, + kScrollUpdate, + kScrollEnd, + kScrollTypeLast = kScrollEnd, + + kNumVrInputEventTypes + }; + + explicit InputEvent(Type type); + virtual ~InputEvent(); + + Type type() const { return type_; } + + base::TimeTicks time_stamp() const { return time_stamp_; } + + void set_time_stamp(base::TimeTicks time_stamp) { time_stamp_ = time_stamp; } + + gfx::PointF position_in_widget() const { return position_in_widget_; } + + void set_position_in_widget(const gfx::PointF& position) { + position_in_widget_ = position; + } + + void SetPositionInWidget(float x, float y) { + position_in_widget_ = gfx::PointF(x, y); + } + + static bool IsScrollEventType(InputEvent::Type type) { + return kScrollTypeFirst <= type && type <= kScrollTypeLast; + } + + struct { + float delta_x; + float delta_y; + } scroll_data; + + private: + Type type_; + base::TimeTicks time_stamp_; + gfx::PointF position_in_widget_; +}; + +} // namespace vr + +#endif // CHROME_BROWSER_VR_INPUT_EVENT_H_
diff --git a/chrome/browser/vr/platform_input_handler.h b/chrome/browser/vr/platform_input_handler.h index f16919f..40503d9f 100644 --- a/chrome/browser/vr/platform_input_handler.h +++ b/chrome/browser/vr/platform_input_handler.h
@@ -7,10 +7,11 @@ #include "base/callback.h" #include "chrome/browser/vr/text_edit_action.h" -#include "third_party/blink/public/platform/web_input_event.h" namespace vr { +class InputEvent; + typedef typename base::OnceCallback<void(const base::string16&)> TextStateUpdateCallback; @@ -19,11 +20,9 @@ class PlatformInputHandler { public: virtual ~PlatformInputHandler() {} - virtual void ForwardEventToPlatformUi( - std::unique_ptr<blink::WebInputEvent> event) = 0; - virtual void ForwardEventToContent( - std::unique_ptr<blink::WebInputEvent> event, - int content_id) = 0; + virtual void ForwardEventToPlatformUi(std::unique_ptr<InputEvent> event) = 0; + virtual void ForwardEventToContent(std::unique_ptr<InputEvent> event, + int content_id) = 0; // Text input specific. virtual void ClearFocusedElement() = 0;
diff --git a/chrome/browser/vr/platform_ui_input_delegate.cc b/chrome/browser/vr/platform_ui_input_delegate.cc index ab9f596..601273cc4 100644 --- a/chrome/browser/vr/platform_ui_input_delegate.cc +++ b/chrome/browser/vr/platform_ui_input_delegate.cc
@@ -8,9 +8,6 @@ #include "base/time/time.h" #include "chrome/browser/vr/platform_controller.h" #include "chrome/browser/vr/platform_input_handler.h" -#include "third_party/blink/public/platform/web_gesture_event.h" -#include "third_party/blink/public/platform/web_mouse_event.h" -#include "third_party/blink/public/platform/web_touch_event.h" namespace vr { @@ -30,136 +27,80 @@ void PlatformUiInputDelegate::OnHoverEnter( const gfx::PointF& normalized_hit_point, base::TimeTicks timestamp) { - SendGestureToTarget(MakeMouseEvent(blink::WebInputEvent::kMouseEnter, - normalized_hit_point, timestamp)); + SendGestureToTarget( + MakeInputEvent(InputEvent::kHoverEnter, normalized_hit_point, timestamp)); } void PlatformUiInputDelegate::OnHoverLeave(base::TimeTicks timestamp) { - // Note that we send an out of bounds mouse leave event. With blink feature + // Note that we send an out of bounds hover leave event. With blink feature // UpdateHoverPostLayout turned on, a MouseMove event will dispatched post a // Layout. Sending a mouse leave event at 0,0 will result continuous // MouseMove events sent to the content if the content keeps relayout itself. // See https://crbug.com/762573 for details. - SendGestureToTarget(MakeMouseEvent(blink::WebInputEvent::kMouseLeave, - kOutOfBoundsPoint, timestamp)); + SendGestureToTarget( + MakeInputEvent(InputEvent::kHoverLeave, kOutOfBoundsPoint, timestamp)); } void PlatformUiInputDelegate::OnHoverMove( const gfx::PointF& normalized_hit_point, base::TimeTicks timestamp) { - SendGestureToTarget(MakeMouseEvent(blink::WebInputEvent::kMouseMove, - normalized_hit_point, timestamp)); + SendGestureToTarget( + MakeInputEvent(InputEvent::kHoverMove, normalized_hit_point, timestamp)); } void PlatformUiInputDelegate::OnButtonDown( const gfx::PointF& normalized_hit_point, base::TimeTicks timestamp) { - SendGestureToTarget(MakeTouchEvent(blink::WebInputEvent::kTouchStart, - normalized_hit_point, timestamp)); + SendGestureToTarget( + MakeInputEvent(InputEvent::kButtonDown, normalized_hit_point, timestamp)); } void PlatformUiInputDelegate::OnButtonUp( const gfx::PointF& normalized_hit_point, base::TimeTicks timestamp) { - SendGestureToTarget(MakeTouchEvent(blink::WebInputEvent::kTouchEnd, - normalized_hit_point, timestamp)); + SendGestureToTarget( + MakeInputEvent(InputEvent::kButtonUp, normalized_hit_point, timestamp)); } void PlatformUiInputDelegate::OnTouchMove( const gfx::PointF& normalized_hit_point, base::TimeTicks timestamp) { - SendGestureToTarget(MakeTouchEvent(blink::WebInputEvent::kTouchMove, - normalized_hit_point, timestamp)); + SendGestureToTarget( + MakeInputEvent(InputEvent::kMove, normalized_hit_point, timestamp)); } -void PlatformUiInputDelegate::OnFlingCancel( - std::unique_ptr<blink::WebGestureEvent> gesture, +void PlatformUiInputDelegate::OnInputEvent( + std::unique_ptr<InputEvent> event, const gfx::PointF& normalized_hit_point) { - UpdateGesture(normalized_hit_point, *gesture); - SendGestureToTarget(std::move(gesture)); -} - -void PlatformUiInputDelegate::OnScrollBegin( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point) { - UpdateGesture(normalized_hit_point, *gesture); - SendGestureToTarget(std::move(gesture)); -} - -void PlatformUiInputDelegate::OnScrollUpdate( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point) { - UpdateGesture(normalized_hit_point, *gesture); - SendGestureToTarget(std::move(gesture)); -} - -void PlatformUiInputDelegate::OnScrollEnd( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point) { - UpdateGesture(normalized_hit_point, *gesture); - SendGestureToTarget(std::move(gesture)); + UpdateGesture(normalized_hit_point, event.get()); + SendGestureToTarget(std::move(event)); } void PlatformUiInputDelegate::UpdateGesture( const gfx::PointF& normalized_content_hit_point, - blink::WebGestureEvent& gesture) { - gesture.SetPositionInWidget( + InputEvent* gesture) { + gesture->set_position_in_widget( ScalePoint(normalized_content_hit_point, size_.width(), size_.height())); } void PlatformUiInputDelegate::SendGestureToTarget( - std::unique_ptr<blink::WebInputEvent> event) { + std::unique_ptr<InputEvent> event) { if (!event || !input_handler_) return; input_handler_->ForwardEventToPlatformUi(std::move(event)); } -std::unique_ptr<blink::WebMouseEvent> PlatformUiInputDelegate::MakeMouseEvent( - blink::WebInputEvent::Type type, +std::unique_ptr<InputEvent> PlatformUiInputDelegate::MakeInputEvent( + InputEvent::Type type, const gfx::PointF& normalized_web_content_location, - base::TimeTicks timestamp) const { + base::TimeTicks time_stamp) const { gfx::Point location = CalculateLocation(normalized_web_content_location); - auto mouse_event = std::make_unique<blink::WebMouseEvent>( - type, blink::WebInputEvent::kNoModifiers, timestamp); - mouse_event->pointer_type = blink::WebPointerProperties::PointerType::kMouse; - mouse_event->button = blink::WebPointerProperties::Button::kLeft; - mouse_event->SetPositionInWidget(location.x(), location.y()); - mouse_event->click_count = 1; - return mouse_event; -} - -std::unique_ptr<blink::WebTouchEvent> PlatformUiInputDelegate::MakeTouchEvent( - blink::WebInputEvent::Type type, - const gfx::PointF& normalized_web_content_location, - base::TimeTicks timestamp) const { - gfx::Point location = CalculateLocation(normalized_web_content_location); - blink::WebInputEvent::Modifiers modifiers = - blink::WebInputEvent::kNoModifiers; - - blink::WebTouchPoint::State touch_state = - blink::WebTouchPoint::kStateUndefined; - switch (type) { - case blink::WebInputEvent::kTouchStart: - touch_state = blink::WebTouchPoint::kStatePressed; - break; - case blink::WebInputEvent::kTouchEnd: - touch_state = blink::WebTouchPoint::kStateReleased; - break; - case blink::WebInputEvent::kTouchMove: - touch_state = blink::WebTouchPoint::kStateMoved; - break; - default: - NOTREACHED(); - } - - auto touch_event = - std::make_unique<blink::WebTouchEvent>(type, modifiers, timestamp); - touch_event->touches_length = 1; - touch_event->touches[0].state = touch_state; - touch_event->touches[0].SetPositionInWidget(location.x(), location.y()); - return touch_event; + auto event = std::make_unique<InputEvent>(type); + event->set_time_stamp(time_stamp); + event->SetPositionInWidget(location.x(), location.y()); + return event; } gfx::Point PlatformUiInputDelegate::CalculateLocation(
diff --git a/chrome/browser/vr/platform_ui_input_delegate.h b/chrome/browser/vr/platform_ui_input_delegate.h index a2857f7..77e5d395 100644 --- a/chrome/browser/vr/platform_ui_input_delegate.h +++ b/chrome/browser/vr/platform_ui_input_delegate.h
@@ -11,23 +11,17 @@ #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" +#include "chrome/browser/vr/input_event.h" #include "chrome/browser/vr/macros.h" #include "chrome/browser/vr/model/text_input_info.h" #include "chrome/browser/vr/text_edit_action.h" #include "chrome/browser/vr/vr_export.h" -#include "third_party/blink/public/platform/web_input_event.h" #include "ui/gfx/geometry/size.h" namespace base { class TimeTicks; } // namespace base -namespace blink { -class WebGestureEvent; -class WebMouseEvent; -class WebTouchEvent; -} // namespace blink - namespace gfx { class PointF; } // namespace gfx @@ -59,18 +53,8 @@ base::TimeTicks timestamp); VIRTUAL_FOR_MOCKS void OnTouchMove(const gfx::PointF& normalized_hit_point, base::TimeTicks timestamp); - VIRTUAL_FOR_MOCKS void OnFlingCancel( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point); - VIRTUAL_FOR_MOCKS void OnScrollBegin( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point); - VIRTUAL_FOR_MOCKS void OnScrollUpdate( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point); - VIRTUAL_FOR_MOCKS void OnScrollEnd( - std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point); + VIRTUAL_FOR_MOCKS void OnInputEvent(std::unique_ptr<InputEvent> event, + const gfx::PointF& normalized_hit_point); void SetSize(int width, int height) { size_ = {width, height}; } void SetPlatformInputHandlerForTest(PlatformInputHandler* input_handler) { @@ -78,20 +62,16 @@ } protected: - virtual void SendGestureToTarget(std::unique_ptr<blink::WebInputEvent> event); + virtual void SendGestureToTarget(std::unique_ptr<InputEvent> event); PlatformInputHandler* input_handler() const { return input_handler_; } private: void UpdateGesture(const gfx::PointF& normalized_content_hit_point, - blink::WebGestureEvent& gesture); - std::unique_ptr<blink::WebMouseEvent> MakeMouseEvent( - blink::WebInputEvent::Type type, + InputEvent* gesture); + std::unique_ptr<InputEvent> MakeInputEvent( + InputEvent::Type type, const gfx::PointF& normalized_web_content_location, - base::TimeTicks timestamp) const; - std::unique_ptr<blink::WebTouchEvent> MakeTouchEvent( - blink::WebInputEvent::Type type, - const gfx::PointF& normalized_web_content_location, - base::TimeTicks timestamp) const; + base::TimeTicks time_stamp) const; gfx::Point CalculateLocation( const gfx::PointF& normalized_web_content_location) const;
diff --git a/chrome/browser/vr/platform_ui_input_delegate_unittest.cc b/chrome/browser/vr/platform_ui_input_delegate_unittest.cc new file mode 100644 index 0000000..a10a4226 --- /dev/null +++ b/chrome/browser/vr/platform_ui_input_delegate_unittest.cc
@@ -0,0 +1,90 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/vr/platform_ui_input_delegate.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::AllOf; +using ::testing::Property; +using ::testing::Ref; + +namespace vr { + +class PlatformUiInputDelegateTest : public PlatformUiInputDelegate { + public: + MOCK_METHOD1(FwdSendGestureToTarget, void(const InputEvent& event)); + + void SendGestureToTarget(std::unique_ptr<InputEvent> event) override; +}; + +void PlatformUiInputDelegateTest::SendGestureToTarget( + std::unique_ptr<InputEvent> event) { + FwdSendGestureToTarget(*event); +} + +TEST(PlatformUiInputDelegateTest, OnHoverEvents) { + PlatformUiInputDelegateTest delegate; + delegate.SetSize(40.0f, 30.0f); + + EXPECT_CALL(delegate, + FwdSendGestureToTarget( + AllOf(Property(&InputEvent::type, InputEvent::kHoverEnter), + Property(&InputEvent::position_in_widget, + gfx::PointF(20.0f, 15.0f))))); + delegate.OnHoverEnter({0.5f, 0.5f}, base::TimeTicks()); + + EXPECT_CALL( + delegate, + FwdSendGestureToTarget(AllOf( + Property(&InputEvent::type, InputEvent::kHoverMove), + Property(&InputEvent::position_in_widget, gfx::PointF(4.0f, 6.0f))))); + delegate.OnHoverMove({0.1f, 0.2f}, base::TimeTicks()); + + EXPECT_CALL(delegate, FwdSendGestureToTarget(Property( + &InputEvent::type, InputEvent::kHoverLeave))); + delegate.OnHoverLeave(base::TimeTicks()); +} + +TEST(PlatformUiInputDelegateTest, OnButtonEvents) { + PlatformUiInputDelegateTest delegate; + delegate.SetSize(40.0f, 30.0f); + + EXPECT_CALL( + delegate, + FwdSendGestureToTarget(AllOf( + Property(&InputEvent::type, InputEvent::kButtonDown), + Property(&InputEvent::position_in_widget, gfx::PointF(4.0f, 3.0f))))); + delegate.OnButtonDown({0.1f, 0.1f}, base::TimeTicks()); + + EXPECT_CALL( + delegate, + FwdSendGestureToTarget(AllOf( + Property(&InputEvent::type, InputEvent::kMove), + Property(&InputEvent::position_in_widget, gfx::PointF(8.0f, 3.0f))))); + delegate.OnTouchMove({0.2f, 0.1f}, base::TimeTicks()); + + EXPECT_CALL( + delegate, + FwdSendGestureToTarget(AllOf( + Property(&InputEvent::type, InputEvent::kButtonUp), + Property(&InputEvent::position_in_widget, gfx::PointF(0.0f, 3.0f))))); + delegate.OnButtonUp({0.0f, 0.1f}, base::TimeTicks()); +} + +TEST(PlatformUiInputDelegateTest, OnInputEvent) { + PlatformUiInputDelegateTest delegate; + delegate.SetSize(40.0f, 30.0f); + + auto event = std::make_unique<InputEvent>(InputEvent::kTypeUndefined); + EXPECT_CALL(delegate, + FwdSendGestureToTarget( + AllOf(Ref(*event), Property(&InputEvent::position_in_widget, + gfx::PointF(4.0f, 3.0f))))); + delegate.OnInputEvent(std::move(event), {0.1f, 0.1f}); +} + +} // namespace vr
diff --git a/chrome/browser/vr/test/mock_content_input_delegate.cc b/chrome/browser/vr/test/mock_content_input_delegate.cc index 2d88332..07cf6e4 100644 --- a/chrome/browser/vr/test/mock_content_input_delegate.cc +++ b/chrome/browser/vr/test/mock_content_input_delegate.cc
@@ -4,9 +4,6 @@ #include "chrome/browser/vr/test/mock_content_input_delegate.h" -#include "third_party/blink/public/platform/web_gesture_event.h" -#include "ui/gfx/geometry/point_f.h" - namespace vr { MockContentInputDelegate::MockContentInputDelegate()
diff --git a/chrome/browser/vr/test/mock_content_input_delegate.h b/chrome/browser/vr/test/mock_content_input_delegate.h index d644b60..dfbed93 100644 --- a/chrome/browser/vr/test/mock_content_input_delegate.h +++ b/chrome/browser/vr/test/mock_content_input_delegate.h
@@ -8,7 +8,6 @@ #include "base/macros.h" #include "chrome/browser/vr/content_input_delegate.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/public/platform/web_gesture_event.h" namespace vr { @@ -33,34 +32,13 @@ // As move-only parameters aren't supported by mock methods, we will override // the functions explicitly and fwd the calls to the mocked functions. - MOCK_METHOD2(FwdContentFlingCancel, - void(std::unique_ptr<blink::WebGestureEvent>& gesture, - const gfx::PointF& normalized_hit_point)); - MOCK_METHOD2(FwdContentScrollBegin, - void(std::unique_ptr<blink::WebGestureEvent>& gesture, - const gfx::PointF& normalized_hit_point)); - MOCK_METHOD2(FwdContentScrollUpdate, - void(std::unique_ptr<blink::WebGestureEvent>& gesture, - const gfx::PointF& normalized_hit_point)); - MOCK_METHOD2(FwdContentScrollEnd, - void(std::unique_ptr<blink::WebGestureEvent>& gesture, + MOCK_METHOD2(FwdContentInputEvent, + void(std::unique_ptr<InputEvent>& gesture, const gfx::PointF& normalized_hit_point)); - void OnFlingCancel(std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point) override { - FwdContentFlingCancel(gesture, normalized_hit_point); - } - void OnScrollBegin(std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point) override { - FwdContentScrollBegin(gesture, normalized_hit_point); - } - void OnScrollUpdate(std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point) override { - FwdContentScrollUpdate(gesture, normalized_hit_point); - } - void OnScrollEnd(std::unique_ptr<blink::WebGestureEvent> gesture, - const gfx::PointF& normalized_hit_point) override { - FwdContentScrollEnd(gesture, normalized_hit_point); + void OnInputEvent(std::unique_ptr<InputEvent> gesture, + const gfx::PointF& normalized_hit_point) override { + FwdContentInputEvent(gesture, normalized_hit_point); } };
diff --git a/chrome/browser/vr/test/ui_pixel_test.cc b/chrome/browser/vr/test/ui_pixel_test.cc index 536b09c..3a395ab 100644 --- a/chrome/browser/vr/test/ui_pixel_test.cc +++ b/chrome/browser/vr/test/ui_pixel_test.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/vr/ui_input_manager.h" #include "chrome/browser/vr/ui_renderer.h" #include "chrome/browser/vr/ui_scene.h" -#include "third_party/blink/public/platform/web_gesture_event.h" #include "third_party/skia/include/core/SkImageEncoder.h" #include "third_party/skia/include/core/SkStream.h" #include "ui/gl/gl_bindings.h" @@ -91,12 +90,12 @@ frame_buffer_size_.height()}; render_info.right_eye_model.viewport = {0, 0, 0, 0}; - GestureList gesture_list; + InputEventList input_event_list; ReticleModel reticle_model; EXPECT_TRUE( ui_->scene()->OnBeginFrame(base::TimeTicks(), render_info.head_pose)); ui_->input_manager()->HandleInput(MsToTicks(1), render_info, controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); ui_->OnControllerUpdated(controller_model, reticle_model); ui_->ui_renderer()->Draw(render_info);
diff --git a/chrome/browser/vr/test/ui_test.cc b/chrome/browser/vr/test/ui_test.cc index b5e450f..a46beca 100644 --- a/chrome/browser/vr/test/ui_test.cc +++ b/chrome/browser/vr/test/ui_test.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/vr/ui_renderer.h" #include "chrome/browser/vr/ui_scene.h" #include "chrome/browser/vr/ui_scene_creator.h" -#include "third_party/blink/public/platform/web_gesture_event.h" #include "ui/gfx/geometry/vector3d_f.h" namespace vr { @@ -251,7 +250,7 @@ RenderInfo render_info; ReticleModel reticle_model; - GestureList gesture_list; + InputEventList input_event_list; ControllerModel controller_model; controller_model.laser_direction = direction; controller_model.laser_origin = origin; @@ -259,13 +258,13 @@ controller_model.touchpad_button_state = UiInputManager::ButtonState::DOWN; ui_->input_manager()->HandleInput(current_time_, render_info, controller_model, &reticle_model, - &gesture_list); + &input_event_list); OnBeginFrame(); controller_model.touchpad_button_state = UiInputManager::ButtonState::UP; ui_->input_manager()->HandleInput(current_time_, render_info, controller_model, &reticle_model, - &gesture_list); + &input_event_list); OnBeginFrame(); }
diff --git a/chrome/browser/vr/testapp/BUILD.gn b/chrome/browser/vr/testapp/BUILD.gn index 00b222e..53673df 100644 --- a/chrome/browser/vr/testapp/BUILD.gn +++ b/chrome/browser/vr/testapp/BUILD.gn
@@ -24,14 +24,16 @@ deps = [ ":assets_component_version_header", ":vr_testapp_pak", - "//chrome/browser/vr:vr_gl_test_support", - "//chrome/browser/vr:vr_test_support", + "//chrome/browser/vr:vr_common", "//components:components_tests_pak", + "//components/security_state/core", + "//components/toolbar:vector_icons", "//components/tracing:startup_tracing", "//ui/display/types", "//ui/events", "//ui/events:dom_keycode_converter", "//ui/events/ozone:events_ozone_layout", + "//ui/gl/init", "//ui/ozone", "//ui/platform_window", ]
diff --git a/chrome/browser/vr/testapp/vr_test_context.cc b/chrome/browser/vr/testapp/vr_test_context.cc index 49745f6..917d6a0d 100644 --- a/chrome/browser/vr/testapp/vr_test_context.cc +++ b/chrome/browser/vr/testapp/vr_test_context.cc
@@ -33,7 +33,6 @@ #include "components/omnibox/browser/vector_icons.h" #include "components/security_state/core/security_state.h" #include "components/toolbar/vector_icons.h" -#include "third_party/blink/public/platform/web_gesture_event.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" #include "ui/base/resource/resource_bundle.h" @@ -80,19 +79,19 @@ out_image->get()); } -GestureList CreateScrollGestureEventList(blink::WebGestureEvent::Type type) { - auto gesture = std::make_unique<blink::WebGestureEvent>(); - gesture->SetType(type); - GestureList list; +InputEventList CreateScrollGestureEventList(InputEvent::Type type) { + std::unique_ptr<InputEvent> gesture = std::make_unique<InputEvent>(type); + InputEventList list; list.push_back(std::move(gesture)); return list; } -GestureList CreateScrollGestureEventList(blink::WebGestureEvent::Type type, - const gfx::Vector2dF& delta) { +InputEventList CreateScrollGestureEventList(InputEvent::Type type, + const gfx::Vector2dF& delta) { auto list = CreateScrollGestureEventList(type); - list.front()->data.scroll_update.delta_x = delta.x(); - list.front()->data.scroll_update.delta_y = delta.y(); + InputEvent* event = static_cast<InputEvent*>(list.front().get()); + event->scroll_data.delta_x = delta.x(); + event->scroll_data.delta_y = delta.y(); return list; } @@ -302,8 +301,8 @@ touchpad_touch_position_.y() + kTouchpadPositionDelta * direction, 0.0f, 1.0f)); } else { - gesture_lists_.push(CreateScrollGestureEventList( - blink::WebGestureEvent::kGestureScrollBegin)); + input_event_lists_.push( + CreateScrollGestureEventList(InputEvent::kScrollBegin)); auto offset = gfx::Vector2dF(event->AsMouseWheelEvent()->offset()); if (event->IsShiftDown()) { @@ -312,11 +311,11 @@ } else { offset.Scale(kVerticalScrollScaleFactor); } - gesture_lists_.push(CreateScrollGestureEventList( - blink::WebGestureEvent::kGestureScrollUpdate, offset)); + input_event_lists_.push( + CreateScrollGestureEventList(InputEvent::kScrollUpdate, offset)); - gesture_lists_.push(CreateScrollGestureEventList( - blink::WebGestureEvent::kGestureScrollEnd)); + input_event_lists_.push( + CreateScrollGestureEventList(InputEvent::kScrollEnd)); } return; } @@ -420,13 +419,14 @@ RotateToward(controller_model.laser_direction, &controller_model.transform); // Hit testing is done in terms of this synthesized controller model. - if (gesture_lists_.empty()) { - gesture_lists_.push(GestureList()); + if (input_event_lists_.empty()) { + input_event_lists_.push(InputEventList()); } ReticleModel reticle_model; ui_->input_manager()->HandleInput(current_time, render_info, controller_model, - &reticle_model, &gesture_lists_.front()); - gesture_lists_.pop(); + &reticle_model, + &input_event_lists_.front()); + input_event_lists_.pop(); // Now that we have accurate hit information, we use this to construct a // controller model for display.
diff --git a/chrome/browser/vr/testapp/vr_test_context.h b/chrome/browser/vr/testapp/vr_test_context.h index b1174f17..79059137 100644 --- a/chrome/browser/vr/testapp/vr_test_context.h +++ b/chrome/browser/vr/testapp/vr_test_context.h
@@ -125,7 +125,7 @@ PlatformController::Handedness handedness_ = PlatformController::kRightHanded; - std::queue<GestureList> gesture_lists_; + std::queue<InputEventList> input_event_lists_; DISALLOW_COPY_AND_ASSIGN(VrTestContext); };
diff --git a/chrome/browser/vr/ui_input_manager.cc b/chrome/browser/vr/ui_input_manager.cc index dcaca409..fd57134 100644 --- a/chrome/browser/vr/ui_input_manager.cc +++ b/chrome/browser/vr/ui_input_manager.cc
@@ -8,15 +8,14 @@ #include "base/containers/adapters.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "chrome/browser/vr/elements/ui_element.h" +#include "chrome/browser/vr/input_event.h" #include "chrome/browser/vr/model/controller_model.h" #include "chrome/browser/vr/model/reticle_model.h" #include "chrome/browser/vr/model/text_input_info.h" #include "chrome/browser/vr/ui_renderer.h" #include "chrome/browser/vr/ui_scene.h" -// TODO(tiborg): Remove include once we use a generic type to pass scroll/fling -// gestures. -#include "third_party/blink/public/platform/web_gesture_event.h" namespace vr { @@ -42,19 +41,14 @@ return o.x() > -1.0f && o.x() < 1.0f && o.y() > -1.0f && o.y() < 1.0f; } -bool IsScrollEvent(const GestureList& list) { +bool IsScrollOrFling(const InputEventList& list) { if (list.empty()) { return false; } // We assume that we only need to consider the first gesture in the list. - blink::WebInputEvent::Type type = list.front()->GetType(); - if (type == blink::WebInputEvent::kGestureScrollBegin || - type == blink::WebInputEvent::kGestureScrollEnd || - type == blink::WebInputEvent::kGestureScrollUpdate || - type == blink::WebInputEvent::kGestureFlingCancel) { - return true; - } - return false; + auto type = list.front()->type(); + return InputEvent::IsScrollEventType(type) || + type == InputEvent::kFlingCancel; } void HitTestElements(UiScene* scene, @@ -91,13 +85,13 @@ const RenderInfo& render_info, const ControllerModel& controller_model, ReticleModel* reticle_model, - GestureList* gesture_list) { + InputEventList* input_event_list) { UpdateQuiescenceState(current_time, controller_model); UpdateControllerFocusState(current_time, render_info, controller_model); reticle_model->target_element_id = 0; reticle_model->target_local_point = kInvalidTargetPoint; UiElement* target_element = - GetTargetElement(controller_model, reticle_model, *gesture_list); + GetTargetElement(controller_model, reticle_model, *input_event_list); auto element_local_point = reticle_model->target_local_point; if (input_capture_element_id_) @@ -105,8 +99,8 @@ GetCapturedElementHitPoint(reticle_model->target_point); // Sending end and cancel events. - SendFlingCancel(gesture_list, element_local_point); - SendScrollEnd(gesture_list, element_local_point, + SendFlingCancel(input_event_list, element_local_point); + SendScrollEnd(input_event_list, element_local_point, controller_model.touchpad_button_state); SendButtonUp(element_local_point, controller_model.touchpad_button_state, controller_model.last_button_timestamp); @@ -114,7 +108,7 @@ // Sending update events. if (in_scroll_) { - SendScrollUpdate(gesture_list, element_local_point); + SendScrollUpdate(input_event_list, element_local_point); } else if (in_click_) { SendTouchMove(element_local_point, controller_model.last_orientation_timestamp); @@ -126,7 +120,7 @@ // Sending begin events. SendHoverEnter(target_element, reticle_model->target_local_point, controller_model.last_orientation_timestamp); - SendScrollBegin(target_element, gesture_list, element_local_point); + SendScrollBegin(target_element, input_event_list, element_local_point); SendButtonDown(target_element, reticle_model->target_local_point, controller_model.touchpad_button_state, controller_model.last_button_timestamp); @@ -143,13 +137,13 @@ } } -void UiInputManager::SendFlingCancel(GestureList* gesture_list, +void UiInputManager::SendFlingCancel(InputEventList* input_event_list, const gfx::PointF& target_point) { if (!fling_target_id_) { return; } - if (gesture_list->empty() || (gesture_list->front()->GetType() != - blink::WebInputEvent::kGestureFlingCancel)) { + if (input_event_list->empty() || + (input_event_list->front()->type() != InputEvent::kFlingCancel)) { return; } @@ -157,13 +151,13 @@ UiElement* element = scene_->GetUiElementById(fling_target_id_); if (element) { DCHECK(element->scrollable()); - element->OnFlingCancel(std::move(gesture_list->front()), target_point); + element->OnFlingCancel(std::move(input_event_list->front()), target_point); } - gesture_list->erase(gesture_list->begin()); + input_event_list->erase(input_event_list->begin()); fling_target_id_ = 0; } -void UiInputManager::SendScrollEnd(GestureList* gesture_list, +void UiInputManager::SendScrollEnd(InputEventList* input_event_list, const gfx::PointF& target_point, ButtonState button_state) { if (!in_scroll_) { @@ -174,53 +168,52 @@ if (previous_button_state_ != button_state && button_state == ButtonState::DOWN) { - DCHECK_GT(gesture_list->size(), 0LU); - DCHECK_EQ(gesture_list->front()->GetType(), - blink::WebInputEvent::kGestureScrollEnd); + DCHECK_GT(input_event_list->size(), 0LU); + DCHECK_EQ(input_event_list->front()->type(), InputEvent::kScrollEnd); } DCHECK(!element || element->scrollable()); - if (gesture_list->empty() || gesture_list->front()->GetType() != - blink::WebInputEvent::kGestureScrollEnd) { + if (input_event_list->empty() || + input_event_list->front()->type() != InputEvent::kScrollEnd) { return; } - DCHECK_LE(gesture_list->size(), 1LU); + DCHECK_LE(input_event_list->size(), 1LU); fling_target_id_ = input_capture_element_id_; - element->OnScrollEnd(std::move(gesture_list->front()), target_point); - gesture_list->erase(gesture_list->begin()); + element->OnScrollEnd(std::move(input_event_list->front()), target_point); + input_event_list->erase(input_event_list->begin()); input_capture_element_id_ = 0; in_scroll_ = false; } void UiInputManager::SendScrollBegin(UiElement* target, - GestureList* gesture_list, + InputEventList* input_event_list, const gfx::PointF& target_point) { if (in_scroll_ || !target || !target->scrollable()) return; - if (gesture_list->empty() || (gesture_list->front()->GetType() != - blink::WebInputEvent::kGestureScrollBegin)) { + if (input_event_list->empty() || + input_event_list->front()->type() != InputEvent::kScrollBegin) { return; } input_capture_element_id_ = target->id(); in_scroll_ = true; - target->OnScrollBegin(std::move(gesture_list->front()), target_point); - gesture_list->erase(gesture_list->begin()); + target->OnScrollBegin(std::move(input_event_list->front()), target_point); + input_event_list->erase(input_event_list->begin()); } -void UiInputManager::SendScrollUpdate(GestureList* gesture_list, +void UiInputManager::SendScrollUpdate(InputEventList* input_event_list, const gfx::PointF& target_point) { DCHECK(input_capture_element_id_); - if (gesture_list->empty() || (gesture_list->front()->GetType() != - blink::WebInputEvent::kGestureScrollUpdate)) { + if (input_event_list->empty() || + (input_event_list->front()->type() != InputEvent::kScrollUpdate)) { return; } // Scrolling currently only supported on content window. UiElement* element = scene_->GetUiElementById(input_capture_element_id_); if (element) { DCHECK(element->scrollable()); - element->OnScrollUpdate(std::move(gesture_list->front()), target_point); + element->OnScrollUpdate(std::move(input_event_list->front()), target_point); } - gesture_list->erase(gesture_list->begin()); + input_event_list->erase(input_event_list->begin()); } void UiInputManager::SendHoverLeave(UiElement* current_target, @@ -302,7 +295,7 @@ UiElement* UiInputManager::GetTargetElement( const ControllerModel& controller_model, ReticleModel* reticle_model, - const GestureList& gesture_list) const { + const InputEventList& input_event_list) const { // If we place the reticle based on elements intersecting the controller beam, // we can end up with the reticle hiding behind elements, or jumping laterally // in the field of view. This is physically correct, but hard to use. For @@ -347,7 +340,7 @@ UiElement* target_element = scene_->GetUiElementById(reticle_model->target_element_id); if (target_element) { - if (IsScrollEvent(gesture_list) && !input_capture_element_id_) { + if (IsScrollOrFling(input_event_list) && !input_capture_element_id_) { DCHECK(!in_scroll_ && !in_click_); UiElement* ancestor = target_element; while (!ancestor->scrollable() && ancestor->parent())
diff --git a/chrome/browser/vr/ui_input_manager.h b/chrome/browser/vr/ui_input_manager.h index ea214e4..1043ab30 100644 --- a/chrome/browser/vr/ui_input_manager.h +++ b/chrome/browser/vr/ui_input_manager.h
@@ -15,20 +15,17 @@ #include "ui/gfx/geometry/vector3d_f.h" #include "ui/gfx/transform.h" -namespace blink { -class WebGestureEvent; -} - namespace vr { class UiScene; class UiElement; +class InputEvent; struct ControllerModel; struct RenderInfo; struct ReticleModel; struct EditedText; -using GestureList = std::vector<std::unique_ptr<blink::WebGestureEvent>>; +using InputEventList = std::vector<std::unique_ptr<InputEvent>>; // Based on controller input finds the hit UI element and determines the // interaction with UI elements and the web contents. @@ -50,12 +47,11 @@ explicit UiInputManager(UiScene* scene); ~UiInputManager(); - // TODO(tiborg): Use generic gesture type instead of blink::WebGestureEvent. void HandleInput(base::TimeTicks current_time, const RenderInfo& render_info, const ControllerModel& controller_model, ReticleModel* reticle_model, - GestureList* gesture_list); + InputEventList* input_event_list); void OnPause(); @@ -76,15 +72,15 @@ } private: - void SendFlingCancel(GestureList* gesture_list, + void SendFlingCancel(InputEventList* input_event_list, const gfx::PointF& target_point); - void SendScrollEnd(GestureList* gesture_list, + void SendScrollEnd(InputEventList* input_event_list, const gfx::PointF& target_point, ButtonState button_state); void SendScrollBegin(UiElement* target, - GestureList* gesture_list, + InputEventList* input_event_list, const gfx::PointF& target_point); - void SendScrollUpdate(GestureList* gesture_list, + void SendScrollUpdate(InputEventList* input_event_list, const gfx::PointF& target_point); void SendHoverLeave(UiElement* current_target, base::TimeTicks timestamp); @@ -107,7 +103,7 @@ UiElement* GetTargetElement(const ControllerModel& controller_model, ReticleModel* reticle_model, - const GestureList& gesture_list) const; + const InputEventList& input_event_list) const; void UpdateQuiescenceState(base::TimeTicks current_time, const ControllerModel& controller_model); void UpdateControllerFocusState(base::TimeTicks current_time,
diff --git a/chrome/browser/vr/ui_input_manager_unittest.cc b/chrome/browser/vr/ui_input_manager_unittest.cc index f5b7287..ad61c33 100644 --- a/chrome/browser/vr/ui_input_manager_unittest.cc +++ b/chrome/browser/vr/ui_input_manager_unittest.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/vr/elements/invisible_hit_target.h" #include "chrome/browser/vr/elements/rect.h" #include "chrome/browser/vr/elements/ui_element.h" +#include "chrome/browser/vr/input_event.h" #include "chrome/browser/vr/model/model.h" #include "chrome/browser/vr/test/animation_utils.h" #include "chrome/browser/vr/test/constants.h" @@ -24,7 +25,6 @@ #include "chrome/browser/vr/ui_scene_creator.h" #include "chrome/browser/vr/ui_unsupported_mode.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/public/platform/web_gesture_event.h" using ::testing::_; using ::testing::InSequence; @@ -57,14 +57,11 @@ MOCK_METHOD2(OnTouchMove, void(const gfx::PointF& position, base::TimeTicks timestamp)); MOCK_METHOD2(OnScrollBegin, - void(std::unique_ptr<blink::WebGestureEvent>, - const gfx::PointF&)); + void(std::unique_ptr<InputEvent>, const gfx::PointF&)); MOCK_METHOD2(OnScrollUpdate, - void(std::unique_ptr<blink::WebGestureEvent>, - const gfx::PointF&)); + void(std::unique_ptr<InputEvent>, const gfx::PointF&)); MOCK_METHOD2(OnScrollEnd, - void(std::unique_ptr<blink::WebGestureEvent>, - const gfx::PointF&)); + void(std::unique_ptr<InputEvent>, const gfx::PointF&)); MOCK_METHOD0(MockedOnScrollBegin, void()); MOCK_METHOD1(OnFocusChanged, void(bool)); MOCK_METHOD1(OnInputEdited, void(const EditedText&)); @@ -134,9 +131,8 @@ &reticle_model_, &gesture_list_); } - void AddGesture(blink::WebGestureEvent::Type type) { - auto gesture = std::make_unique<blink::WebGestureEvent>(); - gesture->SetType(type); + void AddGesture(InputEvent::Type type) { + auto gesture = std::make_unique<InputEvent>(type); gesture_list_.push_back(std::move(gesture)); } @@ -145,7 +141,7 @@ std::unique_ptr<UiInputManager> input_manager_; ReticleModel reticle_model_; ControllerModel controller_model_; - GestureList gesture_list_; + InputEventList gesture_list_; InSequence inSequence; }; @@ -268,15 +264,15 @@ controller_model.laser_origin = {0, 0, 0}; controller_model.touchpad_button_state = kUp; ReticleModel reticle_model; - GestureList gesture_list; + InputEventList input_event_list; input_manager_->HandleInput(MsToTicks(1), RenderInfo(), controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); EXPECT_EQ(0, reticle_model.target_element_id); controller_model.laser_direction = kForwardVector; input_manager_->HandleInput(MsToTicks(1), RenderInfo(), controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); EXPECT_EQ(p_element->id(), reticle_model.target_element_id); EXPECT_NEAR(-1.0, reticle_model.target_point.z(), kEpsilon); } @@ -377,25 +373,25 @@ p_back_element->set_scrollable(true); // Scroll on an element. - AddGesture(blink::WebGestureEvent::kGestureScrollBegin); + AddGesture(InputEvent::kScrollBegin); EXPECT_CALL(*p_front_element, OnHoverEnter(_, _)); EXPECT_CALL(*p_front_element, OnScrollBegin(_, _)); HandleInput(kForwardVector, kUp); EXPECT_TRUE(gesture_list_.empty()); - AddGesture(blink::WebGestureEvent::kGestureScrollUpdate); + AddGesture(InputEvent::kScrollUpdate); EXPECT_CALL(*p_front_element, OnScrollUpdate(_, _)); HandleInput(kForwardVector, kUp); EXPECT_TRUE(gesture_list_.empty()); // Move away. - AddGesture(blink::WebGestureEvent::kGestureScrollUpdate); + AddGesture(InputEvent::kScrollUpdate); EXPECT_CALL(*p_front_element, OnHoverLeave(_)); EXPECT_CALL(*p_front_element, OnScrollUpdate(_, _)); HandleInput(kBackwardVector, kUp); EXPECT_TRUE(gesture_list_.empty()); // Release scroll. - AddGesture(blink::WebGestureEvent::kGestureScrollEnd); + AddGesture(InputEvent::kScrollEnd); EXPECT_CALL(*p_front_element, OnScrollEnd(_, _)); EXPECT_CALL(*p_back_element, OnHoverEnter(_, _)); HandleInput(kBackwardVector, kUp); @@ -403,7 +399,7 @@ // Start scrolling on a new element. EXPECT_CALL(*p_back_element, OnHoverMove(_, _)); - AddGesture(blink::WebGestureEvent::kGestureScrollBegin); + AddGesture(InputEvent::kScrollBegin); EXPECT_CALL(*p_back_element, OnScrollBegin(_, _)); HandleInput(kBackwardVector, kUp); EXPECT_TRUE(gesture_list_.empty()); @@ -421,7 +417,7 @@ child->set_focusable(true); p_element->AddChild(std::move(child)); - AddGesture(blink::WebGestureEvent::kGestureScrollBegin); + AddGesture(InputEvent::kScrollBegin); EXPECT_CALL(*p_element, OnHoverEnter(_, _)); EXPECT_CALL(*p_element, OnScrollBegin(_, _)); EXPECT_CALL(*p_child, OnScrollBegin(_, _)).Times(0); @@ -508,9 +504,9 @@ controller_model.laser_origin = origin; controller_model.touchpad_button_state = UiInputManager::ButtonState::DOWN; ReticleModel reticle_model; - GestureList gesture_list; + InputEventList input_event_list; input_manager_->HandleInput(MsToTicks(1), RenderInfo(), controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); // We should have hit the content quad if our math was correct. ASSERT_NE(0, reticle_model.target_element_id); @@ -522,7 +518,7 @@ EXPECT_CALL(*content_input_delegate_, OnHoverMove(_, _)).Times(0); input_manager_->HandleInput(MsToTicks(1), RenderInfo(), controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); } TEST_F(UiInputManagerContentTest, AudioPermissionPromptHitTesting) { @@ -539,9 +535,9 @@ controller_model.laser_origin = origin; controller_model.touchpad_button_state = UiInputManager::ButtonState::DOWN; ReticleModel reticle_model; - GestureList gesture_list; + InputEventList input_event_list; input_manager_->HandleInput(MsToTicks(1), RenderInfo(), controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); // Even if the reticle is over the URL bar, the backplane should be in front // and should be hit. @@ -565,9 +561,9 @@ controller_model.laser_origin = origin; controller_model.touchpad_button_state = UiInputManager::ButtonState::DOWN; ReticleModel reticle_model; - GestureList gesture_list; + InputEventList input_event_list; input_manager_->HandleInput(MsToTicks(1), RenderInfo(), controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); // We should have hit the content quad if our math was correct. ASSERT_NE(0, reticle_model.target_element_id); @@ -578,7 +574,7 @@ OnBeginFrame(); input_manager_->HandleInput(MsToTicks(1), RenderInfo(), controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); // We should have hit the content quad even though, geometrically, it stacks // behind the backplane. @@ -598,14 +594,14 @@ controller_center.x(), controller_center.y(), controller_center.z()); controller_model.laser_origin = controller_center; ReticleModel reticle_model; - GestureList gesture_list; + InputEventList input_event_list; RenderInfo render_info = CreateRenderInfo(); // The controller is initially not in the viewport. EXPECT_FALSE(input_manager_->controller_resting_in_viewport()); input_manager_->HandleInput(MsToTicks(1), render_info, controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); ui_->OnControllerUpdated(controller_model, reticle_model); scene_->OnBeginFrame(base::TimeTicks(), head_pose_); @@ -614,7 +610,7 @@ EXPECT_FALSE(input_manager_->controller_resting_in_viewport()); input_manager_->HandleInput(MsToTicks(50000), render_info, controller_model, - &reticle_model, &gesture_list); + &reticle_model, &input_event_list); ui_->OnControllerUpdated(controller_model, reticle_model); scene_->OnBeginFrame(base::TimeTicks(), head_pose_);
diff --git a/chrome/browser/vr/ui_unittest.cc b/chrome/browser/vr/ui_unittest.cc index be436ce..d3ee3c7 100644 --- a/chrome/browser/vr/ui_unittest.cc +++ b/chrome/browser/vr/ui_unittest.cc
@@ -32,7 +32,6 @@ #include "components/omnibox/browser/autocomplete_match.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/web_gesture_event.h" namespace vr {
diff --git a/chrome/chrome_cleaner/constants/BUILD.gn b/chrome/chrome_cleaner/constants/BUILD.gn index f582818a..cbd10bf 100644 --- a/chrome/chrome_cleaner/constants/BUILD.gn +++ b/chrome/chrome_cleaner/constants/BUILD.gn
@@ -3,6 +3,45 @@ # found in the LICENSE file. import("//build/config/chrome_build.gni") +import("//build/util/process_version.gni") + +declare_args() { + reporter_branding_path = "REPORTER_BRANDING" + cleaner_branding_path = "CLEANER_BRANDING" + version_path = "VERSION" + lastchange_path = "//build/util/LASTCHANGE" +} + +process_version("version_header") { + sources = [ + cleaner_branding_path, + lastchange_path, + version_path, + ] + + template_file = "version.h.in" + output = "$target_gen_dir/version.h" +} + +process_version("chrome_cleanup_tool_branding_header") { + sources = [ + cleaner_branding_path, + ] + + template_file = "branding.h.in" + + output = "$target_gen_dir/chrome_cleanup_tool_branding.h" +} + +process_version("software_reporter_tool_branding_header") { + sources = [ + reporter_branding_path, + ] + + template_file = "branding.h.in" + + output = "$target_gen_dir/software_reporter_tool_branding.h" +} source_set("common_strings") { sources = [
diff --git a/chrome/chrome_cleaner/constants/CLEANER_BRANDING b/chrome/chrome_cleaner/constants/CLEANER_BRANDING new file mode 100644 index 0000000..be44837 --- /dev/null +++ b/chrome/chrome_cleaner/constants/CLEANER_BRANDING
@@ -0,0 +1,7 @@ +COMPANY_FULLNAME=The Chromium Authors +COMPANY_SHORTNAME=The Chromium Authors +PRODUCT_FULLNAME=Chrome Cleanup Tool +PRODUCT_SHORTNAME=Chrome Cleanup Tool +PRODUCT_INSTALLER_FULLNAME=Chrome Cleanup Tool Installer +PRODUCT_INSTALLER_SHORTNAME=Chrome Cleanup Tool Installer +COPYRIGHT=Copyright 2015 Google Inc. All Rights Reserved.
diff --git a/chrome/chrome_cleaner/constants/REPORTER_BRANDING b/chrome/chrome_cleaner/constants/REPORTER_BRANDING new file mode 100644 index 0000000..ce00d89f --- /dev/null +++ b/chrome/chrome_cleaner/constants/REPORTER_BRANDING
@@ -0,0 +1,7 @@ +COMPANY_FULLNAME=The Chromium Authors +COMPANY_SHORTNAME=The Chromium Authors +PRODUCT_FULLNAME=Software Reporter Tool +PRODUCT_SHORTNAME=Software Reporter Tool +PRODUCT_INSTALLER_FULLNAME=Software Reporter Tool Installer +PRODUCT_INSTALLER_SHORTNAME=Software Reporter Tool Installer +COPYRIGHT=Copyright 2015 Google Inc. All Rights Reserved.
diff --git a/chrome/chrome_cleaner/constants/VERSION b/chrome/chrome_cleaner/constants/VERSION new file mode 100644 index 0000000..3895a61 --- /dev/null +++ b/chrome/chrome_cleaner/constants/VERSION
@@ -0,0 +1,4 @@ +MAJOR=0 +MINOR=0 +BUILD=0 +PATCH=0
diff --git a/chrome/chrome_cleaner/constants/branding.h.in b/chrome/chrome_cleaner/constants/branding.h.in new file mode 100644 index 0000000..b68df2b --- /dev/null +++ b/chrome/chrome_cleaner/constants/branding.h.in
@@ -0,0 +1,9 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// chrome_cleanup_tool_branding.h and software_reporter_tool_branding.h are +// generated from branding.h.in. Edit the source! + +#define PRODUCT_FULLNAME_STRING L"@PRODUCT_FULLNAME@" +#define PRODUCT_SHORTNAME_STRING L"@PRODUCT_SHORTNAME@"
diff --git a/chrome/chrome_cleaner/constants/version.h.in b/chrome/chrome_cleaner/constants/version.h.in new file mode 100644 index 0000000..a4943f7 --- /dev/null +++ b/chrome/chrome_cleaner/constants/version.h.in
@@ -0,0 +1,25 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// version.h is generated from version.h.in. Edit the source! + +// See also software_reporter_tool_branding.h and +// chrome_cleanup_tool_branding.h for branding that varies by executable. + +// Version Information + +#define CHROME_VERSION @MAJOR@,@MINOR@,@BUILD@ +#define CHROME_VERSION_STRING L"@MAJOR@.@MINOR@.@BUILD@" +#define CHROME_VERSION_UTF8_STRING "@MAJOR@.@MINOR@.@BUILD@" + +// Branding Information + +#define COMPANY_FULLNAME_STRING L"@COMPANY_FULLNAME@" +#define COMPANY_SHORTNAME_STRING L"@COMPANY_SHORTNAME@" +#define COPYRIGHT_STRING L"@COPYRIGHT@" +#define OFFICIAL_BUILD_STRING L"@OFFICIAL_BUILD@" + +// Changelist Information + +#define LASTCHANGE_STRING L"@LASTCHANGE@"
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc index 9cd4c88..4caceebd 100644 --- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc +++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -3424,8 +3424,9 @@ SimulateUserInputChangeForElement(&confirmation_password, ""); EXPECT_EQ(0, fake_driver_.called_show_manual_fallback_for_saving_count()); } -// Tests that information about Gaia reauthentication form is not sent to the -// browser, nor on load nor on user click. + +// Tests that information about Gaia reauthentication form is sent to the +// browser with information that the password should not be saved. TEST_F(PasswordAutofillAgentTest, GaiaReauthenticationFormIgnored) { // HTML is already loaded in test SetUp method, so information about password // forms was already sent to the |fake_drive_|. Hence it should be reset. @@ -3447,9 +3448,12 @@ // Simulate a user clicking on the password element. autofill_agent_->FormControlElementClicked(password_element_, false); - // Check that no information about Gaia reauthentication is not sent. - EXPECT_FALSE(fake_driver_.called_password_forms_parsed()); - EXPECT_FALSE(fake_driver_.called_password_forms_rendered()); + // Check that information about Gaia reauthentication is sent to the browser. + ASSERT_TRUE(fake_driver_.called_password_forms_parsed()); + const std::vector<autofill::PasswordForm>& parsed_forms = + fake_driver_.password_forms_parsed().value(); + ASSERT_EQ(1u, parsed_forms.size()); + EXPECT_TRUE(parsed_forms[0].is_gaia_with_skip_save_password_form); } // Tests that "Show all saved passwords" option is shown on a password field.
diff --git a/chrome/renderer/pepper/pepper_flash_font_file_host.cc b/chrome/renderer/pepper/pepper_flash_font_file_host.cc index 4313c6a..981e54e3 100644 --- a/chrome/renderer/pepper/pepper_flash_font_file_host.cc +++ b/chrome/renderer/pepper/pepper_flash_font_file_host.cc
@@ -13,12 +13,38 @@ #include "ppapi/host/ppapi_host.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/serialized_structs.h" - -#if defined(OS_LINUX) || defined(OS_OPENBSD) -#include "content/public/child/child_process_sandbox_support_linux.h" -#include "content/public/common/common_sandbox_support_linux.h" -#elif defined(OS_WIN) #include "third_party/skia/include/core/SkFontMgr.h" + +#if !defined(OS_WIN) +namespace { + +SkFontStyle::Weight PepperWeightToSkWeight(int pepper_weight) { + switch (pepper_weight) { + case PP_BROWSERFONT_TRUSTED_WEIGHT_100: + return SkFontStyle::kThin_Weight; + case PP_BROWSERFONT_TRUSTED_WEIGHT_200: + return SkFontStyle::kExtraLight_Weight; + case PP_BROWSERFONT_TRUSTED_WEIGHT_300: + return SkFontStyle::kLight_Weight; + case PP_BROWSERFONT_TRUSTED_WEIGHT_400: + return SkFontStyle::kNormal_Weight; + case PP_BROWSERFONT_TRUSTED_WEIGHT_500: + return SkFontStyle::kMedium_Weight; + case PP_BROWSERFONT_TRUSTED_WEIGHT_600: + return SkFontStyle::kSemiBold_Weight; + case PP_BROWSERFONT_TRUSTED_WEIGHT_700: + return SkFontStyle::kBold_Weight; + case PP_BROWSERFONT_TRUSTED_WEIGHT_800: + return SkFontStyle::kExtraBold_Weight; + case PP_BROWSERFONT_TRUSTED_WEIGHT_900: + return SkFontStyle::kBlack_Weight; + default: + NOTREACHED(); + return SkFontStyle::kInvisible_Weight; + } +} + +} // namespace #endif PepperFlashFontFileHost::PepperFlashFontFileHost( @@ -28,24 +54,23 @@ const ppapi::proxy::SerializedFontDescription& description, PP_PrivateFontCharset charset) : ResourceHost(host->GetPpapiHost(), instance, resource) { -#if defined(OS_LINUX) || defined(OS_OPENBSD) - fd_.reset(content::MatchFontWithFallback( - description.face, - description.weight >= PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD, - description.italic, - charset, - PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT)); -#elif defined(OS_WIN) - int weight = description.weight; + int weight; +#if defined(OS_WIN) + // TODO(https://crbug.com/857388): Figure out why this diverges from the + // #else block. + weight = description.weight; if (weight == FW_DONTCARE) weight = SkFontStyle::kNormal_Weight; +#else + weight = PepperWeightToSkWeight(description.weight); +#endif SkFontStyle style(weight, SkFontStyle::kNormal_Width, description.italic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant); sk_sp<SkFontMgr> font_mgr(SkFontMgr::RefDefault()); + typeface_ = sk_sp<SkTypeface>( font_mgr->matchFamilyStyle(description.face.c_str(), style)); -#endif // defined(OS_LINUX) || defined(OS_OPENBSD) } PepperFlashFontFileHost::~PepperFlashFontFileHost() {} @@ -64,12 +89,6 @@ void* buffer, size_t* length) { bool result = false; -#if defined(OS_LINUX) || defined(OS_OPENBSD) - int fd = fd_.get(); - if (fd != -1) - result = content::GetFontTable(fd, table, 0 /* offset */, - reinterpret_cast<uint8_t*>(buffer), length); -#elif defined(OS_WIN) if (typeface_) { table = base::ByteSwap(table); if (buffer == NULL) { @@ -82,7 +101,6 @@ result = true; } } -#endif return result; }
diff --git a/chrome/renderer/pepper/pepper_flash_font_file_host.h b/chrome/renderer/pepper/pepper_flash_font_file_host.h index 3e9ba7d..6b67a7c 100644 --- a/chrome/renderer/pepper/pepper_flash_font_file_host.h +++ b/chrome/renderer/pepper/pepper_flash_font_file_host.h
@@ -14,12 +14,8 @@ #include "ppapi/c/private/pp_private_font_charset.h" #include "ppapi/host/resource_host.h" -#if defined(OS_LINUX) || defined(OS_OPENBSD) -#include "base/files/scoped_file.h" -#elif defined(OS_WIN) #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkTypeface.h" -#endif namespace content { class RendererPpapiHost; @@ -50,11 +46,7 @@ uint32_t table); bool GetFontData(uint32_t table, void* buffer, size_t* length); -#if defined(OS_LINUX) || defined(OS_OPENBSD) - base::ScopedFD fd_; -#elif defined(OS_WIN) sk_sp<SkTypeface> typeface_; -#endif DISALLOW_COPY_AND_ASSIGN(PepperFlashFontFileHost); };
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 41079e8..d5417c5c 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -503,7 +503,6 @@ "../browser/chrome_find_request_manager_browsertest.cc", "../browser/chrome_main_browsertest.cc", "../browser/chrome_navigation_browsertest.cc", - "../browser/chrome_network_service_browsertest.cc", "../browser/chrome_network_service_restart_browsertest.cc", "../browser/chrome_origin_trials_browsertest.cc", "../browser/chrome_plugin_browsertest.cc",
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn index ff14588..732de74 100644 --- a/chrome/utility/BUILD.gn +++ b/chrome/utility/BUILD.gn
@@ -199,6 +199,10 @@ if (is_android || enable_extensions) { deps += [ "//chrome/services/media_gallery_util:lib" ] } + + if (is_linux && !is_android) { + deps += [ "//components/services/font:lib" ] + } } if (!is_android) {
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn index 3ba9532..0fa8be5 100644 --- a/chromecast/browser/BUILD.gn +++ b/chromecast/browser/BUILD.gn
@@ -201,6 +201,7 @@ deps += [ "//components/metrics:serialization", + "//components/services/font:lib", "//third_party/fontconfig", ] }
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS index aea8f3b..225a17d1 100644 --- a/chromecast/browser/DEPS +++ b/chromecast/browser/DEPS
@@ -16,6 +16,7 @@ "+components/prefs", "+components/pref_registry", "+components/proxy_config", + "+components/services/font", "+components/storage_monitor", "+components/user_prefs", "+components/version_info",
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc index 05b22d807..2d914a59 100644 --- a/chromecast/browser/cast_content_browser_client.cc +++ b/chromecast/browser/cast_content_browser_client.cc
@@ -76,6 +76,11 @@ #include "media/mojo/services/media_service.h" // nogncheck #endif // ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS +#if defined(OS_LINUX) +#include "components/services/font/font_service_app.h" // nogncheck +#include "components/services/font/public/interfaces/constants.mojom.h" // nogncheck +#endif + #if defined(OS_LINUX) || defined(OS_ANDROID) #include "components/crash/content/app/breakpad_linux.h" #include "components/crash/content/browser/crash_handler_host_linux.h" @@ -683,6 +688,14 @@ #endif } +void CastContentBrowserClient::RegisterOutOfProcessServices( + OutOfProcessServiceMap* services) { +#if defined(OS_LINUX) + (*services)[font_service::mojom::kServiceName] = + base::BindRepeating(&base::ASCIIToUTF16, "Font Service"); +#endif +} + std::unique_ptr<base::Value> CastContentBrowserClient::GetServiceManifestOverlay( base::StringPiece service_name) {
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h index 8f9502c..a61ea2db 100644 --- a/chromecast/browser/cast_content_browser_client.h +++ b/chromecast/browser/cast_content_browser_client.h
@@ -183,6 +183,7 @@ void RegisterInProcessServices( StaticServiceMap* services, content::ServiceManagerConnection* connection) override; + void RegisterOutOfProcessServices(OutOfProcessServiceMap* services) override; std::unique_ptr<base::Value> GetServiceManifestOverlay( base::StringPiece service_name) override; void GetAdditionalMappedFilesForChildProcess(
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn index c61a797..2ead21f 100644 --- a/chromeos/services/device_sync/BUILD.gn +++ b/chromeos/services/device_sync/BUILD.gn
@@ -10,12 +10,8 @@ static_library("device_sync") { sources = [ - "cryptauth_client_factory_impl.cc", - "cryptauth_client_factory_impl.h", "cryptauth_enroller_factory_impl.cc", "cryptauth_enroller_factory_impl.h", - "cryptauth_token_fetcher_impl.cc", - "cryptauth_token_fetcher_impl.h", "device_sync_base.cc", "device_sync_base.h", "device_sync_impl.cc", @@ -75,7 +71,6 @@ testonly = true sources = [ - "cryptauth_token_fetcher_impl_unittest.cc", "device_sync_service_unittest.cc", ]
diff --git a/chromeos/services/device_sync/cryptauth_client_factory_impl.cc b/chromeos/services/device_sync/cryptauth_client_factory_impl.cc deleted file mode 100644 index 29ca54f..0000000 --- a/chromeos/services/device_sync/cryptauth_client_factory_impl.cc +++ /dev/null
@@ -1,34 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chromeos/services/device_sync/cryptauth_client_factory_impl.h" - -#include "chromeos/services/device_sync/cryptauth_token_fetcher_impl.h" -#include "components/cryptauth/cryptauth_client_impl.h" - -namespace chromeos { - -namespace device_sync { - -CryptAuthClientFactoryImpl::CryptAuthClientFactoryImpl( - identity::IdentityManager* identity_manager, - scoped_refptr<net::URLRequestContextGetter> url_request_context, - const cryptauth::DeviceClassifier& device_classifier) - : identity_manager_(identity_manager), - url_request_context_(std::move(url_request_context)), - device_classifier_(device_classifier) {} - -CryptAuthClientFactoryImpl::~CryptAuthClientFactoryImpl() = default; - -std::unique_ptr<cryptauth::CryptAuthClient> -CryptAuthClientFactoryImpl::CreateInstance() { - return std::make_unique<cryptauth::CryptAuthClientImpl>( - std::make_unique<cryptauth::CryptAuthApiCallFlow>(), - std::make_unique<CryptAuthAccessTokenFetcherImpl>(identity_manager_), - url_request_context_, device_classifier_); -} - -} // namespace device_sync - -} // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_client_factory_impl.h b/chromeos/services/device_sync/cryptauth_client_factory_impl.h deleted file mode 100644 index 9786627..0000000 --- a/chromeos/services/device_sync/cryptauth_client_factory_impl.h +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_CLIENT_FACTORY_IMPL_H_ -#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_CLIENT_FACTORY_IMPL_H_ - -#include "base/memory/ref_counted.h" -#include "components/cryptauth/cryptauth_client.h" -#include "components/cryptauth/proto/cryptauth_api.pb.h" - -namespace identity { -class IdentityManager; -} // namespace identity - -namespace net { -class URLRequestContextGetter; -} // namespace net - -namespace chromeos { - -namespace device_sync { - -// CryptAuthClientFactory implementation which utilizes IdentityManager. -class CryptAuthClientFactoryImpl : public cryptauth::CryptAuthClientFactory { - public: - CryptAuthClientFactoryImpl( - identity::IdentityManager* identity_manager, - scoped_refptr<net::URLRequestContextGetter> url_request_context, - const cryptauth::DeviceClassifier& device_classifier); - ~CryptAuthClientFactoryImpl() override; - - // cryptauth::CryptAuthClientFactory: - std::unique_ptr<cryptauth::CryptAuthClient> CreateInstance() override; - - private: - identity::IdentityManager* identity_manager_; - const scoped_refptr<net::URLRequestContextGetter> url_request_context_; - const cryptauth::DeviceClassifier device_classifier_; -}; - -} // namespace device_sync - -} // namespace chromeos - -#endif // CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_CLIENT_FACTORY_IMPL_H_
diff --git a/chromeos/services/device_sync/cryptauth_token_fetcher_impl.cc b/chromeos/services/device_sync/cryptauth_token_fetcher_impl.cc deleted file mode 100644 index 32bc5ea..0000000 --- a/chromeos/services/device_sync/cryptauth_token_fetcher_impl.cc +++ /dev/null
@@ -1,69 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chromeos/services/device_sync/cryptauth_token_fetcher_impl.h" - -#include <set> - -#include "services/identity/public/cpp/identity_manager.h" - -namespace chromeos { - -namespace device_sync { - -namespace { - -const char kIdentityManagerConsumerName[] = "multi_device_service"; -const char kCryptAuthApiScope[] = "https://www.googleapis.com/auth/cryptauth"; - -} // namespace - -CryptAuthAccessTokenFetcherImpl::CryptAuthAccessTokenFetcherImpl( - identity::IdentityManager* identity_manager) - : identity_manager_(identity_manager), weak_ptr_factory_(this) {} - -CryptAuthAccessTokenFetcherImpl::~CryptAuthAccessTokenFetcherImpl() { - // If any callbacks are still pending when the object is deleted, run them - // now, passing an empty string to indicate failure. This ensures that no - // callbacks are left hanging forever. - InvokeThenClearPendingCallbacks(std::string()); -} - -void CryptAuthAccessTokenFetcherImpl::FetchAccessToken( - const AccessTokenCallback& callback) { - pending_callbacks_.emplace_back(callback); - - // If the token is already being fetched, continue waiting for it. - if (access_token_fetcher_) - return; - - const OAuth2TokenService::ScopeSet kScopes{kCryptAuthApiScope}; - access_token_fetcher_ = - identity_manager_->CreateAccessTokenFetcherForPrimaryAccount( - kIdentityManagerConsumerName, kScopes, - base::BindOnce(&CryptAuthAccessTokenFetcherImpl::OnAccessTokenFetched, - weak_ptr_factory_.GetWeakPtr()), - identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate); -} - -void CryptAuthAccessTokenFetcherImpl::InvokeThenClearPendingCallbacks( - const std::string& access_token) { - for (auto& callback : pending_callbacks_) - callback.Run(access_token); - - pending_callbacks_.clear(); -} - -void CryptAuthAccessTokenFetcherImpl::OnAccessTokenFetched( - GoogleServiceAuthError error, - std::string access_token) { - access_token_fetcher_.reset(); - InvokeThenClearPendingCallbacks( - error == GoogleServiceAuthError::AuthErrorNone() ? access_token - : std::string()); -} - -} // namespace device_sync - -} // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_token_fetcher_impl.h b/chromeos/services/device_sync/cryptauth_token_fetcher_impl.h deleted file mode 100644 index 69d52d6..0000000 --- a/chromeos/services/device_sync/cryptauth_token_fetcher_impl.h +++ /dev/null
@@ -1,54 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_TOKEN_FETCHER_IMPL_H_ -#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_TOKEN_FETCHER_IMPL_H_ - -#include <memory> -#include <vector> - -#include "base/memory/weak_ptr.h" -#include "components/cryptauth/cryptauth_access_token_fetcher.h" -#include "google_apis/gaia/google_service_auth_error.h" - -namespace identity { -class IdentityManager; -class PrimaryAccountAccessTokenFetcher; -} // namespace identity - -namespace chromeos { - -namespace device_sync { - -// CryptAuthAccessTokenFetcher implementation which utilizes IdentityManager. -class CryptAuthAccessTokenFetcherImpl - : public cryptauth::CryptAuthAccessTokenFetcher { - public: - explicit CryptAuthAccessTokenFetcherImpl( - identity::IdentityManager* identity_manager); - - ~CryptAuthAccessTokenFetcherImpl() override; - - // cryptauth::CryptAuthAccessTokenFetcher: - void FetchAccessToken(const AccessTokenCallback& callback) override; - - private: - void InvokeThenClearPendingCallbacks(const std::string& access_token); - void OnAccessTokenFetched(GoogleServiceAuthError error, - std::string access_token); - - identity::IdentityManager* identity_manager_; - - std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher> - access_token_fetcher_; - std::vector<AccessTokenCallback> pending_callbacks_; - - base::WeakPtrFactory<CryptAuthAccessTokenFetcherImpl> weak_ptr_factory_; -}; - -} // namespace device_sync - -} // namespace chromeos - -#endif // CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_TOKEN_FETCHER_IMPL_H_
diff --git a/chromeos/services/device_sync/cryptauth_token_fetcher_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_token_fetcher_impl_unittest.cc deleted file mode 100644 index 2ef43210..0000000 --- a/chromeos/services/device_sync/cryptauth_token_fetcher_impl_unittest.cc +++ /dev/null
@@ -1,132 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chromeos/services/device_sync/cryptauth_token_fetcher_impl.h" - -#include "base/callback.h" -#include "base/macros.h" -#include "base/run_loop.h" -#include "base/test/scoped_task_environment.h" -#include "services/identity/public/cpp/identity_test_environment.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace chromeos { - -namespace device_sync { - -namespace { - -const char kAccessToken[] = "access_token"; -const char kTestEmail[] = "example@gmail.com"; - -} // namespace - -class DeviceSyncCryptAuthAccessTokenFetcherImplTest : public testing::Test { - protected: - DeviceSyncCryptAuthAccessTokenFetcherImplTest() {} - - void SetUp() override { - identity_test_environment_ = - std::make_unique<identity::IdentityTestEnvironment>(); - identity_test_environment_->MakePrimaryAccountAvailable(kTestEmail); - - token_fetcher_ = std::make_unique<CryptAuthAccessTokenFetcherImpl>( - identity_test_environment_->identity_manager()); - } - - void TearDown() override { - // Deleting the object should not result in any additional tokens provided. - token_fetcher_.reset(); - EXPECT_EQ(nullptr, GetTokenAndReset()); - - EXPECT_TRUE(on_access_token_received_callback_.is_null()); - } - - void set_on_access_token_received_callback( - base::OnceClosure on_access_token_received_callback) { - EXPECT_TRUE(on_access_token_received_callback_.is_null()); - on_access_token_received_callback_ = - std::move(on_access_token_received_callback); - } - - void StartFetchingAccessToken() { - token_fetcher_->FetchAccessToken(base::Bind( - &DeviceSyncCryptAuthAccessTokenFetcherImplTest::OnAccessTokenFetched, - base::Unretained(this))); - } - - void OnAccessTokenFetched(const std::string& access_token) { - last_access_token_ = std::make_unique<std::string>(access_token); - if (!on_access_token_received_callback_.is_null()) - std::move(on_access_token_received_callback_).Run(); - } - - std::unique_ptr<std::string> GetTokenAndReset() { - return std::move(last_access_token_); - } - - const base::test::ScopedTaskEnvironment scoped_task_environment_; - - std::unique_ptr<std::string> last_access_token_; - base::OnceClosure on_access_token_received_callback_; - - std::unique_ptr<identity::IdentityTestEnvironment> identity_test_environment_; - std::unique_ptr<CryptAuthAccessTokenFetcherImpl> token_fetcher_; - - private: - DISALLOW_COPY_AND_ASSIGN(DeviceSyncCryptAuthAccessTokenFetcherImplTest); -}; - -TEST_F(DeviceSyncCryptAuthAccessTokenFetcherImplTest, TestSuccess) { - base::RunLoop run_loop; - set_on_access_token_received_callback(run_loop.QuitClosure()); - - // Start fetching the token. Nothing should be returned yet since the message - // loop has not been run. - StartFetchingAccessToken(); - EXPECT_EQ(nullptr, GetTokenAndReset()); - - identity_test_environment_ - ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( - kAccessToken, base::Time::Max() /* expiration */); - - // Run the request and confirm that the access token was returned. - run_loop.Run(); - EXPECT_EQ(kAccessToken, *GetTokenAndReset()); -} - -TEST_F(DeviceSyncCryptAuthAccessTokenFetcherImplTest, TestFailure) { - base::RunLoop run_loop; - set_on_access_token_received_callback(run_loop.QuitClosure()); - - // Start fetching the token. Nothing should be returned yet since the message - // loop has not been run. - StartFetchingAccessToken(); - EXPECT_EQ(nullptr, GetTokenAndReset()); - - identity_test_environment_ - ->WaitForAccessTokenRequestIfNecessaryAndRespondWithError( - GoogleServiceAuthError( - GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS)); - - // Run the request and confirm that an empty string was returned, signifying a - // failure to fetch the token. - run_loop.Run(); - EXPECT_EQ(std::string(), *GetTokenAndReset()); -} - -TEST_F(DeviceSyncCryptAuthAccessTokenFetcherImplTest, - TestDeletedBeforeOperationFinished) { - StartFetchingAccessToken(); - EXPECT_EQ(nullptr, GetTokenAndReset()); - - // Deleting the object should result in the callback being invoked with an - // empty string (indicating failure). - token_fetcher_.reset(); - EXPECT_EQ(std::string(), *GetTokenAndReset()); -} - -} // namespace device_sync - -} // namespace chromeos
diff --git a/chromeos/services/device_sync/device_sync_impl.cc b/chromeos/services/device_sync/device_sync_impl.cc index 22a6ea5c..ea2ff50 100644 --- a/chromeos/services/device_sync/device_sync_impl.cc +++ b/chromeos/services/device_sync/device_sync_impl.cc
@@ -9,8 +9,8 @@ #include "base/optional.h" #include "base/time/default_clock.h" #include "chromeos/components/proximity_auth/logging/logging.h" -#include "chromeos/services/device_sync/cryptauth_client_factory_impl.h" #include "chromeos/services/device_sync/cryptauth_enroller_factory_impl.h" +#include "components/cryptauth/cryptauth_client_impl.h" #include "components/cryptauth/cryptauth_device_manager_impl.h" #include "components/cryptauth/cryptauth_enrollment_manager_impl.h" #include "components/cryptauth/cryptauth_gcm_manager_impl.h" @@ -324,9 +324,10 @@ gcm_driver_, pref_service_.get()); cryptauth_gcm_manager_->StartListening(); - cryptauth_client_factory_ = std::make_unique<CryptAuthClientFactoryImpl>( - identity_manager_, url_request_context_, - cryptauth::device_classifier_util::GetDeviceClassifier()); + cryptauth_client_factory_ = + std::make_unique<cryptauth::CryptAuthClientFactoryImpl>( + identity_manager_, url_request_context_, + cryptauth::device_classifier_util::GetDeviceClassifier()); // Initialize |crypauth_device_manager_| and start observing. Start() is not // called yet since the device has not completed enrollment.
diff --git a/components/autofill/content/common/autofill_types.mojom b/components/autofill/content/common/autofill_types.mojom index 71e5244..2b0820e 100644 --- a/components/autofill/content/common/autofill_types.mojom +++ b/components/autofill/content/common/autofill_types.mojom
@@ -247,6 +247,7 @@ bool is_affiliation_based_match; PasswordFormSubmissionIndicatorEvent submission_event; bool only_for_fallback_saving; + bool is_gaia_with_skip_save_password_form; }; // TODO(leonhsl): Use map directly after http://crbug.com/628104 solved.
diff --git a/components/autofill/content/common/autofill_types_struct_traits.cc b/components/autofill/content/common/autofill_types_struct_traits.cc index f0cb980..f1d14d1 100644 --- a/components/autofill/content/common/autofill_types_struct_traits.cc +++ b/components/autofill/content/common/autofill_types_struct_traits.cc
@@ -795,6 +795,8 @@ out->is_public_suffix_match = data.is_public_suffix_match(); out->is_affiliation_based_match = data.is_affiliation_based_match(); out->only_for_fallback_saving = data.only_for_fallback_saving(); + out->is_gaia_with_skip_save_password_form = + data.is_gaia_with_skip_save_password_form(); return true; }
diff --git a/components/autofill/content/common/autofill_types_struct_traits.h b/components/autofill/content/common/autofill_types_struct_traits.h index 2f4cdc23..da498a2 100644 --- a/components/autofill/content/common/autofill_types_struct_traits.h +++ b/components/autofill/content/common/autofill_types_struct_traits.h
@@ -628,6 +628,11 @@ return r.only_for_fallback_saving; } + static bool is_gaia_with_skip_save_password_form( + const autofill::PasswordForm& r) { + return r.is_gaia_with_skip_save_password_form; + } + static bool Read(autofill::mojom::PasswordFormDataView data, autofill::PasswordForm* out); };
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc index f0b3f67..80390a44 100644 --- a/components/autofill/content/renderer/password_autofill_agent.cc +++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -1152,16 +1152,6 @@ std::vector<PasswordForm> password_forms; for (const blink::WebFormElement& form : forms) { - if (IsGaiaReauthenticationForm(form)) { - // Bail if this is a GAIA passwords site reauthentication form, so that - // page will be ignored. - return; - } - if (IsGaiaWithSkipSavePasswordForm(form)) { - // Bail if this is a GAIA enable Chrome sync flow, so that page will be - // ignored. - return; - } if (only_visible) { bool is_form_visible = form_util::AreFormContentsVisible(form); if (logger) {
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc index ba246c3..9325b8a5 100644 --- a/components/autofill/content/renderer/password_form_conversion_utils.cc +++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -924,6 +924,9 @@ password_form->action = form_util::GetCanonicalActionForForm(web_form); if (!password_form->action.is_valid()) return nullptr; + password_form->is_gaia_with_skip_save_password_form = + IsGaiaWithSkipSavePasswordForm(web_form) || + IsGaiaReauthenticationForm(web_form); blink::WebVector<blink::WebFormControlElement> control_elements; web_form.GetFormControlElements(control_elements);
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 55ae4594..8b59c49 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -170,6 +170,8 @@ "webdata/autofill_profile_data_type_controller.h", "webdata/autofill_profile_sync_bridge.cc", "webdata/autofill_profile_sync_bridge.h", + "webdata/autofill_profile_sync_difference_tracker.cc", + "webdata/autofill_profile_sync_difference_tracker.h", "webdata/autofill_profile_syncable_service.cc", "webdata/autofill_profile_syncable_service.h", "webdata/autofill_table.cc", @@ -444,6 +446,7 @@ "validation_unittest.cc", "webdata/autocomplete_sync_bridge_unittest.cc", "webdata/autofill_profile_sync_bridge_unittest.cc", + "webdata/autofill_profile_sync_difference_tracker_unittest.cc", "webdata/autofill_profile_syncable_service_unittest.cc", "webdata/autofill_table_unittest.cc", "webdata/autofill_wallet_metadata_syncable_service_unittest.cc",
diff --git a/components/autofill/core/browser/autofill_profile.cc b/components/autofill/core/browser/autofill_profile.cc index dc16ff8..f96dba386 100644 --- a/components/autofill/core/browser/autofill_profile.cc +++ b/components/autofill/core/browser/autofill_profile.cc
@@ -254,14 +254,14 @@ } AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) { + if (this == &profile) + return *this; + set_use_count(profile.use_count()); set_use_date(profile.use_date()); set_previous_use_date(profile.previous_use_date()); set_modification_date(profile.modification_date()); - if (this == &profile) - return *this; - set_guid(profile.guid()); set_origin(profile.origin()); @@ -465,6 +465,30 @@ return true; } +void AutofillProfile::OverwriteDataFrom(const AutofillProfile& profile) { + // Verified profiles should never be overwritten with unverified data. + DCHECK(!IsVerified() || profile.IsVerified()); + DCHECK_EQ(guid(), profile.guid()); + + // Some fields should not got overwritten by empty values; back-up the + // values. + std::string language_code_value = language_code(); + std::string origin_value = origin(); + int validity_bitfield_value = GetValidityBitfieldValue(); + base::string16 name_full_value = GetRawInfo(NAME_FULL); + + *this = profile; + + if (origin().empty()) + set_origin(origin_value); + if (language_code().empty()) + set_language_code(language_code_value); + if (GetValidityBitfieldValue() == 0) + SetValidityFromBitfieldValue(validity_bitfield_value); + if (!HasRawInfo(NAME_FULL)) + SetRawInfo(NAME_FULL, name_full_value); +} + bool AutofillProfile::MergeDataFrom(const AutofillProfile& profile, const std::string& app_locale) { // Verified profiles should never be overwritten with unverified data.
diff --git a/components/autofill/core/browser/autofill_profile.h b/components/autofill/core/browser/autofill_profile.h index 72288bd..0353a15 100644 --- a/components/autofill/core/browser/autofill_profile.h +++ b/components/autofill/core/browser/autofill_profile.h
@@ -122,6 +122,10 @@ const std::string& app_locale, const ServerFieldTypeSet& types) const; + // Overwrites the data of |this| profile with data from the given |profile|. + // Expects that the profiles have the same guid. + void OverwriteDataFrom(const AutofillProfile& profile); + // Merges the data from |this| profile and the given |profile| into |this| // profile. Expects that |this| and |profile| have already been deemed // mergeable by an AutofillProfileComparator.
diff --git a/components/autofill/core/browser/autofill_profile_sync_util.cc b/components/autofill/core/browser/autofill_profile_sync_util.cc index 3d85dd1..f81dc6f 100644 --- a/components/autofill/core/browser/autofill_profile_sync_util.cc +++ b/components/autofill/core/browser/autofill_profile_sync_util.cc
@@ -29,13 +29,17 @@ return trimmed_value; } +bool IsAutofillProfileSpecificsValid( + const AutofillProfileSpecifics& specifics) { + return base::IsValidGUID(specifics.guid()); +} + } // namespace std::unique_ptr<EntityData> CreateEntityDataFromAutofillProfile( const AutofillProfile& entry) { - if (!base::IsValidGUID(entry.guid())) { - return nullptr; - } + // Validity of the guid is guaranteed by the database layer. + DCHECK(base::IsValidGUID(entry.guid())); auto entity_data = std::make_unique<EntityData>(); entity_data->non_unique_name = entry.guid(); @@ -124,7 +128,7 @@ std::unique_ptr<AutofillProfile> CreateAutofillProfileFromSpecifics( const AutofillProfileSpecifics& specifics) { - if (!base::IsValidGUID(specifics.guid())) { + if (!IsAutofillProfileSpecificsValid(specifics)) { return nullptr; } std::unique_ptr<AutofillProfile> profile = @@ -215,15 +219,14 @@ } std::string GetStorageKeyFromAutofillProfile(const AutofillProfile& entry) { - if (!base::IsValidGUID(entry.guid())) { - return std::string(); - } + // Validity of the guid is guaranteed by the database layer. + DCHECK(base::IsValidGUID(entry.guid())); return entry.guid(); } std::string GetStorageKeyFromAutofillProfileSpecifics( const AutofillProfileSpecifics& specifics) { - if (!base::IsValidGUID(specifics.guid())) { + if (!IsAutofillProfileSpecificsValid(specifics)) { return std::string(); } return specifics.guid();
diff --git a/components/autofill/core/browser/autofill_profile_sync_util.h b/components/autofill/core/browser/autofill_profile_sync_util.h index ef0d3d9d..a435952 100644 --- a/components/autofill/core/browser/autofill_profile_sync_util.h +++ b/components/autofill/core/browser/autofill_profile_sync_util.h
@@ -21,13 +21,13 @@ class AutofillProfile; // Converts the given |entry| into a syncer EntityData with equivalent -// autofill profile specifics. Returns nullptr if |entry| has invalid guid. +// autofill profile specifics. Returns nullptr if |entry| is invalid. // Shortens all string fields to AutofillTable::kMaxDataLength. std::unique_ptr<syncer::EntityData> CreateEntityDataFromAutofillProfile( const AutofillProfile& entry); // Converts the given autofill profile |specifics| into an equivalent -// AutofillProfile. Returns nullptr if |specifics| has invalid guid. +// AutofillProfile. Returns nullptr if |specifics| is invalid. std::unique_ptr<AutofillProfile> CreateAutofillProfileFromSpecifics( const sync_pb::AutofillProfileSpecifics& specifics);
diff --git a/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc b/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc index e0ebdfb..9204eca5 100644 --- a/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc +++ b/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc
@@ -142,14 +142,6 @@ EXPECT_FALSE(entity_data->specifics.autofill_profile().has_company_name()); } -// Test that nullptr is produced if the input guid is invalid. -TEST_F(AutofillProfileSyncUtilTest, - CreateEntityDataFromAutofillProfile_Invalid) { - AutofillProfile profile(kGuidInvalid, std::string()); - - EXPECT_EQ(nullptr, CreateEntityDataFromAutofillProfile(profile)); -} - // Test that long fields get trimmed. TEST_F(AutofillProfileSyncUtilTest, CreateEntityDataFromAutofillProfile_Trimmed) { @@ -275,13 +267,6 @@ EXPECT_EQ(kGuid, GetStorageKeyFromAutofillProfile(profile)); } -// Tests that empty string is returned for entry with invalid guid. -TEST_F(AutofillProfileSyncUtilTest, GetStorageKeyFromAutofillProfile_Invalid) { - AutofillProfile profile(kGuidInvalid, std::string()); - - EXPECT_EQ(std::string(), GetStorageKeyFromAutofillProfile(profile)); -} - // Tests that guid is returned as storage key. TEST_F(AutofillProfileSyncUtilTest, GetStorageKeyFromAutofillProfileSpecifics) { AutofillProfileSpecifics specifics;
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc index d52c7773..3a125fc3 100644 --- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc +++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
@@ -17,6 +17,7 @@ #include "components/autofill/core/browser/country_names.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/proto/autofill_sync.pb.h" +#include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" @@ -105,8 +106,30 @@ syncer::EntityChangeList entity_data) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(entity_data.empty()); - // TODO(jkrcal): Implement non-empty initial merge. + AutofillProfileInitialSyncDifferenceTracker initial_sync_tracker( + GetAutofillTable()); + + for (const auto& change : entity_data) { + DCHECK(change.data().specifics.has_autofill_profile()); + std::unique_ptr<AutofillProfile> remote = + CreateAutofillProfileFromSpecifics( + change.data().specifics.autofill_profile()); + if (!remote) { + DVLOG(2) << "[AUTOFILL SYNC] Invalid remote specifics " + << change.data().specifics.autofill_profile().SerializeAsString() + << " received from the server in an initial sync."; + continue; + } + RETURN_IF_ERROR( + initial_sync_tracker.IncorporateRemoteProfile(std::move(remote))); + } + + RETURN_IF_ERROR( + initial_sync_tracker.MergeSimilarEntriesForInitialSync(app_locale_)); + RETURN_IF_ERROR( + FlushSyncTracker(std::move(metadata_change_list), &initial_sync_tracker)); + + web_data_backend_->NotifyThatSyncHasStarted(syncer::AUTOFILL_PROFILE); return base::nullopt; } @@ -115,9 +138,27 @@ syncer::EntityChangeList entity_changes) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(entity_changes.empty()); - // TODO(jkrcal): Implement non-empty sync changes. - return base::nullopt; + AutofillProfileSyncDifferenceTracker tracker(GetAutofillTable()); + for (const syncer::EntityChange& change : entity_changes) { + if (change.type() == syncer::EntityChange::ACTION_DELETE) { + RETURN_IF_ERROR(tracker.IncorporateRemoteDelete(change.storage_key())); + } else { + DCHECK(change.data().specifics.has_autofill_profile()); + std::unique_ptr<AutofillProfile> remote = + CreateAutofillProfileFromSpecifics( + change.data().specifics.autofill_profile()); + if (!remote) { + DVLOG(2) + << "[AUTOFILL SYNC] Invalid remote specifics " + << change.data().specifics.autofill_profile().SerializeAsString() + << " received from the server in an initial sync."; + continue; + } + RETURN_IF_ERROR(tracker.IncorporateRemoteProfile(std::move(remote))); + } + } + + return FlushSyncTracker(std::move(metadata_change_list), &tracker); } void AutofillProfileSyncBridge::GetData(StorageKeyList storage_keys, @@ -194,6 +235,29 @@ } } +base::Optional<syncer::ModelError> AutofillProfileSyncBridge::FlushSyncTracker( + std::unique_ptr<MetadataChangeList> metadata_change_list, + AutofillProfileSyncDifferenceTracker* tracker) { + DCHECK(tracker); + + RETURN_IF_ERROR(tracker->FlushToLocal( + base::BindOnce(&AutofillWebDataBackend::NotifyOfMultipleAutofillChanges, + base::Unretained(web_data_backend_)))); + + std::vector<std::unique_ptr<AutofillProfile>> profiles_to_upload_to_sync; + RETURN_IF_ERROR(tracker->FlushToSync(&profiles_to_upload_to_sync)); + for (const std::unique_ptr<AutofillProfile>& entry : + profiles_to_upload_to_sync) { + change_processor()->Put(GetStorageKeyFromAutofillProfile(*entry), + CreateEntityDataFromAutofillProfile(*entry), + metadata_change_list.get()); + } + + return static_cast<syncer::SyncMetadataStoreChangeList*>( + metadata_change_list.get()) + ->TakeError(); +} + void AutofillProfileSyncBridge::LoadMetadata() { if (!web_data_backend_ || !web_data_backend_->GetDatabase() || !GetAutofillTable()) {
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h index d961d0b..547ba71 100644 --- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h +++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h
@@ -26,6 +26,7 @@ namespace autofill { +class AutofillProfileSyncDifferenceTracker; class AutofillTable; class AutofillWebDataBackend; class AutofillWebDataService; @@ -87,6 +88,11 @@ // changes. void ActOnLocalChange(const AutofillProfileChange& change); + // Flushes changes accumulated within |tracker| both to local and to sync. + base::Optional<syncer::ModelError> FlushSyncTracker( + std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, + AutofillProfileSyncDifferenceTracker* tracker); + // Synchronously load sync metadata from the autofill table and pass it to the // processor so that it can start tracking changes. void LoadMetadata();
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc index efc4439..f7c032c1 100644 --- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc +++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
@@ -19,6 +19,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/bind_test_util.h" #include "base/test/scoped_task_environment.h" +#include "base/time/time.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_profile_sync_util.h" #include "components/autofill/core/browser/country_names.h" @@ -62,12 +63,17 @@ using testing::Eq; using testing::Property; using testing::Return; +using testing::UnorderedElementsAre; namespace { // Some guids for testing. const char kGuidA[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A"; const char kGuidB[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B"; +const char kGuidC[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44C"; +const char kGuidD[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44D"; +const char kGuidInvalid[] = "EDC609ED-7EEE-4F27-B00C"; +const char kHttpOrigin[] = "http://www.example.com/"; const char kHttpsOrigin[] = "https://www.example.com/"; const int kValidityStateBitfield = 1984; const char kLocaleString[] = "en-US"; @@ -109,15 +115,35 @@ return entity_data->specifics.autofill_profile(); } -MATCHER_P(HasSpecifics, expected, "") { - const AutofillProfileSpecifics& s1 = arg->specifics.autofill_profile(); - const AutofillProfileSpecifics& s2 = expected; +AutofillProfileSpecifics CreateAutofillProfileSpecifics( + const std::string& guid, + const std::string& origin) { + AutofillProfileSpecifics specifics; + specifics.set_guid(guid); + specifics.set_origin(origin); + // Make it consistent with the constructor of AutofillProfile constructor (the + // clock value is overrided by TestAutofillClock in the test fixture). + specifics.set_use_count(1); + specifics.set_use_date(kJune2017.ToTimeT()); + return specifics; +} - AutofillProfile p1 = CreateAutofillProfile(s1); - AutofillProfile p2 = CreateAutofillProfile(s2); - if (!p1.EqualsIncludingUsageStatsForTesting(p2)) { - *result_listener << "entry\n[" << p1 << "]\n" - << "did not match expected\n[" << p2 << "]"; +MATCHER_P(HasSpecifics, expected, "") { + AutofillProfile arg_profile = + CreateAutofillProfile(arg->specifics.autofill_profile()); + AutofillProfile expected_profile = CreateAutofillProfile(expected); + if (!arg_profile.EqualsIncludingUsageStatsForTesting(expected_profile)) { + *result_listener << "entry\n[" << arg_profile << "]\n" + << "did not match expected\n[" << expected_profile << "]"; + return false; + } + return true; +} + +MATCHER_P(WithUsageStats, expected, "") { + if (!arg.EqualsIncludingUsageStatsForTesting(expected)) { + *result_listener << "entry\n[" << arg << "]\n" + << "did not match expected\n[" << expected << "]"; return false; } return true; @@ -133,6 +159,79 @@ } } +// Returns a profile with all fields set. Contains identical data to the data +// returned from ConstructCompleteSpecifics(). +AutofillProfile ConstructCompleteProfile() { + AutofillProfile profile(kGuidA, kHttpsOrigin); + + profile.set_use_count(7); + profile.set_use_date(base::Time::FromTimeT(1423182152)); + + profile.SetRawInfo(NAME_FULL, ASCIIToUTF16("John K. Doe, Jr.")); + profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("K.")); + profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe")); + + profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("user@example.com")); + profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("1.800.555.1234")); + + profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Fake St.\n" + "Apt. 42")); + EXPECT_EQ(ASCIIToUTF16("123 Fake St."), + profile.GetRawInfo(ADDRESS_HOME_LINE1)); + EXPECT_EQ(ASCIIToUTF16("Apt. 42"), profile.GetRawInfo(ADDRESS_HOME_LINE2)); + + profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google, Inc.")); + profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Mountain View")); + profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California")); + profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("94043")); + profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); + profile.SetRawInfo(ADDRESS_HOME_SORTING_CODE, ASCIIToUTF16("CEDEX")); + profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, + ASCIIToUTF16("Santa Clara")); + profile.set_language_code("en"); + profile.SetValidityFromBitfieldValue(kValidityStateBitfield); + return profile; +} + +// Returns AutofillProfileSpecifics with all Autofill profile fields set. +// Contains identical data to the data returned from ConstructCompleteProfile(). +AutofillProfileSpecifics ConstructCompleteSpecifics() { + AutofillProfileSpecifics specifics; + + specifics.set_guid(kGuidA); + specifics.set_origin(kHttpsOrigin); + specifics.set_use_count(7); + specifics.set_use_date(1423182152); + + specifics.add_name_first("John"); + specifics.add_name_middle("K."); + specifics.add_name_last("Doe"); + specifics.add_name_full("John K. Doe, Jr."); + + specifics.add_email_address("user@example.com"); + + specifics.add_phone_home_whole_number("1.800.555.1234"); + + specifics.set_address_home_line1("123 Fake St."); + specifics.set_address_home_line2("Apt. 42"); + specifics.set_address_home_street_address( + "123 Fake St.\n" + "Apt. 42"); + + specifics.set_company_name("Google, Inc."); + specifics.set_address_home_city("Mountain View"); + specifics.set_address_home_state("California"); + specifics.set_address_home_zip("94043"); + specifics.set_address_home_country("US"); + specifics.set_address_home_sorting_code("CEDEX"); + specifics.set_address_home_dependent_locality("Santa Clara"); + specifics.set_address_home_language_code("en"); + specifics.set_validity_state_bitfield(kValidityStateBitfield); + + return specifics; +} + } // namespace class AutofillProfileSyncBridgeTest : public testing::Test { @@ -188,6 +287,12 @@ real_processor_->OnUpdateReceived(state, initial_updates); } + void ApplySyncChanges(const EntityChangeList& changes) { + const base::Optional<syncer::ModelError> error = bridge()->ApplySyncChanges( + bridge()->CreateMetadataChangeList(), changes); + EXPECT_FALSE(error) << error->ToString(); + } + void AddAutofillProfilesToTable( const std::vector<AutofillProfile>& profile_list) { for (const auto& profile : profile_list) { @@ -195,7 +300,7 @@ } } - std::vector<AutofillProfile> GetAllData() { + std::vector<AutofillProfile> GetAllLocalData() { std::vector<AutofillProfile> data; // Perform an async call synchronously for testing. base::RunLoop loop; @@ -308,6 +413,34 @@ bridge()->AutofillProfileChanged(change); } +// Usage stats should be updated by the client. +TEST_F(AutofillProfileSyncBridgeTest, + AutofillProfileChanged_Updated_UsageStatsOverwrittenByClient) { + // Remote data has a profile with usage stats. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.set_address_home_language_code("en"); + remote.set_use_count(9); + remote.set_use_date(25); + + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), + ElementsAre(WithUsageStats(CreateAutofillProfile(remote)))); + + // Update to the usage stats for that profile. + AutofillProfile local(kGuidA, kHttpsOrigin); + local.set_language_code("en"); + local.set_use_count(10U); + local.set_use_date(base::Time::FromTimeT(30)); + AutofillProfileChange change(AutofillProfileChange::UPDATE, kGuidA, &local); + + EXPECT_CALL( + mock_processor(), + Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _)); + + bridge()->AutofillProfileChanged(change); +} + // Server profile updates should be ignored. TEST_F(AutofillProfileSyncBridgeTest, AutofillProfileChanged_Updated_IgnoreServerProfiles) { @@ -340,7 +473,7 @@ local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); AddAutofillProfilesToTable({local1, local2}); - EXPECT_THAT(GetAllData(), ElementsAre(local1, local2)); + EXPECT_THAT(GetAllLocalData(), UnorderedElementsAre(local1, local2)); } TEST_F(AutofillProfileSyncBridgeTest, GetData) { @@ -366,6 +499,333 @@ EXPECT_THAT(data, ElementsAre(local1)); } +TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData) { + AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin); + local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + AutofillProfile local2 = AutofillProfile(kGuidB, std::string()); + local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); + local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); + AddAutofillProfilesToTable({local1, local2}); + + AutofillProfileSpecifics remote1 = + CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin); + remote1.add_name_first("Jane"); + AutofillProfileSpecifics remote2 = + CreateAutofillProfileSpecifics(kGuidD, kSettingsOrigin); + remote2.add_name_first("Harry"); + // This one will have the name and origin updated. + AutofillProfileSpecifics remote3 = + CreateAutofillProfileSpecifics(kGuidB, kSettingsOrigin); + remote3.add_name_first("Tom Doe"); + + EXPECT_CALL( + mock_processor(), + Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local1)), _)); + EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0); + + StartSyncing({remote1, remote2, remote3}); + + EXPECT_THAT(GetAllLocalData(), + UnorderedElementsAre(local1, CreateAutofillProfile(remote1), + CreateAutofillProfile(remote2), + CreateAutofillProfile(remote3))); +} + +// Ensure that all profile fields are able to be synced up from the client to +// the server. +TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_SyncAllFieldsToServer) { + AutofillProfile local = ConstructCompleteProfile(); + AddAutofillProfilesToTable({local}); + + // This complete profile is fully uploaded to sync. + EXPECT_CALL(mock_processor(), + Put(_, HasSpecifics(ConstructCompleteSpecifics()), _)); + StartSyncing({}); + + // No changes locally. + EXPECT_THAT(GetAllLocalData(), ElementsAre(WithUsageStats(local))); +} + +// Ensure that all profile fields are able to be synced down from the server to +// the client (and nothing gets uploaded back). +TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_SyncAllFieldsToClient) { + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({ConstructCompleteSpecifics()}); + + EXPECT_THAT(GetAllLocalData(), + ElementsAre(WithUsageStats(ConstructCompleteProfile()))); +} + +TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_IdenticalProfiles) { + AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin); + local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + AutofillProfile local2 = AutofillProfile(kGuidB, kSettingsOrigin); + local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); + local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); + AddAutofillProfilesToTable({local1, local2}); + + // The synced profiles are identical to the local ones, except that the guids + // are different. + AutofillProfileSpecifics remote1 = + CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin); + remote1.add_name_first("John"); + remote1.set_address_home_street_address("1 1st st"); + AutofillProfileSpecifics remote2 = + CreateAutofillProfileSpecifics(kGuidD, kHttpsOrigin); + remote2.add_name_first("Tom"); + remote2.set_address_home_street_address("2 2nd st"); + + // Both remote profiles win, only the verified origin is taken over for the + // second profile. + AutofillProfileSpecifics merged2(remote2); + merged2.set_origin(kSettingsOrigin); + EXPECT_CALL(mock_processor(), Put(kGuidD, HasSpecifics(merged2), _)); + + StartSyncing({remote1, remote2}); + + EXPECT_THAT(GetAllLocalData(), + UnorderedElementsAre(CreateAutofillProfile(remote1), + CreateAutofillProfile(merged2))); +} + +TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_SimilarProfiles) { + AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin); + local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local1.set_use_count(27); + AutofillProfile local2 = AutofillProfile(kGuidB, kSettingsOrigin); + local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); + local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); + AddAutofillProfilesToTable({local1, local2}); + + // The synced profiles are identical to the local ones, except that the guids + // and use_count values are different. + AutofillProfileSpecifics remote1 = + CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin); + remote1.add_name_first("John"); + remote1.set_address_home_street_address("1 1st st"); + remote1.set_company_name("Frobbers, Inc."); + remote1.set_use_count(13); + AutofillProfileSpecifics remote2 = + CreateAutofillProfileSpecifics(kGuidD, kHttpsOrigin); + remote2.add_name_first("Tom"); + remote2.set_address_home_street_address("2 2nd st"); + remote2.set_company_name("Fizzbang, LLC."); + remote2.set_use_count(4); + + // The first profile should have its origin updated. + // The second profile should remain as-is, because an unverified profile + // should never overwrite a verified one. + AutofillProfileSpecifics merged1(remote1); + merged1.set_origin(kHttpOrigin); + // TODO(jkrcal): This is taken over from the previous test suite without any + // reasoning why this happens. This indeed happens, deep in + // AutofillProfileComparator when merging profiles both without NAME_FULL, we + // obtain a profile with NAME_FULL. Not sure if intended. + merged1.add_name_full("John"); + // Merging two profile takes their max use count. + merged1.set_use_count(27); + + // Expect updating the first (merged) profile and adding the second local one. + EXPECT_CALL(mock_processor(), Put(kGuidC, HasSpecifics(merged1), _)); + EXPECT_CALL( + mock_processor(), + Put(kGuidB, HasSpecifics(CreateAutofillProfileSpecifics(local2)), _)); + EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0); + + StartSyncing({remote1, remote2}); + + EXPECT_THAT(GetAllLocalData(), + UnorderedElementsAre( + WithUsageStats(CreateAutofillProfile(merged1)), local2, + WithUsageStats(CreateAutofillProfile(remote2)))); +} + +// TODO(jkrcal): All the MergeSimilarProfiles_* tests need some diff in Info to +// trigger the merge similar code path (we create the diff using phone number). +// Otherwise, we trigger the merge same code path and none of the tests pass. Is +// it desired? + +// Tests that MergeSimilarProfiles keeps the most recent use date of the two +// profiles being merged. +TEST_F(AutofillProfileSyncBridgeTest, + MergeSyncData_SimilarProfiles_OlderUseDate) { + // Different guids, same origin, difference in the phone number. + AutofillProfile local(kGuidA, kHttpOrigin); + local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567")); + local.set_use_date(base::Time::FromTimeT(30)); + AddAutofillProfilesToTable({local}); + + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); + // |local| has a more recent use date. + remote.set_use_date(25); + + // The use date of |local| should replace the use date of |remote|. + AutofillProfileSpecifics merged(remote); + merged.set_use_date(30); + merged.add_phone_home_whole_number("650234567"); + EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _)); + + StartSyncing({remote}); +} + +// Tests that MergeSimilarProfiles keeps the most recent use date of the two +// profiles being merged. +TEST_F(AutofillProfileSyncBridgeTest, + MergeSyncData_SimilarProfiles_NewerUseDate) { + // Different guids, same origin, difference in the phone number. + AutofillProfile local(kGuidA, kHttpOrigin); + local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567")); + local.set_use_date(base::Time::FromTimeT(30)); + AddAutofillProfilesToTable({local}); + + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); + // |remote| has a more recent use date. + remote.set_use_date(35); + + // The use date of |local| should _not_ replace the use date of |remote|. + AutofillProfileSpecifics merged(remote); + merged.add_phone_home_whole_number("650234567"); + EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _)); + + StartSyncing({remote}); +} + +// Tests that MergeSimilarProfiles saves the max of the use counts of the two +// profiles in |remote|. +TEST_F(AutofillProfileSyncBridgeTest, + MergeSyncData_SimilarProfiles_NonZeroUseCounts) { + // Different guids, same origin, difference in the phone number. + AutofillProfile local(kGuidA, kHttpOrigin); + local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567")); + local.set_use_count(12); + AddAutofillProfilesToTable({local}); + + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); + remote.set_use_count(5); + + // The use count of |local| should replace the use count of |remote|. + AutofillProfileSpecifics merged(remote); + merged.set_use_count(12); + merged.add_phone_home_whole_number("650234567"); + EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _)); + + StartSyncing({remote}); +} + +// Tests that when merging similar profiles for initial sync, we add the +// additional information of |local| into |remote|. +TEST_F(AutofillProfileSyncBridgeTest, + MergeSyncData_SimilarProfiles_LocalOriginPreserved) { + AutofillProfile local(kGuidA, kHttpsOrigin); + local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567")); + AddAutofillProfilesToTable({local}); + + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); + remote.set_address_home_language_code("en"); + + // Expect that the resulting merged profile is written back to sync and that + // it has the phone number and origin from |local|. + AutofillProfileSpecifics merged(remote); + merged.set_origin(kHttpsOrigin); + merged.add_phone_home_whole_number("650234567"); + // TODO(jkrcal): Is this expected that language code gets deleted? Not + // explicitly covered by previous tests but happens. + merged.set_address_home_language_code(""); + EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _)); + + StartSyncing({remote}); +} + +// Sync data without origin should not overwrite existing origin in local +// autofill profile. +TEST_F(AutofillProfileSyncBridgeTest, + MergeSyncData_SimilarProfiles_LocalExistingOriginPreserved) { + AutofillProfile local(kGuidA, kHttpsOrigin); + AddAutofillProfilesToTable({local}); + + // Remote data does not have an origin value. + AutofillProfileSpecifics remote = CreateAutofillProfileSpecifics(kGuidA, ""); + remote.clear_origin(); + remote.add_name_first("John"); + ASSERT_FALSE(remote.has_origin()); + + // Expect no sync events to add origin to the remote data. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + + // Expect the local autofill profile to still have an origin after sync. + AutofillProfile merged(local); + merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + + EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); +} + +// Ensure that no Sync events are generated to fill in missing origins from Sync +// with explicitly present empty ones. This ensures that the migration to add +// origins to profiles does not generate lots of needless Sync updates. +TEST_F(AutofillProfileSyncBridgeTest, + MergeSyncData_SimilarProfiles_LocalMissingOriginPreserved) { + AutofillProfile local = AutofillProfile(kGuidA, std::string()); + local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + AddAutofillProfilesToTable({local}); + + // Create a Sync profile identical to |local|, except with no origin set. + AutofillProfileSpecifics remote = CreateAutofillProfileSpecifics(kGuidA, ""); + remote.clear_origin(); + remote.add_name_first("John"); + ASSERT_FALSE(remote.has_origin()); + + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); +} + +TEST_F(AutofillProfileSyncBridgeTest, ApplySyncChanges) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin); + AddAutofillProfilesToTable({local}); + + StartSyncing({}); + + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); + remote.add_name_first("Jane"); + + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0); + + ApplySyncChanges( + {EntityChange::CreateDelete(kGuidA), + EntityChange::CreateAdd(kGuidB, SpecificsToEntity(remote))}); + + EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); +} + +// Ensure that entries with invalid specifics are ignored. +TEST_F(AutofillProfileSyncBridgeTest, ApplySyncChanges_OmitsInvalidSpecifics) { + StartSyncing({}); + + AutofillProfileSpecifics remote_valid = + CreateAutofillProfileSpecifics(kGuidA, std::string()); + AutofillProfileSpecifics remote_invalid = + CreateAutofillProfileSpecifics(kGuidInvalid, std::string()); + + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + ApplySyncChanges( + {EntityChange::CreateAdd(kGuidA, SpecificsToEntity(remote_valid)), + EntityChange::CreateAdd(kGuidInvalid, + SpecificsToEntity(remote_invalid))}); + + EXPECT_THAT(GetAllLocalData(), + ElementsAre(CreateAutofillProfile(remote_valid))); +} + // Verifies that setting the street address field also sets the (deprecated) // address line 1 and line 2 fields. TEST_F(AutofillProfileSyncBridgeTest, StreetAddress_SplitAutomatically) { @@ -402,4 +862,363 @@ EXPECT_FALSE(remote.has_address_home_street_address()); } +// Ensure that the street address field takes precedence over the (deprecated) +// address line 1 and line 2 fields, even though these are expected to always be +// in sync in practice. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_StreetAddress_TakesPrecedenceOverAddressLines) { + // Create remote entry with conflicting address data in the street address + // field vs. the address line 1 and address line 2 fields. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.set_address_home_line1("123 Example St."); + remote.set_address_home_line2("Apt. 42"); + remote.set_address_home_street_address( + "456 El Camino Real\n" + "Suite #1337"); + + StartSyncing({remote}); + + // Verify that full street address takes precedence over address lines. + AutofillProfile local(kGuidA, kHttpsOrigin); + local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, + ASCIIToUTF16("456 El Camino Real\n" + "Suite #1337")); + local.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("456 El Camino Real")); + local.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Suite #1337")); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); +} + +// Ensure that no Sync events are generated to fill in missing street address +// fields from Sync with explicitly present ones identical to the data stored in +// the line1 and line2 fields. This ensures that the migration to add the +// street address field to profiles does not generate lots of needless Sync +// updates. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_StreetAddress_NoUpdateToEmptyStreetAddressSyncedUp) { + AutofillProfile local(kGuidA, kHttpsOrigin); + local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Example St.\n" + "Apt. 42")); + AddAutofillProfilesToTable({local}); + + // Create a Sync profile identical to |profile|, except without street address + // explicitly set. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.set_address_home_line1("123 Example St."); + remote.set_address_home_line2("Apt. 42"); + + // No update to sync, no change in local data. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); +} + +// Missing language code field should not generate sync events. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_LanguageCode_MissingCodesNoSync) { + AutofillProfile local(kGuidA, kHttpsOrigin); + ASSERT_TRUE(local.language_code().empty()); + AddAutofillProfilesToTable({local}); + + // Remote data does not have a language code value. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + ASSERT_FALSE(remote.has_address_home_language_code()); + + // No update to sync, no change in local data. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); +} + +// Empty language code should be overwritten by sync. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_LanguageCode_ExistingRemoteWinsOverMissingLocal) { + AutofillProfile local(kGuidA, kHttpsOrigin); + ASSERT_TRUE(local.language_code().empty()); + AddAutofillProfilesToTable({local}); + + // Remote data has "en" language code. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.set_address_home_language_code("en"); + + // No update to sync, remote language code overwrites the empty local one. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); +} + +// Local language code should be overwritten by remote one. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_LanguageCode_ExistingRemoteWinsOverExistingLocal) { + AutofillProfile local(kGuidA, kHttpsOrigin); + local.set_language_code("de"); + AddAutofillProfilesToTable({local}); + + // Remote data has "en" language code. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.set_address_home_language_code("en"); + + // No update to sync, remote language code overwrites the local one. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); +} + +// Sync data without language code should not overwrite existing language code +// in local autofill profile. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_LanguageCode_ExistingLocalWinsOverMissingRemote) { + // Local autofill profile has "en" language code. + AutofillProfile local(kGuidA, kHttpsOrigin); + local.set_language_code("en"); + AddAutofillProfilesToTable({local}); + + // Remote data does not have a language code value. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.add_name_first("John"); + ASSERT_FALSE(remote.has_address_home_language_code()); + + // Expect local autofill profile to still have "en" language code after + AutofillProfile merged(local); + merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + + // No update to sync, remote language code overwrites the local one. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); +} + +// Missing validity state bitifield should not generate sync events. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_ValidityState_DefaultValueNoSync) { + AutofillProfile local(kGuidA, kHttpsOrigin); + ASSERT_EQ(0, local.GetValidityBitfieldValue()); + AddAutofillProfilesToTable({local}); + + // Remote data does not have a validity state bitfield value. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + ASSERT_FALSE(remote.has_validity_state_bitfield()); + + // No update to sync, no change in local data. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); +} + +// Default validity state bitfield should be overwritten by sync. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_ValidityState_ExistingRemoteWinsOverMissingLocal) { + AutofillProfile local(kGuidA, kHttpsOrigin); + ASSERT_EQ(0, local.GetValidityBitfieldValue()); + AddAutofillProfilesToTable({local}); + + // Remote data has a non default validity state bitfield value. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.set_validity_state_bitfield(kValidityStateBitfield); + ASSERT_TRUE(remote.has_validity_state_bitfield()); + + // No update to sync, the validity bitfield should be stored to local. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); +} + +// Local validity state bitfield should be overwritten by sync. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_ValidityState_ExistingRemoteWinsOverExistingLocal) { + AutofillProfile local(kGuidA, kHttpsOrigin); + local.SetValidityFromBitfieldValue(kValidityStateBitfield + 1); + AddAutofillProfilesToTable({local}); + + // Remote data has a non default validity state bitfield value. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.set_validity_state_bitfield(kValidityStateBitfield); + ASSERT_TRUE(remote.has_validity_state_bitfield()); + + // No update to sync, the remote validity bitfield should overwrite local. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); +} + +// Sync data without a default validity state bitfield should not overwrite +// an existing validity state bitfield in local autofill profile. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_ValidityState_ExistingLocalWinsOverMissingRemote) { + AutofillProfile local(kGuidA, kHttpsOrigin); + local.SetValidityFromBitfieldValue(kValidityStateBitfield); + AddAutofillProfilesToTable({local}); + + // Remote data has a non default validity state bitfield value. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.add_name_first("John"); + ASSERT_FALSE(remote.has_validity_state_bitfield()); + + // Expect local autofill profile to still have the validity state after. + AutofillProfile merged(local); + merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + + // No update to sync, the local validity bitfield should stay untouched. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); +} + +// Missing full name field should not generate sync events. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_FullName_MissingValueNoSync) { + // Local autofill profile has an empty full name. + AutofillProfile local(kGuidA, kHttpsOrigin); + local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + AddAutofillProfilesToTable({local}); + + // Remote data does not have a full name value. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.add_name_first(std::string("John")); + + // No update to sync, no change in local data. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); +} + +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_FullName_ExistingLocalWinsOverMissingRemote) { + // Local autofill profile has a full name. + AutofillProfile local(kGuidA, kHttpsOrigin); + local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John Jacob Smith, Jr")); + AddAutofillProfilesToTable({local}); + + // Remote data does not have a full name value. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.add_name_first(std::string("John")); + remote.add_name_middle(std::string("Jacob")); + remote.add_name_last(std::string("Smith")); + + // Expect local autofill profile to still have the same full name after. + AutofillProfile merged(local); + merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + merged.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Jacob")); + merged.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith")); + + // No update to sync, no change in local data. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); +} + +// Missing use_count/use_date fields should not generate sync events. +TEST_F(AutofillProfileSyncBridgeTest, + RemoteWithSameGuid_UsageStats_MissingValueNoSync) { + // Local autofill profile has 0 for use_count/use_date. + AutofillProfile local(kGuidA, kHttpsOrigin); + local.set_language_code("en"); + local.set_use_count(0); + local.set_use_date(base::Time()); + AddAutofillProfilesToTable({local}); + + // Remote data does not have use_count/use_date. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.clear_use_count(); + remote.clear_use_date(); + remote.set_address_home_language_code("en"); + + // No update to sync, no change in local data. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(WithUsageStats(local))); +} + +struct UpdatesUsageStatsTestCase { + size_t local_use_count; + base::Time local_use_date; + size_t remote_use_count; + int remote_use_date; + size_t merged_use_count; + base::Time merged_use_date; +}; + +class AutofillProfileSyncBridgeUpdatesUsageStatsTest + : public AutofillProfileSyncBridgeTest, + public testing::WithParamInterface<UpdatesUsageStatsTestCase> { + public: + AutofillProfileSyncBridgeUpdatesUsageStatsTest() {} + ~AutofillProfileSyncBridgeUpdatesUsageStatsTest() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncBridgeUpdatesUsageStatsTest); +}; + +TEST_P(AutofillProfileSyncBridgeUpdatesUsageStatsTest, UpdatesUsageStats) { + auto test_case = GetParam(); + + // Local data has usage stats. + AutofillProfile local(kGuidA, kHttpsOrigin); + local.set_language_code("en"); + local.set_use_count(test_case.local_use_count); + local.set_use_date(test_case.local_use_date); + ASSERT_EQ(test_case.local_use_count, local.use_count()); + ASSERT_EQ(test_case.local_use_date, local.use_date()); + AddAutofillProfilesToTable({local}); + + // Remote data has usage stats. + AutofillProfileSpecifics remote = + CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); + remote.set_address_home_language_code("en"); + remote.set_use_count(test_case.remote_use_count); + remote.set_use_date(test_case.remote_use_date); + ASSERT_TRUE(remote.has_use_count()); + ASSERT_TRUE(remote.has_use_date()); + + // Expect the local autofill profile to have usage stats after sync. + AutofillProfile merged(local); + merged.set_use_count(test_case.merged_use_count); + merged.set_use_date(test_case.merged_use_date); + + // Expect no changes to remote data. + EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); + + StartSyncing({remote}); + EXPECT_THAT(GetAllLocalData(), ElementsAre(WithUsageStats(merged))); +} + +INSTANTIATE_TEST_CASE_P( + AutofillProfileSyncBridgeTest, + AutofillProfileSyncBridgeUpdatesUsageStatsTest, + testing::Values( + // Local profile with default stats. + UpdatesUsageStatsTestCase{ + /*local_use_count=*/0U, + /*local_use_date=*/base::Time(), + /*remote_use_count=*/9U, + /*remote_use_date=*/4321, + /*merged_use_count=*/9U, + /*merged_use_date=*/base::Time::FromTimeT(4321)}, + // Local profile has older stats than the server. + UpdatesUsageStatsTestCase{ + /*local_use_count=*/3U, + /*local_use_date=*/base::Time::FromTimeT(1234), + /*remote_use_count=*/9U, /*remote_use_date=*/4321, + /*merged_use_count=*/9U, + /*merged_use_date=*/base::Time::FromTimeT(4321)}, + // Local profile has newer stats than the server + UpdatesUsageStatsTestCase{ + /*local_use_count=*/10U, + /*local_use_date=*/base::Time::FromTimeT(9999), + /*remote_use_count=*/9U, /*remote_use_date=*/4321, + /*merged_use_count=*/9U, + /*merged_use_date=*/base::Time::FromTimeT(4321)})); + } // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc new file mode 100644 index 0000000..c5d3a8ee --- /dev/null +++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
@@ -0,0 +1,296 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h" + +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_profile.h" +#include "components/autofill/core/browser/autofill_profile_comparator.h" +#include "components/autofill/core/browser/autofill_profile_sync_util.h" +#include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h" +#include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/sync/model/model_error.h" + +namespace autofill { + +using base::Optional; +using syncer::ModelError; + +AutofillProfileSyncDifferenceTracker::AutofillProfileSyncDifferenceTracker( + AutofillTable* table) + : table_(table) {} + +AutofillProfileSyncDifferenceTracker::~AutofillProfileSyncDifferenceTracker() {} + +Optional<ModelError> +AutofillProfileSyncDifferenceTracker::IncorporateRemoteProfile( + std::unique_ptr<AutofillProfile> remote) { + const std::string remote_storage_key = + GetStorageKeyFromAutofillProfile(*remote); + + if (!GetLocalOnlyEntries()) { + return ModelError(FROM_HERE, "Failed reading from WebDatabase."); + } + + Optional<AutofillProfile> local_with_same_storage_key = + ReadEntry(remote_storage_key); + + if (local_with_same_storage_key) { + // The remote profile already exists locally with the same key. Update + // the local entry with remote data. + std::unique_ptr<AutofillProfile> updated = + std::make_unique<AutofillProfile>(local_with_same_storage_key.value()); + // We ignore remote updates to a verified profile because we want to keep + // the exact version that the user edited by hand. + if (local_with_same_storage_key->IsVerified() && !remote->IsVerified()) { + return base::nullopt; + } + updated->OverwriteDataFrom(*remote); + // TODO(jkrcal): if |updated| deviates from |remote|, we should sync it back + // up. The only way |updated| can differ is having some extra fields + // compared to |remote|. Thus, this cannot lead to an infinite loop of + // commits from two clients as each commit decreases the set of empty + // fields. This invariant depends on the implementation of + // OverwriteDataFrom() and thus should be enforced by a DCHECK. + + if (!updated->EqualsForSyncPurposes(*local_with_same_storage_key)) { + // We need to write back locally new changes in this entry. + update_to_local_.push_back(std::move(updated)); + } + GetLocalOnlyEntries()->erase(remote_storage_key); + return base::nullopt; + } + + // Check if profile appears under a different storage key to be de-duplicated. + for (const auto& pair : *GetLocalOnlyEntries()) { + const std::string& local_storage_key = pair.first; + const AutofillProfile& local = *pair.second; + + // Look for exact duplicates, compare only profile contents (and + // ignore origin and language code in comparison). + if (local.Compare(*remote) == 0) { + // We found a duplicate, we keep the new (remote) one and delete the + // local one. + DVLOG(2) + << "[AUTOFILL SYNC] The profile " + << base::UTF16ToUTF8(local.GetRawInfo(NAME_FIRST)) + << base::UTF16ToUTF8(local.GetRawInfo(NAME_LAST)) + << " already exists with a different storage key; keep the remote key" + << remote_storage_key << " and delete the local key " + << local_storage_key; + + // Ensure that a verified profile can never revert back to an unverified + // one. In such a case, take over the local origin for the new (remote) + // entry. + if (local.IsVerified() && !remote->IsVerified()) { + remote->set_origin(local.origin()); + // Save a copy of the remote profile also to sync. + save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote)); + } + // Delete the local profile that gets replaced by |remote|. + DeleteFromLocal(local_storage_key); + break; + } + } + + add_to_local_.push_back(std::move(remote)); + return base::nullopt; +} + +Optional<ModelError> +AutofillProfileSyncDifferenceTracker::IncorporateRemoteDelete( + const std::string& storage_key) { + DCHECK(!storage_key.empty()); + DeleteFromLocal(storage_key); + return base::nullopt; +} + +Optional<ModelError> AutofillProfileSyncDifferenceTracker::FlushToLocal( + base::OnceClosure autofill_changes_callback) { + for (const std::string& storage_key : delete_from_local_) { + if (!table_->RemoveAutofillProfile(storage_key)) { + return ModelError(FROM_HERE, "Failed deleting from WebDatabase"); + } + } + for (const std::unique_ptr<AutofillProfile>& entry : add_to_local_) { + if (!table_->AddAutofillProfile(*entry)) { + return ModelError(FROM_HERE, "Failed updating WebDatabase"); + } + } + for (const std::unique_ptr<AutofillProfile>& entry : update_to_local_) { + if (!table_->UpdateAutofillProfile(*entry)) { + return ModelError(FROM_HERE, "Failed updating WebDatabase"); + } + } + if (!delete_from_local_.empty() || !add_to_local_.empty() || + !update_to_local_.empty()) { + std::move(autofill_changes_callback).Run(); + } + return base::nullopt; +} + +Optional<ModelError> AutofillProfileSyncDifferenceTracker::FlushToSync( + std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync) { + for (std::unique_ptr<AutofillProfile>& entry : save_to_sync_) { + profiles_to_upload_to_sync->push_back(std::move(entry)); + } + return base::nullopt; +} + +Optional<AutofillProfile> AutofillProfileSyncDifferenceTracker::ReadEntry( + const std::string& storage_key) { + DCHECK(GetLocalOnlyEntries()); + auto iter = GetLocalOnlyEntries()->find(storage_key); + if (iter != GetLocalOnlyEntries()->end()) { + return *iter->second; + } + return base::nullopt; +} + +void AutofillProfileSyncDifferenceTracker::DeleteFromLocal( + const std::string& storage_key) { + DCHECK(GetLocalOnlyEntries()); + delete_from_local_.insert(storage_key); + GetLocalOnlyEntries()->erase(storage_key); +} + +std::map<std::string, std::unique_ptr<AutofillProfile>>* +AutofillProfileSyncDifferenceTracker::GetLocalOnlyEntries() { + if (!InitializeLocalOnlyEntriesIfNeeded()) { + return nullptr; + } + return &local_only_entries_; +} + +bool AutofillProfileSyncDifferenceTracker:: + InitializeLocalOnlyEntriesIfNeeded() { + if (local_only_entries_initialized_) { + return true; + } + + std::vector<std::unique_ptr<AutofillProfile>> entries; + if (!table_->GetAutofillProfiles(&entries)) { + return false; + } + + for (std::unique_ptr<AutofillProfile>& entry : entries) { + std::string storage_key = GetStorageKeyFromAutofillProfile(*entry); + local_only_entries_[storage_key] = std::move(entry); + } + + local_only_entries_initialized_ = true; + return true; +} + +AutofillProfileInitialSyncDifferenceTracker:: + AutofillProfileInitialSyncDifferenceTracker(AutofillTable* table) + : AutofillProfileSyncDifferenceTracker(table) {} + +AutofillProfileInitialSyncDifferenceTracker:: + ~AutofillProfileInitialSyncDifferenceTracker() {} + +Optional<ModelError> +AutofillProfileInitialSyncDifferenceTracker::IncorporateRemoteDelete( + const std::string& storage_key) { + // Remote delete is not allowed in initial sync. + NOTREACHED(); + return base::nullopt; +} + +Optional<ModelError> AutofillProfileInitialSyncDifferenceTracker::FlushToSync( + std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync) { + // First, flush standard updates to sync. + AutofillProfileSyncDifferenceTracker::FlushToSync(profiles_to_upload_to_sync); + + // For initial sync, we additionally need to upload all local only entries. + if (!GetLocalOnlyEntries()) { + return ModelError(FROM_HERE, "Failed reading from WebDatabase."); + } + for (auto& pair : *GetLocalOnlyEntries()) { + std::string storage_key = pair.first; + // No deletions coming from remote are allowed for initial sync. + DCHECK(delete_from_local_.count(storage_key) == 0); + profiles_to_upload_to_sync->push_back(std::move(pair.second)); + } + return base::nullopt; +} + +Optional<ModelError> +AutofillProfileInitialSyncDifferenceTracker::MergeSimilarEntriesForInitialSync( + const std::string& app_locale) { + if (!GetLocalOnlyEntries()) { + return ModelError(FROM_HERE, "Failed reading from WebDatabase."); + } + + // This merge cannot happen on the fly during IncorporateRemoteSpecifics(). + // Namely, we do not want to merge a local entry with a _similar_ remote + // entry if anoter perfectly fitting remote entry comes later during the + // initial sync (a remote entry fits perfectly to a given local entry if + // it has fully equal data or even the same storage key). After all the calls + // to IncorporateRemoteSpecifics() are over, GetLocalOnlyEntries() only + // contains unmatched entries that can be safely merged with similar remote + // entries. + + AutofillProfileComparator comparator(app_locale); + // Loop over all new remote entries to find merge candidates. Using + // non-const reference because we want to update |remote| in place if + // needed. + for (std::unique_ptr<AutofillProfile>& remote : add_to_local_) { + Optional<AutofillProfile> local = + FindMergeableLocalEntry(*remote, comparator); + if (!local) { + continue; + } + + DVLOG(2) + << "[AUTOFILL SYNC] A similar profile to " + << base::UTF16ToUTF8(remote->GetRawInfo(NAME_FIRST)) + << base::UTF16ToUTF8(remote->GetRawInfo(NAME_LAST)) + << " already exists with a different storage key; keep the remote key" + << GetStorageKeyFromAutofillProfile(*remote) + << ", merge local data into it and delete the local key" + << GetStorageKeyFromAutofillProfile(*local); + + // For similar profile pairs, the local profile is always removed and its + // content merged (if applicable) in the profile that came from sync. + AutofillProfile remote_before_merge = *remote; + remote->MergeDataFrom(*local, app_locale); + if (!remote->EqualsForSyncPurposes(remote_before_merge)) { + // We need to sync new changes in the entry back to the server. + save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote)); + // |remote| is updated in place within |add_to_local_| so the newest + // merged version is stored to local. + } + + DeleteFromLocal(GetStorageKeyFromAutofillProfile(*local)); + } + + return base::nullopt; +} + +Optional<AutofillProfile> +AutofillProfileInitialSyncDifferenceTracker::FindMergeableLocalEntry( + const AutofillProfile& remote, + const AutofillProfileComparator& comparator) { + DCHECK(GetLocalOnlyEntries()); + + // Both the remote and the local entry need to be non-verified to be + // mergeable. + if (remote.IsVerified()) { + return base::nullopt; + } + + // Check if there is a mergeable local profile. + for (const auto& pair : *GetLocalOnlyEntries()) { + const AutofillProfile& local_candidate = *pair.second; + if (!local_candidate.IsVerified() && + comparator.AreMergeable(local_candidate, remote)) { + return local_candidate; + } + } + return base::nullopt; +} + +} // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h new file mode 100644 index 0000000..4c7fbf7a --- /dev/null +++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
@@ -0,0 +1,139 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNC_DIFFERENCE_TRACKER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNC_DIFFERENCE_TRACKER_H_ + +#include <map> +#include <memory> +#include <set> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/optional.h" + +namespace syncer { +class ModelError; +} // namespace syncer + +namespace autofill { + +class AutofillProfile; +class AutofillProfileComparator; +class AutofillTable; + +// This is used to respond to ApplySyncChanges() and MergeSyncData(). Attempts +// to lazily load local data, and then react to sync data by maintaining +// internal state until flush calls are made, at which point the applicable +// modification should be sent towards local and sync directions. +class AutofillProfileSyncDifferenceTracker { + public: + explicit AutofillProfileSyncDifferenceTracker(AutofillTable* table); + virtual ~AutofillProfileSyncDifferenceTracker(); + + // Adds a new |remote| entry to the diff tracker, originating from the sync + // server. The provided |remote| entry must be valid. + base::Optional<syncer::ModelError> IncorporateRemoteProfile( + std::unique_ptr<AutofillProfile> remote); + + // Informs the diff tracker that the entry with |storage_key| has been deleted + // from the sync server. |storage_key| must be non-empty. + virtual base::Optional<syncer::ModelError> IncorporateRemoteDelete( + const std::string& storage_key); + + // Writes all local changes to the provided autofill |table_|. After flushing, + // not further remote changes should get incorporated. + base::Optional<syncer::ModelError> FlushToLocal( + base::OnceClosure autofill_changes_callback); + + // Writes into |profiles_to_upload_to_sync| all autofill profiles to be sent + // to the sync server. After flushing, not further remote changes should get + // incorporated. + virtual base::Optional<syncer::ModelError> FlushToSync( + std::vector<std::unique_ptr<AutofillProfile>>* + profiles_to_upload_to_sync); + + protected: + // If the entry is found, |entry| will be return, otherwise base::nullopt is + // returned. + base::Optional<AutofillProfile> ReadEntry(const std::string& storage_key); + + // Tries to find a local entry that is mergeable with |remote| (according to + // |comparator|). If such an entry is found, it is returned. Otherwise, + // base::nullopt is returned. + base::Optional<AutofillProfile> FindMergeableLocalEntry( + const AutofillProfile& remote, + const AutofillProfileComparator& comparator); + + // Informs the tracker that a local entry with |storage_key| should get + // deleted. + void DeleteFromLocal(const std::string& storage_key); + + // Accessor for data that is only stored local. Initializes the data if + // needed. Returns nullptr if initialization failed. + std::map<std::string, std::unique_ptr<AutofillProfile>>* + GetLocalOnlyEntries(); + + // Helper function called by GetLocalOnlyEntries(). + bool InitializeLocalOnlyEntriesIfNeeded(); + + // The table for reading local data. + AutofillTable* const table_; + + // This class loads local data from |table_| lazily. This field tracks if that + // has happened or not yet. + bool local_only_entries_initialized_ = false; + + // We use unique_ptrs for storing AutofillProfile to avoid unnecessary copies. + + // Local data, mapped by storage key. Use unique_to_local() to access it. + std::map<std::string, std::unique_ptr<AutofillProfile>> local_only_entries_; + + // Contain changes (originating from sync) that need to be saved to the local + // store. + std::set<std::string> delete_from_local_; + std::vector<std::unique_ptr<AutofillProfile>> add_to_local_; + std::vector<std::unique_ptr<AutofillProfile>> update_to_local_; + + // Contains merged data for entries that existed on both sync and local sides + // and need to be saved back to sync. + std::vector<std::unique_ptr<AutofillProfile>> save_to_sync_; + + private: + DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncDifferenceTracker); +}; + +class AutofillProfileInitialSyncDifferenceTracker + : public AutofillProfileSyncDifferenceTracker { + public: + explicit AutofillProfileInitialSyncDifferenceTracker(AutofillTable* table); + ~AutofillProfileInitialSyncDifferenceTracker() override; + + base::Optional<syncer::ModelError> IncorporateRemoteDelete( + const std::string& storage_key) override; + + base::Optional<syncer::ModelError> FlushToSync( + std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync) + override; + + // Performs an additional pass through remote entries incorporated from sync + // to find any similarities with local entries. Should be run after all + // entries get incorporated but before flushing results to local/sync. + base::Optional<syncer::ModelError> MergeSimilarEntriesForInitialSync( + const std::string& app_locale); + + private: + // Returns a local entry that is mergeable with |remote| if it exists. + // Otherwise, returns base::nullopt. + base::Optional<AutofillProfile> FindMergeableLocalEntry( + const AutofillProfile& remote, + const AutofillProfileComparator& comparator); + + DISALLOW_COPY_AND_ASSIGN(AutofillProfileInitialSyncDifferenceTracker); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNC_DIFFERENCE_TRACKER_H_
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc new file mode 100644 index 0000000..779ad92 --- /dev/null +++ b/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
@@ -0,0 +1,438 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h" + +#include "base/bind_helpers.h" +#include "base/files/scoped_temp_dir.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/mock_callback.h" +#include "base/test/scoped_task_environment.h" +#include "base/time/time.h" +#include "components/autofill/core/browser/autofill_profile.h" +#include "components/autofill/core/browser/autofill_profile_sync_util.h" +#include "components/autofill/core/browser/country_names.h" +#include "components/autofill/core/browser/test_autofill_clock.h" +#include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/autofill/core/common/autofill_constants.h" +#include "components/sync/model/model_error.h" +#include "components/webdata/common/web_database.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { +namespace { + +using base::ASCIIToUTF16; +using base::MockCallback; +using testing::ElementsAre; +using testing::IsEmpty; + +// Some guids for testing. +const char kGuidA[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A"; +const char kGuidB[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B"; +const char kHttpOrigin[] = "http://www.example.com/"; +const char kHttpsOrigin[] = "https://www.example.com/"; +const char kLocaleString[] = "en-US"; +const base::Time kJune2017 = base::Time::FromDoubleT(1497552271); + +} // namespace + +class AutofillProfileSyncDifferenceTrackerTestBase : public testing::Test { + public: + AutofillProfileSyncDifferenceTrackerTestBase() {} + ~AutofillProfileSyncDifferenceTrackerTestBase() override {} + + void SetUp() override { + // Fix a time for implicitly constructed use_dates in AutofillProfile. + test_clock_.SetNow(kJune2017); + + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + db_.AddTable(&table_); + db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase")); + } + + void AddAutofillProfilesToTable( + const std::vector<AutofillProfile>& profile_list) { + for (const auto& profile : profile_list) { + table_.AddAutofillProfile(profile); + } + } + + void IncorporateRemoteProfile(const AutofillProfile& profile) { + EXPECT_EQ(base::nullopt, tracker()->IncorporateRemoteProfile( + std::make_unique<AutofillProfile>(profile))); + } + + std::vector<AutofillProfile> FlushAndReturnProfilesToUploadToSync() { + EXPECT_EQ(base::nullopt, + tracker()->FlushToLocal( + /*autofill_changes_callback=*/base::DoNothing())); + + std::vector<std::unique_ptr<AutofillProfile>> vector_of_unique_ptrs; + EXPECT_EQ(base::nullopt, + tracker()->FlushToSync( + /*profiles_to_upload_to_sync=*/&vector_of_unique_ptrs)); + + // Copy all the elements by value so that we have a vector that is easier to + // work with in the test. + std::vector<AutofillProfile> vector_of_values; + for (const std::unique_ptr<AutofillProfile>& entry : + vector_of_unique_ptrs) { + vector_of_values.push_back(*entry); + } + return vector_of_values; + } + + std::vector<AutofillProfile> GetAllLocalData() { + std::vector<std::unique_ptr<AutofillProfile>> vector_of_unique_ptrs; + // Meant as an assertion but I cannot use ASSERT_TRUE in non-void function. + EXPECT_TRUE(table()->GetAutofillProfiles(&vector_of_unique_ptrs)); + + // Copy all the elements by value so that we have a vector that is easier to + // work with in the test. + std::vector<AutofillProfile> local_data; + for (const std::unique_ptr<AutofillProfile>& entry : + vector_of_unique_ptrs) { + local_data.push_back(*entry); + } + return local_data; + } + + virtual AutofillProfileSyncDifferenceTracker* tracker() = 0; + + AutofillTable* table() { return &table_; } + + private: + autofill::TestAutofillClock test_clock_; + base::ScopedTempDir temp_dir_; + base::test::ScopedTaskEnvironment scoped_task_environment_; + AutofillTable table_; + WebDatabase db_; + + DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncDifferenceTrackerTestBase); +}; + +class AutofillProfileSyncDifferenceTrackerTest + : public AutofillProfileSyncDifferenceTrackerTestBase { + public: + AutofillProfileSyncDifferenceTrackerTest() : tracker_(table()) {} + ~AutofillProfileSyncDifferenceTrackerTest() override {} + + AutofillProfileSyncDifferenceTracker* tracker() override { return &tracker_; } + + private: + AutofillProfileSyncDifferenceTracker tracker_; + + DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncDifferenceTrackerTest); +}; + +TEST_F(AutofillProfileSyncDifferenceTrackerTest, + IncorporateRemoteProfileShouldOverwriteProfileWithSameKey) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin); + local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + AddAutofillProfilesToTable({local}); + + // The remote profile is completely different but it has the same key. + AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin); + remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); + + IncorporateRemoteProfile(remote); + + // Nothing gets uploaded to sync and the remote profile wins. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty()); + EXPECT_THAT(GetAllLocalData(), ElementsAre(remote)); +} + +TEST_F(AutofillProfileSyncDifferenceTrackerTest, + IncorporateRemoteProfileShouldOverwriteUnverifiedProfileByVerified) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin); + local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + AddAutofillProfilesToTable({local}); + + // The remote profile has the same key but it is not verified. + AutofillProfile remote = AutofillProfile(kGuidA, kSettingsOrigin); + remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); + + IncorporateRemoteProfile(remote); + + // Nothing gets uploaded to sync and the local profile wins. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty()); + EXPECT_THAT(GetAllLocalData(), ElementsAre(remote)); +} + +TEST_F(AutofillProfileSyncDifferenceTrackerTest, + IncorporateRemoteProfileShouldNotOverwriteVerifiedProfileByUnverified) { + AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin); + local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + AddAutofillProfilesToTable({local}); + + // The remote profile has the same key but it is not verified. + AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin); + remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); + + IncorporateRemoteProfile(remote); + + // Nothing gets uploaded to sync and the local profile wins. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty()); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); +} + +TEST_F(AutofillProfileSyncDifferenceTrackerTest, + IncorporateRemoteProfileShouldNotOverwriteFullNameByEmptyString) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin); + local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John")); + AddAutofillProfilesToTable({local}); + + // The remote profile has the same key. + AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin); + remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2st st")); + + AutofillProfile merged(remote); + merged.SetRawInfo(NAME_FULL, ASCIIToUTF16("John")); + + IncorporateRemoteProfile(remote); + + // Nothing gets uploaded to sync and the remote profile wins except for the + // full name. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty()); + EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); +} + +TEST_F(AutofillProfileSyncDifferenceTrackerTest, + IncorporateRemoteProfileShouldMergeIdenticalProfilesWithDifferentKeys) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin); + local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + AddAutofillProfilesToTable({local}); + + // The remote profile is identical to the local one, except that the guids and + // origins are different. + AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin); + remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + + IncorporateRemoteProfile(remote); + + // Nothing gets uploaded to sync and the remote profile wins. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty()); + EXPECT_THAT(GetAllLocalData(), ElementsAre(remote)); +} + +TEST_F( + AutofillProfileSyncDifferenceTrackerTest, + IncorporateRemoteProfileShouldMergeIdenticalProfilesWithDifferentKeysButKeepVerifiedOrigin) { + AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin); + local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + AddAutofillProfilesToTable({local}); + + // The remote profile has the same key. + AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin); + remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + + AutofillProfile merged(remote); + merged.set_origin(kSettingsOrigin); + + IncorporateRemoteProfile(remote); + + // Nothing gets uploaded to sync and the remote profile wins except for the + // full name. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(merged)); + EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); +} + +TEST_F(AutofillProfileSyncDifferenceTrackerTest, + FlushToLocalShouldNotCallbackWhenNotNeeded) { + MockCallback<base::OnceClosure> autofill_changes_callback; + + EXPECT_CALL(autofill_changes_callback, Run()).Times(0); + EXPECT_EQ(base::nullopt, + tracker()->FlushToLocal(autofill_changes_callback.Get())); +} + +TEST_F(AutofillProfileSyncDifferenceTrackerTest, + FlushToLocalShouldCallbackWhenProfileDeleted) { + AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin); + AddAutofillProfilesToTable({local}); + + tracker()->IncorporateRemoteDelete(kGuidA); + + MockCallback<base::OnceClosure> autofill_changes_callback; + EXPECT_CALL(autofill_changes_callback, Run()).Times(1); + EXPECT_EQ(base::nullopt, + tracker()->FlushToLocal(autofill_changes_callback.Get())); + + // On top of that, the profile should also get deleted. + EXPECT_THAT(GetAllLocalData(), IsEmpty()); +} + +TEST_F(AutofillProfileSyncDifferenceTrackerTest, + FlushToLocalShouldCallbackWhenProfileAdded) { + AutofillProfile remote = AutofillProfile(kGuidA, kSettingsOrigin); + IncorporateRemoteProfile(remote); + + MockCallback<base::OnceClosure> autofill_changes_callback; + EXPECT_CALL(autofill_changes_callback, Run()).Times(1); + EXPECT_EQ(base::nullopt, + tracker()->FlushToLocal(autofill_changes_callback.Get())); + + // On top of that, the profile should also get added. + EXPECT_THAT(GetAllLocalData(), ElementsAre(remote)); +} + +TEST_F(AutofillProfileSyncDifferenceTrackerTest, + FlushToLocalShouldCallbackWhenProfileUpdated) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin); + AddAutofillProfilesToTable({local}); + + AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin); + remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + IncorporateRemoteProfile(remote); + + MockCallback<base::OnceClosure> autofill_changes_callback; + EXPECT_CALL(autofill_changes_callback, Run()).Times(1); + EXPECT_EQ(base::nullopt, + tracker()->FlushToLocal(autofill_changes_callback.Get())); + + // On top of that, the profile with key kGuidA should also get updated. + EXPECT_THAT(GetAllLocalData(), ElementsAre(remote)); +} + +class AutofillProfileInitialSyncDifferenceTrackerTest + : public AutofillProfileSyncDifferenceTrackerTestBase { + public: + AutofillProfileInitialSyncDifferenceTrackerTest() + : initial_tracker_(table()) {} + ~AutofillProfileInitialSyncDifferenceTrackerTest() override {} + + void MergeSimilarEntriesForInitialSync() { + initial_tracker_.MergeSimilarEntriesForInitialSync(kLocaleString); + } + + AutofillProfileSyncDifferenceTracker* tracker() override { + return &initial_tracker_; + } + + private: + AutofillProfileInitialSyncDifferenceTracker initial_tracker_; + + DISALLOW_COPY_AND_ASSIGN(AutofillProfileInitialSyncDifferenceTrackerTest); +}; + +TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, + MergeSimilarEntriesForInitialSyncShouldSyncUpChanges) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin); + local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local.set_use_count(27); + AddAutofillProfilesToTable({local}); + + // The remote profile matches the local one (except for origin and use count). + AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin); + remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); + remote.set_use_count(13); + + // The remote profile wins (as regards the storage key). + AutofillProfile merged(remote); + // The local origin wins when merging. + merged.set_origin(kHttpOrigin); + // Merging two profile takes their max use count. + merged.set_use_count(27); + + IncorporateRemoteProfile(remote); + MergeSimilarEntriesForInitialSync(); + + // The merged profile needs to get uploaded back to sync and stored locally. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(merged)); + EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); +} + +TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, + MergeSimilarEntriesForInitialSyncShouldNotSyncUpWhenNotNeeded) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin); + local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local.set_use_count(13); + AddAutofillProfilesToTable({local}); + + // The remote profile matches the local one and has some additional data. + AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin); + remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); + // Merging two profile takes their max use count, so use count of 27 is taken. + remote.set_use_count(27); + + IncorporateRemoteProfile(remote); + MergeSimilarEntriesForInitialSync(); + + // Nothing gets uploaded to sync and the remote profile wins. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty()); + EXPECT_THAT(GetAllLocalData(), ElementsAre(remote)); +} + +TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, + MergeSimilarEntriesForInitialSyncNotMatchNonsimilarEntries) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin); + local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); + AddAutofillProfilesToTable({local}); + + // The remote profile has a different street address. + AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin); + remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2st st")); + remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); + + IncorporateRemoteProfile(remote); + MergeSimilarEntriesForInitialSync(); + + // The local profile gets uploaded (due to initial sync) and the remote + // profile gets stored locally. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local)); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote)); +} + +TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, + MergeSimilarEntriesForInitialSyncDoesNotMatchLocalVerifiedEntry) { + // The local entry is verified, should not get merged. + AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin); + local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + AddAutofillProfilesToTable({local}); + + // The remote profile is similar to the local one. + AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin); + remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); + + IncorporateRemoteProfile(remote); + MergeSimilarEntriesForInitialSync(); + + // The local profile gets uploaded (due to initial sync) and the remote + // profile gets stored locally. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local)); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote)); +} + +TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, + MergeSimilarEntriesForInitialSyncDoesNotMatchRemoteVerifiedEntry) { + AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin); + local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + AddAutofillProfilesToTable({local}); + + // The remote profile is similar to the local one but is verified and thus it + // should not get merged. + AutofillProfile remote = AutofillProfile(kGuidB, kSettingsOrigin); + remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); + + IncorporateRemoteProfile(remote); + MergeSimilarEntriesForInitialSync(); + + // The local profile gets uploaded (due to initial sync) and the remote + // profile gets stored locally. + EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local)); + EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote)); +} + +} // namespace autofill
diff --git a/components/autofill/core/common/password_form.cc b/components/autofill/core/common/password_form.cc index ed798d9..9fc16b1 100644 --- a/components/autofill/core/common/password_form.cc +++ b/components/autofill/core/common/password_form.cc
@@ -76,6 +76,8 @@ submission_event_string_stream << form.submission_event; target->SetString("submission_event", submission_event_string_stream.str()); target->SetBoolean("only_for_fallback_saving", form.only_for_fallback_saving); + target->SetBoolean("is_gaia_with_skip_save_password_form", + form.is_gaia_with_skip_save_password_form); } } // namespace @@ -98,7 +100,8 @@ is_public_suffix_match(false), is_affiliation_based_match(false), submission_event(SubmissionIndicatorEvent::NONE), - only_for_fallback_saving(false) {} + only_for_fallback_saving(false), + is_gaia_with_skip_save_password_form(false) {} PasswordForm::PasswordForm(const PasswordForm& other) = default; @@ -156,7 +159,9 @@ app_display_name == form.app_display_name && app_icon_url == form.app_icon_url && submission_event == form.submission_event && - only_for_fallback_saving == form.only_for_fallback_saving; + only_for_fallback_saving == form.only_for_fallback_saving && + is_gaia_with_skip_save_password_form == + form.is_gaia_with_skip_save_password_form; } bool PasswordForm::operator!=(const PasswordForm& form) const {
diff --git a/components/autofill/core/common/password_form.h b/components/autofill/core/common/password_form.h index 6a3d00e..b3d9b14 100644 --- a/components/autofill/core/common/password_form.h +++ b/components/autofill/core/common/password_form.h
@@ -342,6 +342,9 @@ // fields were found). But this form can be saved only with the fallback. bool only_for_fallback_saving; + // True iff this is Gaia form which should be skipped on saving. + bool is_gaia_with_skip_save_password_form; + // Return true if we consider this form to be a change password form. // We use only client heuristics, so it could include signup forms. bool IsPossibleChangePasswordForm() const;
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp index 3a529438b..cea0375 100644 --- a/components/autofill_strings.grdp +++ b/components/autofill_strings.grdp
@@ -153,10 +153,6 @@ Scan new card </message> - <message name="IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE" desc="Text shown as the title of the suggestion drop down when a password field is clicked."> - Use password for: - </message> - <if expr="not use_titlecase"> <message name="IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK" desc="The text shown as an option in the suggestion drop down when a password field is clicked"> Show all saved passwords
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java index 91c48456..f941c9a 100644 --- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java +++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
@@ -32,8 +32,9 @@ static final int BACKGROUND_TASK_WEBAPK_UPDATE = 12; static final int BACKGROUND_TASK_DOWNLOAD_RESUMPTION = 13; static final int BACKGROUND_TASK_FEED_REFRESH = 14; + static final int BACKGROUND_TASK_COMPONENT_UPDATE = 15; // Keep this one at the end and increment appropriately when adding new tasks. - static final int BACKGROUND_TASK_COUNT = 15; + static final int BACKGROUND_TASK_COUNT = 16; static final String KEY_CACHED_UMA = "bts_cached_uma"; @@ -250,6 +251,8 @@ return BACKGROUND_TASK_DOWNLOAD_RESUMPTION; case TaskIds.FEED_REFRESH_JOB_ID: return BACKGROUND_TASK_FEED_REFRESH; + case TaskIds.COMPONENT_UPDATE_JOB_ID: + return BACKGROUND_TASK_COMPONENT_UPDATE; default: assert false; }
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java index 4210211..9257fba 100644 --- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java +++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
@@ -27,6 +27,7 @@ public static final int WEBAPK_UPDATE_JOB_ID = 91; public static final int DOWNLOAD_RESUMPTION_JOB_ID = 55; public static final int FEED_REFRESH_JOB_ID = 22; + public static final int COMPONENT_UPDATE_JOB_ID = 2; private TaskIds() {} }
diff --git a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java index caa0465c..2dab5b2 100644 --- a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java +++ b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java
@@ -84,7 +84,10 @@ TaskIds.DOWNLOAD_RESUMPTION_JOB_ID)); assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_FEED_REFRESH, BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(TaskIds.FEED_REFRESH_JOB_ID)); - assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT, 15); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COMPONENT_UPDATE, + BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId( + TaskIds.COMPONENT_UPDATE_JOB_ID)); + assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT, 16); } @Test
diff --git a/components/consent_auditor/consent_auditor.cc b/components/consent_auditor/consent_auditor.cc index 3dbb22b..678f169 100644 --- a/components/consent_auditor/consent_auditor.cc +++ b/components/consent_auditor/consent_auditor.cc
@@ -90,11 +90,12 @@ user_event_service_(user_event_service), app_version_(app_version), app_locale_(app_locale) { - DCHECK(!IsSeparateConsentTypeEnabled() || consent_sync_bridge_); + if (IsSeparateConsentTypeEnabled()) { + DCHECK(consent_sync_bridge_); + } else { + DCHECK(user_event_service_); + } DCHECK(pref_service_); - // TODO(vitaliii): Don't require user_event_service when the separate datatype - // is enabled. - DCHECK(user_event_service_); } ConsentAuditor::~ConsentAuditor() {}
diff --git a/components/cryptauth/BUILD.gn b/components/cryptauth/BUILD.gn index fd210f0..21b14b6e 100644 --- a/components/cryptauth/BUILD.gn +++ b/components/cryptauth/BUILD.gn
@@ -16,9 +16,6 @@ "background_eid_generator.h", "connection.cc", "connection.h", - "cryptauth_access_token_fetcher.h", - "cryptauth_access_token_fetcher_impl.cc", - "cryptauth_access_token_fetcher_impl.h", "cryptauth_api_call_flow.cc", "cryptauth_api_call_flow.h", "cryptauth_client.h", @@ -112,6 +109,7 @@ "//crypto", "//google_apis", "//net", + "//services/identity/public/cpp", ] public_deps = [ @@ -185,7 +183,6 @@ sources = [ "background_eid_generator_unittest.cc", "connection_unittest.cc", - "cryptauth_access_token_fetcher_impl_unittest.cc", "cryptauth_api_call_flow_unittest.cc", "cryptauth_client_impl_unittest.cc", "cryptauth_device_manager_impl_unittest.cc", @@ -220,6 +217,7 @@ "//components/prefs:test_support", "//google_apis:test_support", "//net:test_support", + "//services/identity/public/cpp:test_support", "//testing/gtest", ] }
diff --git a/components/cryptauth/DEPS b/components/cryptauth/DEPS index 0779a592f..2193bca9 100644 --- a/components/cryptauth/DEPS +++ b/components/cryptauth/DEPS
@@ -12,5 +12,6 @@ "+device/bluetooth", "+google_apis", "+net", + "+services/identity/public/cpp", "+third_party/cros_system_api/dbus/service_constants.h", ]
diff --git a/components/cryptauth/cryptauth_access_token_fetcher.h b/components/cryptauth/cryptauth_access_token_fetcher.h deleted file mode 100644 index d79e7bd..0000000 --- a/components/cryptauth/cryptauth_access_token_fetcher.h +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_H_ -#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_H_ - -#include <string> - -#include "base/callback_forward.h" - -namespace cryptauth { - -// Simple interface for fetching the OAuth2 access token that authorizes -// CryptAuth API calls. Do not reuse this after calling FetchAccessToken(); -// instead, create a new instance. -class CryptAuthAccessTokenFetcher { - public: - virtual ~CryptAuthAccessTokenFetcher() {} - - // Fetches the access token asynchronously, invoking the callback upon - // completion. If the fetch fails, the callback will be invoked with an empty - // string. - typedef base::Callback<void(const std::string&)> AccessTokenCallback; - virtual void FetchAccessToken(const AccessTokenCallback& callback) = 0; -}; - -} // namespace cryptauth - -#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_H_
diff --git a/components/cryptauth/cryptauth_access_token_fetcher_impl.cc b/components/cryptauth/cryptauth_access_token_fetcher_impl.cc deleted file mode 100644 index f9ba926..0000000 --- a/components/cryptauth/cryptauth_access_token_fetcher_impl.cc +++ /dev/null
@@ -1,60 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/cryptauth/cryptauth_access_token_fetcher_impl.h" - -namespace cryptauth { - -namespace { - -// Returns the set of OAuth2 scopes that CryptAuth uses. -OAuth2TokenService::ScopeSet GetScopes() { - OAuth2TokenService::ScopeSet scopes; - scopes.insert("https://www.googleapis.com/auth/cryptauth"); - return scopes; -} - -} // namespace - -CryptAuthAccessTokenFetcherImpl::CryptAuthAccessTokenFetcherImpl( - OAuth2TokenService* token_service, - const std::string& account_id) - : OAuth2TokenService::Consumer("cryptauth_access_token_fetcher"), - token_service_(token_service), - account_id_(account_id), - fetch_started_(false) { -} - -CryptAuthAccessTokenFetcherImpl::~CryptAuthAccessTokenFetcherImpl() { -} - -void CryptAuthAccessTokenFetcherImpl::FetchAccessToken( - const AccessTokenCallback& callback) { - if (fetch_started_) { - LOG(WARNING) << "Create an instance for each token fetched. Do not reuse."; - callback.Run(std::string()); - return; - } - - fetch_started_ = true; - callback_ = callback; - // This request will return a cached result if it is available, saving a - // network round trip every time we fetch the access token. - token_request_ = token_service_->StartRequest(account_id_, GetScopes(), this); -} - -void CryptAuthAccessTokenFetcherImpl::OnGetTokenSuccess( - const OAuth2TokenService::Request* request, - const std::string& access_token, - const base::Time& expiration_time) { - callback_.Run(access_token); -} - -void CryptAuthAccessTokenFetcherImpl::OnGetTokenFailure( - const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) { - callback_.Run(std::string()); -} - -} // namespace cryptauth
diff --git a/components/cryptauth/cryptauth_access_token_fetcher_impl.h b/components/cryptauth/cryptauth_access_token_fetcher_impl.h deleted file mode 100644 index f683b54..0000000 --- a/components/cryptauth/cryptauth_access_token_fetcher_impl.h +++ /dev/null
@@ -1,59 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_IMPL_H_ -#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_IMPL_H_ - -#include <memory> - -#include "base/callback.h" -#include "base/macros.h" -#include "components/cryptauth/cryptauth_access_token_fetcher.h" -#include "google_apis/gaia/oauth2_token_service.h" - -namespace cryptauth { - -// Implementation of CryptAuthAccessTokenFetcher fetching an access token for a -// given account using the provided OAuth2TokenService. -class CryptAuthAccessTokenFetcherImpl : public CryptAuthAccessTokenFetcher, - public OAuth2TokenService::Consumer { - public: - // |token_service| is not owned, and must outlive this object. - CryptAuthAccessTokenFetcherImpl(OAuth2TokenService* token_service, - const std::string& account_id); - ~CryptAuthAccessTokenFetcherImpl() override; - - // CryptAuthAccessTokenFetcher: - void FetchAccessToken(const AccessTokenCallback& callback) override; - - private: - // OAuth2TokenService::Consumer: - void OnGetTokenSuccess(const OAuth2TokenService::Request* request, - const std::string& access_token, - const base::Time& expiration_time) override; - void OnGetTokenFailure(const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) override; - - // System service that caches and fetches tokens for a given account. - // Not owned. - OAuth2TokenService* token_service_; - - // The account id for whom to mint the token. - std::string account_id_; - - // True if FetchAccessToken() has been called. - bool fetch_started_; - - // Stores the request from |token_service_| to mint the token. - std::unique_ptr<OAuth2TokenService::Request> token_request_; - - // Callback to invoke when the token fetch succeeds or fails. - AccessTokenCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(CryptAuthAccessTokenFetcherImpl); -}; - -} // namespace cryptauth - -#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_IMPL_H_
diff --git a/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc b/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc deleted file mode 100644 index 3e72c23..0000000 --- a/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc +++ /dev/null
@@ -1,76 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/cryptauth/cryptauth_access_token_fetcher_impl.h" - -#include <string> - -#include "base/bind.h" -#include "base/macros.h" -#include "google_apis/gaia/fake_oauth2_token_service.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cryptauth { - -namespace { - -const char kAccountId[] = "account_id"; -const char kAccessToken[] = "access_token"; -const char kInvalidResult[] = "invalid_result"; - -// Callback that saves the fetched access token to the first argument. -void SaveAccessToken(std::string* out_token, const std::string& in_token) { - *out_token = in_token; -} - -} // namespace - -class CryptAuthAccessTokenFetcherTest : public testing::Test { - protected: - CryptAuthAccessTokenFetcherTest() - : fetcher_(&token_service_, kAccountId) { - token_service_.AddAccount(kAccountId); - } - - FakeOAuth2TokenService token_service_; - CryptAuthAccessTokenFetcherImpl fetcher_; - - DISALLOW_COPY_AND_ASSIGN(CryptAuthAccessTokenFetcherTest); -}; - -TEST_F(CryptAuthAccessTokenFetcherTest, FetchSuccess) { - std::string result; - fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result)); - token_service_.IssueAllTokensForAccount(kAccountId, kAccessToken, - base::Time::Max()); - - EXPECT_EQ(kAccessToken, result); -} - -TEST_F(CryptAuthAccessTokenFetcherTest, FetchFailure) { - std::string result(kInvalidResult); - fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result)); - token_service_.IssueErrorForAllPendingRequestsForAccount( - kAccountId, - GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR)); - - EXPECT_EQ(std::string(), result); -} - -TEST_F(CryptAuthAccessTokenFetcherTest, FetcherReuse) { - std::string result1; - fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result1)); - - { - std::string result2(kInvalidResult); - fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result2)); - EXPECT_EQ(std::string(), result2); - } - - token_service_.IssueAllTokensForAccount(kAccountId, kAccessToken, - base::Time::Max()); - EXPECT_EQ(kAccessToken, result1); -} - -} // namespace cryptauth
diff --git a/components/cryptauth/cryptauth_client_impl.cc b/components/cryptauth/cryptauth_client_impl.cc index f16b4a2..19f7e0e6 100644 --- a/components/cryptauth/cryptauth_client_impl.cc +++ b/components/cryptauth/cryptauth_client_impl.cc
@@ -10,8 +10,9 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/memory/ptr_util.h" -#include "components/cryptauth/cryptauth_access_token_fetcher_impl.h" #include "components/cryptauth/switches.h" +#include "services/identity/public/cpp/identity_manager.h" +#include "services/identity/public/cpp/primary_account_access_token_fetcher.h" namespace cryptauth { @@ -53,11 +54,11 @@ CryptAuthClientImpl::CryptAuthClientImpl( std::unique_ptr<CryptAuthApiCallFlow> api_call_flow, - std::unique_ptr<CryptAuthAccessTokenFetcher> access_token_fetcher, + identity::IdentityManager* identity_manager, scoped_refptr<net::URLRequestContextGetter> url_request_context, const DeviceClassifier& device_classifier) : api_call_flow_(std::move(api_call_flow)), - access_token_fetcher_(std::move(access_token_fetcher)), + identity_manager_(identity_manager), url_request_context_(url_request_context), device_classifier_(device_classifier), has_call_started_(false), @@ -277,17 +278,29 @@ request_path_ = request_path; error_callback_ = error_callback; - access_token_fetcher_->FetchAccessToken(base::Bind( - &CryptAuthClientImpl::OnAccessTokenFetched<ResponseProto>, - weak_ptr_factory_.GetWeakPtr(), serialized_request, response_callback)); + + OAuth2TokenService::ScopeSet scopes; + scopes.insert("https://www.googleapis.com/auth/cryptauth"); + + access_token_fetcher_ = + identity_manager_->CreateAccessTokenFetcherForPrimaryAccount( + "cryptauth_client", scopes, + base::BindOnce( + &CryptAuthClientImpl::OnAccessTokenFetched<ResponseProto>, + weak_ptr_factory_.GetWeakPtr(), serialized_request, + response_callback), + identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate); } template <class ResponseProto> void CryptAuthClientImpl::OnAccessTokenFetched( const std::string& serialized_request, const base::Callback<void(const ResponseProto&)>& response_callback, - const std::string& access_token) { - if (access_token.empty()) { + GoogleServiceAuthError error, + std::string access_token) { + access_token_fetcher_.reset(); + + if (error.state() != GoogleServiceAuthError::NONE) { OnApiCallFailed("Failed to get a valid access token."); return; } @@ -320,24 +333,19 @@ // CryptAuthClientFactoryImpl CryptAuthClientFactoryImpl::CryptAuthClientFactoryImpl( - OAuth2TokenService* token_service, - const std::string& account_id, + identity::IdentityManager* identity_manager, scoped_refptr<net::URLRequestContextGetter> url_request_context, const DeviceClassifier& device_classifier) - : token_service_(token_service), - account_id_(account_id), + : identity_manager_(identity_manager), url_request_context_(url_request_context), - device_classifier_(device_classifier) { -} + device_classifier_(device_classifier) {} CryptAuthClientFactoryImpl::~CryptAuthClientFactoryImpl() { } std::unique_ptr<CryptAuthClient> CryptAuthClientFactoryImpl::CreateInstance() { return std::make_unique<CryptAuthClientImpl>( - base::WrapUnique(new CryptAuthApiCallFlow()), - base::WrapUnique( - new CryptAuthAccessTokenFetcherImpl(token_service_, account_id_)), + base::WrapUnique(new CryptAuthApiCallFlow()), identity_manager_, url_request_context_, device_classifier_); }
diff --git a/components/cryptauth/cryptauth_client_impl.h b/components/cryptauth/cryptauth_client_impl.h index 3ccea85..b054edc 100644 --- a/components/cryptauth/cryptauth_client_impl.h +++ b/components/cryptauth/cryptauth_client_impl.h
@@ -7,14 +7,17 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "components/cryptauth/cryptauth_access_token_fetcher.h" #include "components/cryptauth/cryptauth_api_call_flow.h" #include "components/cryptauth/cryptauth_client.h" #include "components/cryptauth/proto/cryptauth_api.pb.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/url_request_context_getter.h" -class OAuth2TokenService; +namespace identity { +class IdentityManager; +class PrimaryAccountAccessTokenFetcher; +} // namespace identity +class GoogleServiceAuthError; namespace cryptauth { @@ -26,13 +29,11 @@ typedef base::Callback<void(const std::string&)> ErrorCallback; // Creates the client using |url_request_context| to make the HTTP request - // through |api_call_flow|. CryptAuthClientImpl takes ownership of - // |access_token_fetcher|, which provides the access token authorizing - // CryptAuth requests. The |device_classifier| argument contains basic device - // information of the caller (e.g. version and device type). + // through |api_call_flow|. The |device_classifier| argument contains basic + // device information of the caller (e.g. version and device type). CryptAuthClientImpl( std::unique_ptr<CryptAuthApiCallFlow> api_call_flow, - std::unique_ptr<CryptAuthAccessTokenFetcher> access_token_fetcher, + identity::IdentityManager* identity_manager, scoped_refptr<net::URLRequestContextGetter> url_request_context, const DeviceClassifier& device_classifier); ~CryptAuthClientImpl() override; @@ -85,7 +86,8 @@ void OnAccessTokenFetched( const std::string& serialized_request, const base::Callback<void(const ResponseProto&)>& response_callback, - const std::string& access_token); + GoogleServiceAuthError error, + std::string access_token); // Called with CryptAuthApiCallFlow completes successfully to deserialize and // return the result. @@ -100,8 +102,11 @@ // Constructs and executes the actual HTTP request. std::unique_ptr<CryptAuthApiCallFlow> api_call_flow_; + identity::IdentityManager* identity_manager_; + // Fetches the access token authorizing the API calls. - std::unique_ptr<CryptAuthAccessTokenFetcher> access_token_fetcher_; + std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher> + access_token_fetcher_; // The context for network requests. scoped_refptr<net::URLRequestContextGetter> url_request_context_; @@ -131,14 +136,12 @@ // Implementation of CryptAuthClientFactory. class CryptAuthClientFactoryImpl : public CryptAuthClientFactory { public: - // |token_service|: Gets the user's access token. - // Not owned, so |token_service| needs to outlive this object. - // |account_id|: The account id of the user. + // |identity_manager|: Gets the user's access token. + // Not owned, so |identity_manager| needs to outlive this object. // |url_request_context|: The request context to make the HTTP requests. // |device_classifier|: Contains basic device information of the client. CryptAuthClientFactoryImpl( - OAuth2TokenService* token_service, - const std::string& account_id, + identity::IdentityManager* identity_manager, scoped_refptr<net::URLRequestContextGetter> url_request_context, const DeviceClassifier& device_classifier); ~CryptAuthClientFactoryImpl() override; @@ -147,8 +150,7 @@ std::unique_ptr<CryptAuthClient> CreateInstance() override; private: - OAuth2TokenService* token_service_; - const std::string account_id_; + identity::IdentityManager* identity_manager_; const scoped_refptr<net::URLRequestContextGetter> url_request_context_; const DeviceClassifier device_classifier_;
diff --git a/components/cryptauth/cryptauth_client_impl_unittest.cc b/components/cryptauth/cryptauth_client_impl_unittest.cc index 960278d..d30fe59b 100644 --- a/components/cryptauth/cryptauth_client_impl_unittest.cc +++ b/components/cryptauth/cryptauth_client_impl_unittest.cc
@@ -8,14 +8,14 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/test/null_task_runner.h" -#include "components/cryptauth/cryptauth_access_token_fetcher.h" +#include "base/test/scoped_task_environment.h" #include "components/cryptauth/cryptauth_api_call_flow.h" #include "components/cryptauth/proto/cryptauth_api.pb.h" #include "components/cryptauth/switches.h" -#include "google_apis/gaia/fake_oauth2_token_service.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/url_request_test_util.h" +#include "services/identity/public/cpp/identity_test_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -32,6 +32,7 @@ const char kTestGoogleApisUrl[] = "https://www.testgoogleapis.com"; const char kAccessToken[] = "access_token"; +const char kEmail[] = "test@gmail.com"; const char kPublicKey1[] = "public_key1"; const char kPublicKey2[] = "public_key2"; const char kBluetoothAddress1[] = "AA:AA:AA:AA:AA:AA"; @@ -43,24 +44,6 @@ const char kDeviceSoftwarePackage[] = "cryptauth_client_unittest"; const DeviceType kDeviceType = CHROME; -// CryptAuthAccessTokenFetcher implementation simply returning a predetermined -// access token. -class FakeCryptAuthAccessTokenFetcher : public CryptAuthAccessTokenFetcher { - public: - FakeCryptAuthAccessTokenFetcher() : access_token_(kAccessToken) {} - - void FetchAccessToken(const AccessTokenCallback& callback) override { - callback.Run(access_token_); - } - - void set_access_token(const std::string& access_token) { - access_token_ = access_token; - }; - - private: - std::string access_token_; -}; - // Mock CryptAuthApiCallFlow, which handles the HTTP requests to CryptAuth. class MockCryptAuthApiCallFlow : public CryptAuthApiCallFlow { public: @@ -98,8 +81,7 @@ class CryptAuthClientTest : public testing::Test { protected: CryptAuthClientTest() - : access_token_fetcher_(new FakeCryptAuthAccessTokenFetcher()), - api_call_flow_(new StrictMock<MockCryptAuthApiCallFlow>()), + : api_call_flow_(new StrictMock<MockCryptAuthApiCallFlow>()), url_request_context_( new net::TestURLRequestContextGetter(new base::NullTaskRunner())), serialized_request_(std::string()) {} @@ -115,9 +97,11 @@ device_classifier.set_device_software_package(kDeviceSoftwarePackage); device_classifier.set_device_type(kDeviceType); + identity_test_environment_.MakePrimaryAccountAvailable(kEmail); + client_.reset( new CryptAuthClientImpl(base::WrapUnique(api_call_flow_), - base::WrapUnique(access_token_fetcher_), + identity_test_environment_.identity_manager(), url_request_context_, device_classifier)); } @@ -145,8 +129,8 @@ } protected: - // Owned by |client_|. - FakeCryptAuthAccessTokenFetcher* access_token_fetcher_; + base::test::ScopedTaskEnvironment scoped_task_environment_; + identity::IdentityTestEnvironment identity_test_environment_; // Owned by |client_|. StrictMock<MockCryptAuthApiCallFlow>* api_call_flow_; @@ -171,6 +155,9 @@ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto), base::Bind(&NotCalled<std::string>), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); GetMyDevicesRequest expected_request; EXPECT_TRUE(expected_request.ParseFromString(serialized_request_)); @@ -209,6 +196,9 @@ base::Bind(&NotCalled<GetMyDevicesResponse>), base::Bind(&SaveResult<std::string>, &error_message), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); std::string kStatus500Error("HTTP status: 500"); FailApiCallFlow(kStatus500Error); @@ -228,6 +218,9 @@ base::Bind(&SaveResult<FindEligibleUnlockDevicesResponse>, &result_proto), base::Bind(&NotCalled<std::string>)); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); FindEligibleUnlockDevicesRequest expected_request; EXPECT_TRUE(expected_request.ParseFromString(serialized_request_)); @@ -271,6 +264,9 @@ request_proto, base::Bind(&NotCalled<FindEligibleUnlockDevicesResponse>), base::Bind(&SaveResult<std::string>, &error_message)); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); std::string kStatus403Error("HTTP status: 403"); FailApiCallFlow(kStatus403Error); @@ -287,6 +283,9 @@ FindEligibleForPromotionRequest(), base::Bind(&SaveResult<FindEligibleForPromotionResponse>, &result_proto), base::Bind(&NotCalled<std::string>)); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); FindEligibleForPromotionRequest expected_request; EXPECT_TRUE(expected_request.ParseFromString(serialized_request_)); @@ -306,6 +305,9 @@ base::Bind(&SaveResult<SendDeviceSyncTickleResponse>, &result_proto), base::Bind(&NotCalled<std::string>), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); SendDeviceSyncTickleRequest expected_request; EXPECT_TRUE(expected_request.ParseFromString(serialized_request_)); @@ -329,6 +331,9 @@ base::Bind(&SaveResult<ToggleEasyUnlockResponse>, &result_proto), base::Bind(&NotCalled<std::string>)); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); ToggleEasyUnlockRequest expected_request; EXPECT_TRUE(expected_request.ParseFromString(serialized_request_)); @@ -359,6 +364,9 @@ request_proto, base::Bind(&SaveResult<SetupEnrollmentResponse>, &result_proto), base::Bind(&NotCalled<std::string>)); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); SetupEnrollmentRequest expected_request; EXPECT_TRUE(expected_request.ParseFromString(serialized_request_)); @@ -404,6 +412,9 @@ base::Bind(&SaveResult<FinishEnrollmentResponse>, &result_proto), base::Bind(&NotCalled<const std::string&>)); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); FinishEnrollmentRequest expected_request; EXPECT_TRUE(expected_request.ParseFromString(serialized_request_)); @@ -420,13 +431,14 @@ } TEST_F(CryptAuthClientTest, FetchAccessTokenFailure) { - access_token_fetcher_->set_access_token(""); - std::string error_message; client_->GetMyDevices(GetMyDevicesRequest(), base::Bind(&NotCalled<GetMyDevicesResponse>), base::Bind(&SaveResult<std::string>, &error_message), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); EXPECT_EQ("Failed to get a valid access token.", error_message); } @@ -441,6 +453,9 @@ base::Bind(&NotCalled<GetMyDevicesResponse>), base::Bind(&SaveResult<std::string>, &error_message), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); flow_result_callback_.Run("Not a valid serialized response message."); EXPECT_EQ("Failed to parse response proto.", error_message); @@ -459,6 +474,9 @@ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto), base::Bind(&NotCalled<std::string>), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); // With request pending, make second request. { @@ -495,6 +513,9 @@ base::Bind(&NotCalled<GetMyDevicesResponse>), base::Bind(&SaveResult<std::string>, &error_message), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); // With request pending, make second request. { @@ -526,6 +547,9 @@ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto), base::Bind(&NotCalled<std::string>), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); GetMyDevicesResponse response_proto; response_proto.add_devices(); @@ -560,6 +584,9 @@ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto), base::Bind(&NotCalled<std::string>), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); GetMyDevicesRequest expected_request; EXPECT_TRUE(expected_request.ParseFromString(serialized_request_)); @@ -575,6 +602,7 @@ TEST_F(CryptAuthClientTest, GetAccessTokenUsed) { EXPECT_TRUE(client_->GetAccessTokenUsed().empty()); + ExpectRequest( "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" "getmydevices?alt=proto"); @@ -587,6 +615,10 @@ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto), base::Bind(&NotCalled<std::string>), PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS); + identity_test_environment_ + .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Max()); + EXPECT_EQ(kAccessToken, client_->GetAccessTokenUsed()); }
diff --git a/components/exo/surface_unittest.cc b/components/exo/surface_unittest.cc index e0ccfb9..27afc38 100644 --- a/components/exo/surface_unittest.cc +++ b/components/exo/surface_unittest.cc
@@ -182,7 +182,13 @@ EXPECT_TRUE(frame_time.is_null()); } -TEST_P(SurfaceTest, SetOpaqueRegion) { +// Disabled due to flakiness: crbug.com/856145 +#if defined(LEAK_SANITIZER) +#define MAYBE_SetOpaqueRegion DISABLED_SetOpaqueRegion +#else +#define MAYBE_SetOpaqueRegion SetOpaqueRegion +#endif +TEST_P(SurfaceTest, MAYBE_SetOpaqueRegion) { gfx::Size buffer_size(1, 1); auto buffer = std::make_unique<Buffer>( exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)); @@ -369,7 +375,13 @@ frame.render_pass_list.back()->damage_rect); } -TEST_P(SurfaceTest, SetBufferTransform) { +// Disabled due to flakiness: crbug.com/856145 +#if defined(LEAK_SANITIZER) +#define MAYBE_SetBufferTransform DISABLED_SetBufferTransform +#else +#define MAYBE_SetBufferTransform SetBufferTransform +#endif +TEST_P(SurfaceTest, MAYBE_SetBufferTransform) { gfx::Size buffer_size(256, 512); auto buffer = std::make_unique<Buffer>( exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)); @@ -522,7 +534,13 @@ frame.render_pass_list.back()->damage_rect); } -TEST_P(SurfaceTest, SetCropAndBufferTransform) { +// Disabled due to flakiness: crbug.com/856145 +#if defined(LEAK_SANITIZER) +#define MAYBE_SetCropAndBufferTransform DISABLED_SetCropAndBufferTransform +#else +#define MAYBE_SetCropAndBufferTransform SetCropAndBufferTransform +#endif +TEST_P(SurfaceTest, MAYBE_SetCropAndBufferTransform) { gfx::Size buffer_size(128, 64); auto buffer = std::make_unique<Buffer>( exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc index 20755cb1..7fc6f4b 100644 --- a/components/password_manager/core/browser/password_autofill_manager.cc +++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -141,20 +141,6 @@ #endif } -void AddSimpleSuggestionWithSeparatorOnTop( - int value, - int frontend_id, - std::vector<autofill::Suggestion>* suggestions) { -#if !defined(OS_ANDROID) - suggestions->push_back(autofill::Suggestion()); - suggestions->back().frontend_id = autofill::POPUP_ITEM_ID_SEPARATOR; -#endif - - autofill::Suggestion suggestion(l10n_util::GetStringUTF8(value), - std::string(), std::string(), frontend_id); - suggestions->push_back(suggestion); -} - } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -241,24 +227,17 @@ return; } - if (options & autofill::IS_PASSWORD_FIELD) { - autofill::Suggestion password_field_suggestions(l10n_util::GetStringUTF16( - IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE)); - password_field_suggestions.frontend_id = autofill::POPUP_ITEM_ID_TITLE; - suggestions.insert(suggestions.begin(), password_field_suggestions); - } - - GURL origin = (fill_data_it->second).origin; + GURL origin = fill_data_it->second.origin; if (ShouldShowManualFallbackForPreLollipop( autofill_client_->GetSyncService())) { - if (base::FeatureList::IsEnabled( - password_manager::features::kManualFallbacksFilling) && - (options & autofill::IS_PASSWORD_FIELD) && password_client_ && + if (password_client_ && password_client_->IsFillingFallbackEnabledForCurrentPage()) { - AddSimpleSuggestionWithSeparatorOnTop( - IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK, - autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY, &suggestions); + autofill::Suggestion suggestion( + l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS), + std::string(), std::string(), + autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY); + suggestions.push_back(suggestion); show_all_saved_passwords_shown_context_ = metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD;
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc index 9c18dcd..006da433 100644 --- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc +++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -130,6 +130,15 @@ #endif } +std::vector<base::string16> GetSuggestionList( + std::vector<base::string16> credentials) { + if (!IsPreLollipopAndroid()) { + credentials.push_back( + l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS)); + } + return credentials; +} + } // namespace class PasswordAutofillManagerTest : public testing::Test { @@ -165,16 +174,6 @@ int fill_data_id() { return fill_data_id_; } autofill::PasswordFormFillData& fill_data() { return fill_data_; } - void SetManualFallbacksForFilling(bool enabled) { - if (enabled) { - scoped_feature_list_.InitAndEnableFeature( - password_manager::features::kManualFallbacksFilling); - } else { - scoped_feature_list_.InitAndDisableFeature( - password_manager::features::kManualFallbacksFilling); - } - } - void SetManualFallbacksForFillingStandalone(bool enabled) { if (enabled) { scoped_feature_list_.InitAndEnableFeature( @@ -185,12 +184,6 @@ } } - static bool IsManualFallbackForFillingEnabled() { - return base::FeatureList::IsEnabled( - password_manager::features::kManualFallbacksFilling) && - !IsPreLollipopAndroid(); - } - std::unique_ptr<PasswordAutofillManager> password_autofill_manager_; base::string16 test_username_; @@ -260,6 +253,8 @@ // suggestions. TEST_F(PasswordAutofillManagerTest, ExternalDelegatePasswordSuggestions) { for (bool is_suggestion_on_password_field : {false, true}) { + SCOPED_TRACE(testing::Message() << "is_suggestion_on_password_field = " + << is_suggestion_on_password_field); std::unique_ptr<TestPasswordManagerClient> client( new TestPasswordManagerClient); std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient); @@ -277,16 +272,11 @@ FillSuggestion(test_username_, test_password_)); std::vector<autofill::PopupItemId> ids = { - autofill::POPUP_ITEM_ID_USERNAME_ENTRY}; - if (is_suggestion_on_password_field) { - ids = {autofill::POPUP_ITEM_ID_TITLE, - autofill::POPUP_ITEM_ID_PASSWORD_ENTRY}; - if (IsManualFallbackForFillingEnabled()) { -#if !defined(OS_ANDROID) - ids.push_back(autofill::POPUP_ITEM_ID_SEPARATOR); -#endif - ids.push_back(autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY); - } + is_suggestion_on_password_field + ? autofill::POPUP_ITEM_ID_PASSWORD_ENTRY + : autofill::POPUP_ITEM_ID_USERNAME_ENTRY}; + if (!IsPreLollipopAndroid()) { + ids.push_back(autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY); } EXPECT_CALL( *autofill_client, @@ -334,41 +324,43 @@ // First, simulate displaying suggestions matching an empty prefix. Also // verify that both the values and labels are filled correctly. The 'value' // should be the user name; the 'label' should be the realm. - EXPECT_CALL(*autofill_client, - ShowAutofillPopup( - element_bounds, _, - testing::AllOf( - SuggestionVectorValuesAre(testing::UnorderedElementsAre( - test_username_, additional_username)), - SuggestionVectorLabelsAre(testing::UnorderedElementsAre( - base::UTF8ToUTF16(data.preferred_realm), - base::UTF8ToUTF16(additional.realm)))), - _)); + EXPECT_CALL( + *autofill_client, + ShowAutofillPopup( + element_bounds, _, + testing::AllOf( + SuggestionVectorValuesAre(testing::UnorderedElementsAreArray( + GetSuggestionList({test_username_, additional_username}))), + SuggestionVectorLabelsAre(testing::AllOf( + testing::Contains(base::UTF8ToUTF16(data.preferred_realm)), + testing::Contains(base::UTF8ToUTF16(additional.realm))))), + _)); password_autofill_manager_->OnShowPasswordSuggestions( - dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(), false, + dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(), 0, element_bounds); // Now simulate displaying suggestions matching "John". EXPECT_CALL( *autofill_client, ShowAutofillPopup(element_bounds, _, - SuggestionVectorValuesAre( - testing::UnorderedElementsAre(additional_username)), + SuggestionVectorValuesAre(testing::ElementsAreArray( + GetSuggestionList({additional_username}))), _)); password_autofill_manager_->OnShowPasswordSuggestions( - dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("John"), false, + dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("John"), 0, element_bounds); // Finally, simulate displaying all suggestions, without any prefix matching. EXPECT_CALL( *autofill_client, - ShowAutofillPopup(element_bounds, _, - SuggestionVectorValuesAre(testing::UnorderedElementsAre( - test_username_, additional_username)), - _)); + ShowAutofillPopup( + element_bounds, _, + SuggestionVectorValuesAre(testing::ElementsAreArray( + GetSuggestionList({test_username_, additional_username}))), + _)); password_autofill_manager_->OnShowPasswordSuggestions( - dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("xyz"), true, - element_bounds); + dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("xyz"), + autofill::SHOW_ALL, element_bounds); } // Verify that, for Android application credentials, the prettified realms of @@ -393,15 +385,15 @@ password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data); EXPECT_CALL(*autofill_client, - ShowAutofillPopup( - _, _, - SuggestionVectorLabelsAre(testing::UnorderedElementsAre( - base::ASCIIToUTF16("android://com.example1.android/"), - base::ASCIIToUTF16("android://com.example2.android/"))), - _)); + ShowAutofillPopup(_, _, + SuggestionVectorLabelsAre(testing::AllOf( + testing::Contains(base::ASCIIToUTF16( + "android://com.example1.android/")), + testing::Contains(base::ASCIIToUTF16( + "android://com.example2.android/")))), + _)); password_autofill_manager_->OnShowPasswordSuggestions( - dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(), false, - gfx::RectF()); + dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(), 0, gfx::RectF()); } TEST_F(PasswordAutofillManagerTest, FillSuggestionPasswordField) { @@ -427,27 +419,12 @@ int dummy_key = 0; password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data); - // Simulate displaying suggestions matching a username and specifying that the - // field is a password field. - base::string16 title = l10n_util::GetStringUTF16( - IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE); - std::vector<base::string16> elements = {title, test_username_}; - if (IsManualFallbackForFillingEnabled()) { - elements = { - title, - test_username_, -#if !defined(OS_ANDROID) - base::string16(), -#endif - l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK) - }; - } - EXPECT_CALL( *autofill_client, - ShowAutofillPopup( - element_bounds, _, - SuggestionVectorValuesAre(testing::ElementsAreArray(elements)), _)); + ShowAutofillPopup(element_bounds, _, + SuggestionVectorValuesAre(testing::ElementsAreArray( + GetSuggestionList({test_username_}))), + _)); password_autofill_manager_->OnShowPasswordSuggestions( dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_, autofill::IS_PASSWORD_FIELD, element_bounds); @@ -483,14 +460,14 @@ int dummy_key = 0; password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data); - EXPECT_CALL( - *autofill_client, - ShowAutofillPopup(element_bounds, _, - SuggestionVectorValuesAre(testing::UnorderedElementsAre( - username, additional_username)), - _)); + EXPECT_CALL(*autofill_client, + ShowAutofillPopup( + element_bounds, _, + SuggestionVectorValuesAre(testing::UnorderedElementsAreArray( + GetSuggestionList({username, additional_username}))), + _)); password_autofill_manager_->OnShowPasswordSuggestions( - dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo"), false, + dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo"), 0, element_bounds); } @@ -527,7 +504,7 @@ EXPECT_CALL(*autofill_client, ShowAutofillPopup(_, _, _, _)).Times(0); password_autofill_manager_->OnShowPasswordSuggestions( - dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("oo"), false, + dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("oo"), 0, element_bounds); } @@ -566,12 +543,12 @@ EXPECT_CALL( *autofill_client, ShowAutofillPopup(element_bounds, _, - SuggestionVectorValuesAre( - testing::UnorderedElementsAre(additional_username)), + SuggestionVectorValuesAre(testing::ElementsAreArray( + GetSuggestionList({additional_username}))), _)); password_autofill_manager_->OnShowPasswordSuggestions( - dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo@exam"), - false, element_bounds); + dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo@exam"), 0, + element_bounds); } // Verify that typing "example" into the username field will match and order @@ -606,12 +583,12 @@ int dummy_key = 0; password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data); - EXPECT_CALL( - *autofill_client, - ShowAutofillPopup(element_bounds, _, - SuggestionVectorValuesAre(testing::UnorderedElementsAre( - username, additional_username)), - _)); + EXPECT_CALL(*autofill_client, + ShowAutofillPopup( + element_bounds, _, + SuggestionVectorValuesAre(testing::ElementsAreArray( + GetSuggestionList({username, additional_username}))), + _)); password_autofill_manager_->OnShowPasswordSuggestions( dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo"), false, element_bounds); @@ -651,45 +628,8 @@ testing::Mock::VerifyAndClearExpectations(client->mock_driver()); } -// Tests that the "Show all passwords" suggestion isn't shown along with -// "Use password for" in the popup when the feature which controls its -// appearance is disabled. -TEST_F(PasswordAutofillManagerTest, - NotShowAllPasswordsOptionOnPasswordFieldWhenFeatureDisabled) { - auto client = std::make_unique<TestPasswordManagerClient>(); - auto autofill_client = std::make_unique<MockAutofillClient>(); - InitializePasswordAutofillManager(client.get(), autofill_client.get()); - - gfx::RectF element_bounds; - autofill::PasswordFormFillData data; - data.username_field.value = test_username_; - data.password_field.value = test_password_; - data.origin = GURL("https://foo.test"); - - int dummy_key = 0; - password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data); - - // String "Use password for:" shown when displaying suggestions matching a - // username and specifying that the field is a password field. - base::string16 title = - l10n_util::GetStringUTF16(IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE); - - SetManualFallbacksForFilling(false); - - // No "Show all passwords row" when feature is disabled. - EXPECT_CALL(*autofill_client, - ShowAutofillPopup(element_bounds, _, - SuggestionVectorValuesAre(testing::ElementsAre( - title, test_username_)), - _)); - password_autofill_manager_->OnShowPasswordSuggestions( - dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_, - autofill::IS_PASSWORD_FIELD, element_bounds); -} - -// Tests that the "Show all passwords" suggestion is shown along with -// "Use password for" in the popup when the feature which controls its -// appearance is enabled. +// Tests that the "Manage passwords" suggestion is shown along with the password +// popup. TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnPasswordField) { const char kShownContextHistogram[] = "PasswordManager.ShowAllSavedPasswordsShownContext"; @@ -716,30 +656,12 @@ int dummy_key = 0; password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data); - // String "Use password for:" shown when displaying suggestions matching a - // username and specifying that the field is a password field. - base::string16 title = - l10n_util::GetStringUTF16(IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE); - - SetManualFallbacksForFilling(true); - - std::vector<base::string16> elements = {title, test_username_}; - if (!IsPreLollipopAndroid()) { - elements = { - title, - test_username_, -#if !defined(OS_ANDROID) - base::string16(), -#endif - l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK) - }; - } - EXPECT_CALL( *autofill_client, - ShowAutofillPopup( - element_bounds, _, - SuggestionVectorValuesAre(testing::ElementsAreArray(elements)), _)); + ShowAutofillPopup(element_bounds, _, + SuggestionVectorValuesAre(testing::ElementsAreArray( + GetSuggestionList({test_username_}))), + _)); password_autofill_manager_->OnShowPasswordSuggestions( dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_, @@ -880,10 +802,9 @@ } } -// Tests that the "Show all passwords" fallback doesn't shows up in non-password +// Tests that the "Manage passwords" fallback shows up in non-password // fields of login forms. -TEST_F(PasswordAutofillManagerTest, - NotShowAllPasswordsOptionOnNonPasswordField) { +TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnNonPasswordField) { auto client = std::make_unique<TestPasswordManagerClient>(); auto autofill_client = std::make_unique<MockAutofillClient>(); InitializePasswordAutofillManager(client.get(), autofill_client.get()); @@ -897,13 +818,12 @@ int dummy_key = 0; password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data); - SetManualFallbacksForFilling(true); - EXPECT_CALL( *autofill_client, - ShowAutofillPopup( - element_bounds, _, - SuggestionVectorValuesAre(testing::ElementsAre(test_username_)), _)); + ShowAutofillPopup(element_bounds, _, + SuggestionVectorValuesAre(testing::ElementsAreArray( + GetSuggestionList({test_username_}))), + _)); password_autofill_manager_->OnShowPasswordSuggestions( dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_, 0, element_bounds); }
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc index 259c2fed..429aeaf 100644 --- a/components/password_manager/core/browser/password_manager.cc +++ b/components/password_manager/core/browser/password_manager.cc
@@ -510,7 +510,8 @@ password_manager::PasswordManagerDriver* driver, const PasswordForm& password_form) { if (!client_->IsSavingAndFillingEnabledForCurrentPage() || - ShouldBlockPasswordForSameOriginButDifferentScheme(password_form)) + ShouldBlockPasswordForSameOriginButDifferentScheme(password_form) || + !client_->GetStoreResultFilter()->ShouldSave(password_form)) return; PasswordFormManager* matched_manager = FindMatchedManager(
diff --git a/components/password_manager/core/browser/password_manager.h b/components/password_manager/core/browser/password_manager.h index 37bbc4f..39a593d 100644 --- a/components/password_manager/core/browser/password_manager.h +++ b/components/password_manager/core/browser/password_manager.h
@@ -124,7 +124,7 @@ // Handles a password form being submitted, assumes that submission is // successful and does not do any checks on success of submission. // For example, this is called if |password_form| was filled - // upon in-page navigation.This often means history.pushState being + // upon in-page navigation. This often means history.pushState being // called from JavaScript. void OnPasswordFormSubmittedNoChecks( password_manager::PasswordManagerDriver* driver,
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc index d9808cd..587ed1a 100644 --- a/components/password_manager/core/browser/password_manager_unittest.cc +++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -565,6 +565,35 @@ user_action_tester.GetActionCount("PasswordManager_LoginPassed")); } +// Tests that on Chrome sign-in form credentials are not saved. +TEST_F(PasswordManagerTest, DoNotSaveOnChromeSignInForm) { + EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage()) + .WillRepeatedly(Return(true)); + + PasswordForm form(MakeSimpleForm()); + form.is_gaia_with_skip_save_password_form = true; + std::vector<PasswordForm> observed = {form}; + EXPECT_CALL(*store_, GetLogins(_, _)) + .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms())); + manager()->OnPasswordFormsParsed(&driver_, observed); + manager()->OnPasswordFormsRendered(&driver_, observed, true); + + EXPECT_CALL(*client_.GetStoreResultFilter(), ShouldSave(_)) + .WillRepeatedly(Return(false)); + // The user is typing a credential. No fallback should be available. + PasswordForm typed_credentials(form); + typed_credentials.password_value = ASCIIToUTF16("pw"); + EXPECT_CALL(client_, ShowManualFallbackForSavingPtr(_, _, _)).Times(0); + manager()->ShowManualFallbackForSaving(&driver_, form); + + // The user submits the form. No prompt should pop up. + OnPasswordFormSubmitted(form); + EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0); + observed.clear(); + manager()->DidNavigateMainFrame(); + manager()->OnPasswordFormsRendered(&driver_, observed, true); +} + // Tests that a UKM metric "Login Passed" is sent when the submitted credentials // are already in the store and OnPasswordFormsParsed is called multiple times. TEST_F(PasswordManagerTest,
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc index 63b9410..d120597 100644 --- a/components/password_manager/core/common/password_manager_features.cc +++ b/components/password_manager/core/common/password_manager_features.cc
@@ -23,11 +23,6 @@ const base::Feature kHtmlBasedUsernameDetector = { "HtmlBaseUsernameDetector", base::FEATURE_ENABLED_BY_DEFAULT}; -// Enable additional elements in the form popup UI, which will allow the user to -// view all saved passwords. -const base::Feature kManualFallbacksFilling = { - "ManualFallbacksFilling", base::FEATURE_DISABLED_BY_DEFAULT}; - // Enable a standalone popup UI, which will allow the user to view all saved // passwords. const base::Feature kManualFallbacksFillingStandalone = { @@ -38,10 +33,6 @@ const base::Feature kPasswordForceSaving = { "PasswordForceSaving", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enable the user to trigger password generation manually. -const base::Feature kManualPasswordGeneration = { - "manual-password-generation", base::FEATURE_ENABLED_BY_DEFAULT}; - // Controls the ability to generate passwords that fit sites' requirements. const base::Feature kPasswordGenerationRequirements = { "PasswordGenerationRequirements", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h index 64b5e653..a47982ae 100644 --- a/components/password_manager/core/common/password_manager_features.h +++ b/components/password_manager/core/common/password_manager_features.h
@@ -20,9 +20,7 @@ extern const base::Feature kAffiliationBasedMatching; extern const base::Feature kAutofillHome; extern const base::Feature kHtmlBasedUsernameDetector; -extern const base::Feature kManualFallbacksFilling; extern const base::Feature kManualFallbacksFillingStandalone; -extern const base::Feature kManualPasswordGeneration; extern const base::Feature kPasswordGenerationRequirements; extern const base::Feature kPasswordGenerationRequirementsDomainOverrides; extern const base::Feature kPasswordForceSaving;
diff --git a/components/password_manager/sync/browser/sync_credentials_filter.cc b/components/password_manager/sync/browser/sync_credentials_filter.cc index 3ca8250..9238f54 100644 --- a/components/password_manager/sync/browser/sync_credentials_filter.cc +++ b/components/password_manager/sync/browser/sync_credentials_filter.cc
@@ -77,9 +77,10 @@ bool SyncCredentialsFilter::ShouldSave( const autofill::PasswordForm& form) const { - return !sync_util::IsSyncAccountCredential( - form, sync_service_factory_function_.Run(), - signin_manager_factory_function_.Run()); + return !form.is_gaia_with_skip_save_password_form && + !sync_util::IsSyncAccountCredential( + form, sync_service_factory_function_.Run(), + signin_manager_factory_function_.Run()); } bool SyncCredentialsFilter::ShouldSaveGaiaPasswordHash(
diff --git a/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc b/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc index 1a32af59..b28a3fd68 100644 --- a/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc +++ b/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc
@@ -374,6 +374,14 @@ EXPECT_FALSE(filter_.ShouldSave(form)); } +TEST_F(CredentialsFilterTest, ShouldSave_SignIn_Form) { + PasswordForm form = SimpleGaiaForm("user@example.org"); + form.is_gaia_with_skip_save_password_form = true; + + SetSyncingPasswords(false); + EXPECT_FALSE(filter_.ShouldSave(form)); +} + TEST_F(CredentialsFilterTest, ShouldSave_SyncCredential_NotSyncingPasswords) { PasswordForm form = SimpleGaiaForm("user@example.org");
diff --git a/components/policy/core/common/policy_loader_win.cc b/components/policy/core/common/policy_loader_win.cc index 1e3d2e2a..6cdd7af 100644 --- a/components/policy/core/common/policy_loader_win.cc +++ b/components/policy/core/common/policy_loader_win.cc
@@ -100,7 +100,11 @@ // Encapculates logic to determine if enterprise policies should be honored. // This is used in various places below. bool ShouldHonorPolicies() { - return base::win::IsEnterpriseManaged(); + bool is_enterprise_version = + base::win::OSInfo::GetInstance()->version_type() != base::win::SUITE_HOME; + return base::win::IsEnrolledToDomain() || + (base::win::IsDeviceRegisteredWithManagement() && + is_enterprise_version); } // Verifies that untrusted policies contain only safe values. Modifies the
diff --git a/components/resources/autofill_scaled_resources.grdp b/components/resources/autofill_scaled_resources.grdp index 97db333..ef11660 100644 --- a/components/resources/autofill_scaled_resources.grdp +++ b/components/resources/autofill_scaled_resources.grdp
@@ -32,7 +32,5 @@ </if> <if expr="is_ios"> <structure type="chrome_scaled_image" name="IDR_AUTOFILL_GOOGLE_PAY_WITH_DIVIDER" file="autofill/infobar_autofill_googlepay_with_divider.png" /> - <structure type="chrome_scaled_image" name="IDR_AUTOFILL_TOOLTIP_ICON" file="autofill/autofill_tooltip_icon.png" /> - <structure type="chrome_scaled_image" name="IDR_AUTOFILL_TOOLTIP_ICON_H" file="autofill/autofill_tooltip_icon_hover.png" /> </if> </grit-part>
diff --git a/components/resources/default_100_percent/autofill/autofill_tooltip_icon.png b/components/resources/default_100_percent/autofill/autofill_tooltip_icon.png deleted file mode 100644 index 4434da71..0000000 --- a/components/resources/default_100_percent/autofill/autofill_tooltip_icon.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_100_percent/autofill/autofill_tooltip_icon_hover.png b/components/resources/default_100_percent/autofill/autofill_tooltip_icon_hover.png deleted file mode 100644 index 893b63d..0000000 --- a/components/resources/default_100_percent/autofill/autofill_tooltip_icon_hover.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_200_percent/autofill/autofill_tooltip_icon.png b/components/resources/default_200_percent/autofill/autofill_tooltip_icon.png deleted file mode 100644 index 47322b9..0000000 --- a/components/resources/default_200_percent/autofill/autofill_tooltip_icon.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_200_percent/autofill/autofill_tooltip_icon_hover.png b/components/resources/default_200_percent/autofill/autofill_tooltip_icon_hover.png deleted file mode 100644 index 5d11236..0000000 --- a/components/resources/default_200_percent/autofill/autofill_tooltip_icon_hover.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_300_percent/autofill/autofill_tooltip_icon.png b/components/resources/default_300_percent/autofill/autofill_tooltip_icon.png deleted file mode 100644 index 0c7dd7e..0000000 --- a/components/resources/default_300_percent/autofill/autofill_tooltip_icon.png +++ /dev/null Binary files differ
diff --git a/components/resources/default_300_percent/autofill/autofill_tooltip_icon_hover.png b/components/resources/default_300_percent/autofill/autofill_tooltip_icon_hover.png deleted file mode 100644 index 33bd12f..0000000 --- a/components/resources/default_300_percent/autofill/autofill_tooltip_icon_hover.png +++ /dev/null Binary files differ
diff --git a/components/safe_browsing/browser/safe_browsing_network_context.cc b/components/safe_browsing/browser/safe_browsing_network_context.cc index 799d6cc..1dcc1cd5ea 100644 --- a/components/safe_browsing/browser/safe_browsing_network_context.cc +++ b/components/safe_browsing/browser/safe_browsing_network_context.cc
@@ -166,7 +166,6 @@ base::FilePath cookie_path = user_data_dir_.Append( base::FilePath::StringType(kSafeBrowsingBaseFilename) + kCookiesFile); network_context_params->cookie_path = cookie_path; - network_context_params->enable_encrypted_cookies = false; base::FilePath channel_id_path = user_data_dir_.Append( base::FilePath::StringType(kSafeBrowsingBaseFilename) + kChannelIDFile);
diff --git a/components/safe_search_api/BUILD.gn b/components/safe_search_api/BUILD.gn index 35e8b8e..086b2ea 100644 --- a/components/safe_search_api/BUILD.gn +++ b/components/safe_search_api/BUILD.gn
@@ -26,6 +26,7 @@ deps = [ ":safe_search_api", "//base", + "//base/test:test_support", "//net", "//net/traffic_annotation:test_support", "//services/network:test_support",
diff --git a/components/safe_search_api/url_checker.cc b/components/safe_search_api/url_checker.cc index b48dc5c..161222e 100644 --- a/components/safe_search_api/url_checker.cc +++ b/components/safe_search_api/url_checker.cc
@@ -8,6 +8,7 @@ #include <utility> #include "base/callback.h" +#include "base/feature_list.h" #include "base/json/json_reader.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" @@ -73,6 +74,10 @@ } // namespace +// Consider all URLs within a google domain to be safe. +const base::Feature kAllowAllGoogleUrls{"SafeSearchAllowAllGoogleURLs", + base::FEATURE_DISABLED_BY_DEFAULT}; + struct URLChecker::Check { Check(const GURL& url, std::unique_ptr<network::SimpleURLLoader> simple_url_loader, @@ -127,19 +132,21 @@ URLChecker::~URLChecker() = default; bool URLChecker::CheckURL(const GURL& url, CheckCallback callback) { - // TODO(treib): Hack: For now, allow all Google URLs to save QPS. If we ever - // remove this, we should find a way to allow at least the NTP. - if (google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN, - google_util::ALLOW_NON_STANDARD_PORTS)) { - std::move(callback).Run(url, Classification::SAFE, false); - return true; - } - // TODO(treib): Hack: For now, allow all YouTube URLs since YouTube has its - // own Safety Mode anyway. - if (google_util::IsYoutubeDomainUrl(url, google_util::ALLOW_SUBDOMAIN, - google_util::ALLOW_NON_STANDARD_PORTS)) { - std::move(callback).Run(url, Classification::SAFE, false); - return true; + if (base::FeatureList::IsEnabled(kAllowAllGoogleUrls)) { + // TODO(treib): Hack: For now, allow all Google URLs to save QPS. + if (google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN, + google_util::ALLOW_NON_STANDARD_PORTS)) { + std::move(callback).Run(url, Classification::SAFE, false); + return true; + } + // TODO(treib): Hack: For now, allow all YouTube URLs since YouTube has its + // own Safety Mode anyway. + if (google_util::IsYoutubeDomainUrl( + url, google_util::ALLOW_SUBDOMAIN, + google_util::ALLOW_NON_STANDARD_PORTS)) { + std::move(callback).Run(url, Classification::SAFE, false); + return true; + } } auto cache_it = cache_.Get(url);
diff --git a/components/safe_search_api/url_checker.h b/components/safe_search_api/url_checker.h index db3ec366..6eca2f1 100644 --- a/components/safe_search_api/url_checker.h +++ b/components/safe_search_api/url_checker.h
@@ -22,11 +22,18 @@ class SharedURLLoaderFactory; } // namespace network +namespace base { +struct Feature; +} + namespace safe_search_api { // The SafeSearch API classification of a URL. enum class Classification { SAFE, UNSAFE }; +// Visible for testing. +extern const base::Feature kAllowAllGoogleUrls; + // This class uses the SafeSearch API to check the SafeSearch classification // of the content on a given URL and returns the result asynchronously // via a callback.
diff --git a/components/safe_search_api/url_checker_unittest.cc b/components/safe_search_api/url_checker_unittest.cc index 8b43b63c..ea4e16c 100644 --- a/components/safe_search_api/url_checker_unittest.cc +++ b/components/safe_search_api/url_checker_unittest.cc
@@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/stl_util.h" +#include "base/test/scoped_feature_list.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" #include "net/base/net_errors.h" @@ -59,6 +60,10 @@ return result; } +std::string BuildPornResponse() { + return BuildResponse(true); +} + } // namespace class SafeSearchURLCheckerTest : public testing::Test { @@ -195,4 +200,44 @@ ASSERT_FALSE(SendValidResponse(url, true)); } +TEST_F(SafeSearchURLCheckerTest, AllowAllGoogleURLs) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(kAllowAllGoogleUrls); + { + GURL url("https://sites.google.com/porn"); + EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, _)); + // No server interaction. + bool cache_hit = CheckURL(url); + ASSERT_TRUE(cache_hit); + } + { + GURL url("https://youtube.com/porn"); + EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, _)); + // No server interaction + bool cache_hit = CheckURL(url); + ASSERT_TRUE(cache_hit); + } +} + +TEST_F(SafeSearchURLCheckerTest, NoAllowAllGoogleURLs) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndDisableFeature(kAllowAllGoogleUrls); + { + GURL url("https://sites.google.com/porn"); + EXPECT_CALL(*this, OnCheckDone(url, Classification::UNSAFE, _)); + SetupResponse(url, net::OK, BuildPornResponse()); + bool cache_hit = CheckURL(url); + ASSERT_FALSE(cache_hit); + WaitForResponse(); + } + { + GURL url("https://youtube.com/porn"); + EXPECT_CALL(*this, OnCheckDone(url, Classification::UNSAFE, _)); + SetupResponse(url, net::OK, BuildPornResponse()); + bool cache_hit = CheckURL(url); + ASSERT_FALSE(cache_hit); + WaitForResponse(); + } +} + } // namespace safe_search_api
diff --git a/components/services/font/public/cpp/font_service_thread.cc b/components/services/font/public/cpp/font_service_thread.cc index 38e3640..b4f3aa4 100644 --- a/components/services/font/public/cpp/font_service_thread.cc +++ b/components/services/font/public/cpp/font_service_thread.cc
@@ -22,6 +22,7 @@ : base::Thread(kFontThreadName), font_service_info_(font_service.PassInterface()), weak_factory_(this) { + DETACH_FROM_THREAD(thread_checker_); Start(); } @@ -31,7 +32,7 @@ SkFontConfigInterface::FontIdentity* out_font_identity, SkString* out_family_name, SkFontStyle* out_style) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId()); bool out_valid = false; // This proxies to the other thread, which proxies to mojo. Only on the reply @@ -54,6 +55,7 @@ std::string* out_family_name, bool* out_is_bold, bool* out_is_italic) { + DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId()); bool out_valid = false; base::WaitableEvent done_event; task_runner()->PostTask( @@ -74,6 +76,7 @@ bool is_bold, float device_scale_factor, font_service::mojom::FontRenderStylePtr* out_font_render_style) { + DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId()); bool out_valid = false; base::WaitableEvent done_event; task_runner()->PostTask( @@ -92,6 +95,7 @@ uint32_t charset, uint32_t fallback_family_type, base::File* out_font_file_handle) { + DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId()); base::WaitableEvent done_event; task_runner()->PostTask( FROM_HERE, @@ -103,7 +107,7 @@ scoped_refptr<MappedFontFile> FontServiceThread::OpenStream( const SkFontConfigInterface::FontIdentity& identity) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId()); base::File stream_file; // This proxies to the other thread, which proxies to mojo. Only on the @@ -210,6 +214,7 @@ void FontServiceThread::OnOpenStreamComplete(base::WaitableEvent* done_event, base::File* output_file, base::File file) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); pending_waitable_events_.erase(done_event); *output_file = std::move(file); done_event->Signal();
diff --git a/components/sync/base/model_type.h b/components/sync/base/model_type.h index 27f49e9..91077499 100644 --- a/components/sync/base/model_type.h +++ b/components/sync/base/model_type.h
@@ -224,6 +224,11 @@ return ModelTypeSet::FromRange(FIRST_USER_MODEL_TYPE, LAST_USER_MODEL_TYPE); } +// User types, which are not user-controlled. +constexpr ModelTypeSet AlwaysPreferredUserTypes() { + return ModelTypeSet(DEVICE_INFO, USER_CONSENTS); +} + // These are the user-selectable data types. constexpr ModelTypeSet UserSelectableTypes() { return ModelTypeSet(BOOKMARKS, PREFERENCES, PASSWORDS, AUTOFILL, THEMES,
diff --git a/components/sync/base/sync_prefs.cc b/components/sync/base/sync_prefs.cc index 07d6faa7..d261d9b 100644 --- a/components/sync/base/sync_prefs.cc +++ b/components/sync/base/sync_prefs.cc
@@ -64,16 +64,13 @@ // although they don't have sync representations. user_types.PutAll(ProxyTypes()); - // Treat device info specially. - RegisterDataTypePreferredPref(registry, DEVICE_INFO, true); - user_types.Remove(DEVICE_INFO); - - // All types are set to off by default, which forces a configuration to - // explicitly enable them. GetPreferredTypes() will ensure that any new - // implicit types are enabled when their pref group is, or via - // KeepEverythingSynced. + // All types except the always-preferred ones are set to off by default, which + // forces a configuration to explicitly enable them. GetPreferredTypes() will + // ensure that any new implicit types are enabled when their pref group is, or + // via KeepEverythingSynced. for (ModelTypeSet::Iterator it = user_types.First(); it.Good(); it.Inc()) { - RegisterDataTypePreferredPref(registry, it.Get(), false); + RegisterDataTypePreferredPref(registry, it.Get(), + AlwaysPreferredUserTypes().Has(it.Get())); } registry->RegisterBooleanPref(prefs::kSyncManaged, false); @@ -436,23 +433,16 @@ ModelType type, bool is_preferred) { const char* pref_name = GetPrefNameForDataType(type); - if (!pref_name) { - NOTREACHED(); - return; - } + DCHECK(pref_name); registry->RegisterBooleanPref(pref_name, is_preferred); } bool SyncPrefs::GetDataTypePreferred(ModelType type) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); const char* pref_name = GetPrefNameForDataType(type); - if (!pref_name) { - NOTREACHED(); - return false; - } + DCHECK(pref_name); - // Device info is always enabled. - if (pref_name == prefs::kSyncDeviceInfo) + if (AlwaysPreferredUserTypes().Has(type)) return true; if (type == PROXY_TABS && @@ -469,13 +459,9 @@ void SyncPrefs::SetDataTypePreferred(ModelType type, bool is_preferred) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); const char* pref_name = GetPrefNameForDataType(type); - if (!pref_name) { - NOTREACHED(); - return; - } + DCHECK(pref_name); - // Device info is always preferred. - if (type == DEVICE_INFO) + if (AlwaysPreferredUserTypes().Has(type)) return; pref_service_->SetBoolean(pref_name, is_preferred);
diff --git a/components/sync/base/sync_prefs_unittest.cc b/components/sync/base/sync_prefs_unittest.cc index 08bc2a2e..af32ae4 100644 --- a/components/sync/base/sync_prefs_unittest.cc +++ b/components/sync/base/sync_prefs_unittest.cc
@@ -66,10 +66,8 @@ SyncPrefs sync_prefs(&pref_service_); sync_prefs.SetKeepEverythingSynced(false); - // Only device info is enabled by default. - ModelTypeSet expected(DEVICE_INFO); ModelTypeSet preferred_types = sync_prefs.GetPreferredDataTypes(UserTypes()); - EXPECT_EQ(expected, preferred_types); + EXPECT_EQ(AlwaysPreferredUserTypes(), preferred_types); // Simulate an upgrade to delete directives + proxy tabs support. None of the // new types or their pref group types should be registering, ensuring they @@ -165,8 +163,7 @@ expected_preferred_types.Put(FAVICON_TRACKING); } - // Device info is always preferred. - expected_preferred_types.Put(DEVICE_INFO); + expected_preferred_types.PutAll(AlwaysPreferredUserTypes()); sync_prefs.SetPreferredDataTypes(user_types, preferred_types); EXPECT_EQ(expected_preferred_types, @@ -229,6 +226,24 @@ EXPECT_TRUE(sync_prefs.GetPreferredDataTypes(UserTypes()).Has(DEVICE_INFO)); sync_prefs.SetKeepEverythingSynced(false); EXPECT_TRUE(sync_prefs.GetPreferredDataTypes(UserTypes()).Has(DEVICE_INFO)); + sync_prefs.SetPreferredDataTypes( + /*registered_types=*/ModelTypeSet(DEVICE_INFO), + /*preferred_types=*/ModelTypeSet()); + EXPECT_TRUE(sync_prefs.GetPreferredDataTypes(UserTypes()).Has(DEVICE_INFO)); +} + +// User Consents should always be enabled. +TEST_F(SyncPrefsTest, UserConsents) { + SyncPrefs sync_prefs(&pref_service_); + EXPECT_TRUE(sync_prefs.GetPreferredDataTypes(UserTypes()).Has(USER_CONSENTS)); + sync_prefs.SetKeepEverythingSynced(true); + EXPECT_TRUE(sync_prefs.GetPreferredDataTypes(UserTypes()).Has(USER_CONSENTS)); + sync_prefs.SetKeepEverythingSynced(false); + EXPECT_TRUE(sync_prefs.GetPreferredDataTypes(UserTypes()).Has(USER_CONSENTS)); + sync_prefs.SetPreferredDataTypes( + /*registered_types=*/ModelTypeSet(USER_CONSENTS), + /*preferred_types=*/ModelTypeSet()); + EXPECT_TRUE(sync_prefs.GetPreferredDataTypes(UserTypes()).Has(USER_CONSENTS)); } // Verify that invalidation versions are persisted and loaded correctly.
diff --git a/components/sync/driver/data_type_controller.h b/components/sync/driver/data_type_controller.h index 7d127de..30121b6 100644 --- a/components/sync/driver/data_type_controller.h +++ b/components/sync/driver/data_type_controller.h
@@ -67,6 +67,8 @@ using ModelLoadCallback = base::Callback<void(ModelType, const SyncError&)>; + using StopCallback = base::OnceClosure; + using AllNodesCallback = base::Callback<void(const ModelType, std::unique_ptr<base::ListValue>)>; @@ -127,9 +129,12 @@ // See comments for ModelAssociationManager::OnSingleDataTypeWillStop. virtual void DeactivateDataType(ModelTypeConfigurer* configurer) = 0; - // Synchronously stops the data type. If StartAssociating has already been - // called but is not done yet it will be aborted. Similarly if LoadModels - // has not completed it will also be aborted. + // Stops the data type. If StartAssociating has already been called but is not + // done yet it will be aborted. Similarly if LoadModels has not completed it + // will also be aborted. Implementations may enter STOPPING state + // transitionaly but should eventually become STOPPED. At this point, + // |callback| will be run. |callback| must not be null. + // // NOTE: Stop() should be called after sync backend machinery has stopped // routing changes to this data type. Stop() should ensure the data type // logic shuts down gracefully by flushing remaining changes and calling @@ -137,7 +142,8 @@ // propagate from sync again from point where Stop() is called. // KEEP_METADATA is used when the engine just stops sync, and CLEAR_METADATA // is used when the user disables sync for data type. - virtual void Stop(SyncStopMetadataFate metadata_fate) = 0; + virtual void Stop(SyncStopMetadataFate metadata_fate, + StopCallback callback) = 0; // Name of this data type. For logging purposes only. std::string name() const { return ModelTypeToString(type()); }
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc index e36f7f3..180f77b 100644 --- a/components/sync/driver/data_type_manager_impl.cc +++ b/components/sync/driver/data_type_manager_impl.cc
@@ -804,6 +804,11 @@ // callback will do nothing because state is set to STOPPING above. model_association_manager_.Stop(metadata_fate); + // Individual data type controllers might still be STOPPING, but we don't + // reflect that in |state_| because, for all practical matters, the manager is + // in a ready state and reconfguration can be triggered. + // TODO(mastiz): Reconsider waiting in STOPPING state until all datatypes have + // stopped. state_ = STOPPED; }
diff --git a/components/sync/driver/directory_data_type_controller.cc b/components/sync/driver/directory_data_type_controller.cc index feaadf2..03d0c7e 100644 --- a/components/sync/driver/directory_data_type_controller.cc +++ b/components/sync/driver/directory_data_type_controller.cc
@@ -60,6 +60,13 @@ configurer->UnregisterDirectoryDataType(type()); } +void DirectoryDataTypeController::Stop(SyncStopMetadataFate metadata_fate, + StopCallback callback) { + DCHECK(CalledOnValidThread()); + Stop(metadata_fate); + std::move(callback).Run(); +} + void DirectoryDataTypeController::GetAllNodes( const AllNodesCallback& callback) { std::unique_ptr<base::ListValue> node_list = GetAllNodesForTypeFromDirectory(
diff --git a/components/sync/driver/directory_data_type_controller.h b/components/sync/driver/directory_data_type_controller.h index 7c3f523..a2873ea 100644 --- a/components/sync/driver/directory_data_type_controller.h +++ b/components/sync/driver/directory_data_type_controller.h
@@ -46,11 +46,15 @@ // the data type's ChangeProcessor registration with the backend). // See ModelTypeConfigurer::DeactivateDataType for more details. void DeactivateDataType(ModelTypeConfigurer* configurer) override; - + void Stop(SyncStopMetadataFate metadata_fate, StopCallback callback) final; void GetAllNodes(const AllNodesCallback& callback) override; void GetStatusCounters(const StatusCountersCallback& callback) override; void RecordMemoryUsageHistogram() override; + // Convenience overload with synchronous API, since directory types are always + // capable of stopping immediately. + virtual void Stop(SyncStopMetadataFate metadata_fate) = 0; + // Returns a ListValue representing all nodes for a specified type by querying // the directory. static std::unique_ptr<base::ListValue> GetAllNodesForTypeFromDirectory(
diff --git a/components/sync/driver/model_association_manager.cc b/components/sync/driver/model_association_manager.cc index 94386c5d..35628bb 100644 --- a/components/sync/driver/model_association_manager.cc +++ b/components/sync/driver/model_association_manager.cc
@@ -9,7 +9,9 @@ #include <algorithm> #include <functional> +#include <utility> +#include "base/barrier_closure.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" @@ -126,38 +128,53 @@ state_ = INITIALIZED; notified_about_ready_for_configure_ = false; - StopDisabledTypes(preferred_types); - LoadEnabledTypes(); + DVLOG(1) << "ModelAssociationManager: Stopping disabled types."; + std::map<DataTypeController*, SyncStopMetadataFate> types_to_stop; + for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); + it != controllers_->end(); ++it) { + DataTypeController* dtc = (*it).second.get(); + // We stop a datatype if it's not desired. Independently of being desired, + // if the datatype is already STOPPING, we also wait for it to stop, to make + // sure it's ready to start again (if appropriate). + if ((dtc->state() != DataTypeController::NOT_RUNNING && + !desired_types_.Has(dtc->type())) || + dtc->state() == DataTypeController::STOPPING) { + const SyncStopMetadataFate metadata_fate = + preferred_types.Has(dtc->type()) ? KEEP_METADATA : CLEAR_METADATA; + types_to_stop[dtc] = metadata_fate; + } + } + + // Run LoadEnabledTypes() only after all relevant types are stopped. + // TODO(mastiz): Add test coverage to this waiting logic, including the + // case where the datatype is STOPPING when this function is called. + base::RepeatingClosure barrier_closure = base::BarrierClosure( + types_to_stop.size(), + base::BindOnce(&ModelAssociationManager::LoadEnabledTypes, + weak_ptr_factory_.GetWeakPtr())); + + for (const auto& dtc_and_metadata_fate : types_to_stop) { + DataTypeController* dtc = dtc_and_metadata_fate.first; + const SyncStopMetadataFate metadata_fate = dtc_and_metadata_fate.second; + DVLOG(1) << "ModelAssociationManager: stop " << dtc->name() << " with " + << SyncStopMetadataFateToString(metadata_fate); + StopDatatype(SyncError(), metadata_fate, dtc, barrier_closure); + } } -void ModelAssociationManager::StopDatatype(const SyncError& error, - SyncStopMetadataFate metadata_fate, - DataTypeController* dtc) { +void ModelAssociationManager::StopDatatype( + const SyncError& error, + SyncStopMetadataFate metadata_fate, + DataTypeController* dtc, + DataTypeController::StopCallback callback) { loaded_types_.Remove(dtc->type()); associated_types_.Remove(dtc->type()); associating_types_.Remove(dtc->type()); - if (error.IsSet() || dtc->state() != DataTypeController::NOT_RUNNING) { - // If an error was set, the delegate must be informed of the error. - delegate_->OnSingleDataTypeWillStop(dtc->type(), error); - dtc->Stop(metadata_fate); - } -} + DCHECK(error.IsSet() || (dtc->state() != DataTypeController::NOT_RUNNING)); -void ModelAssociationManager::StopDisabledTypes(ModelTypeSet preferred_types) { - DVLOG(1) << "ModelAssociationManager: Stopping disabled types."; - for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); - it != controllers_->end(); ++it) { - DataTypeController* dtc = (*it).second.get(); - if (dtc->state() != DataTypeController::NOT_RUNNING && - !desired_types_.Has(dtc->type())) { - const SyncStopMetadataFate metadata_fate = - preferred_types.Has(dtc->type()) ? KEEP_METADATA : CLEAR_METADATA; - DVLOG(1) << "ModelAssociationManager: stop " << dtc->name() << " with " - << SyncStopMetadataFateToString(metadata_fate); - StopDatatype(SyncError(), metadata_fate, dtc); - } - } + delegate_->OnSingleDataTypeWillStop(dtc->type(), error); + dtc->Stop(metadata_fate, std::move(callback)); } void ModelAssociationManager::LoadEnabledTypes() { @@ -248,8 +265,11 @@ for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); it != controllers_->end(); ++it) { DataTypeController* dtc = (*it).second.get(); - if (dtc->state() != DataTypeController::NOT_RUNNING) { - StopDatatype(SyncError(), metadata_fate, dtc); + if (dtc->state() != DataTypeController::NOT_RUNNING && + dtc->state() != DataTypeController::STOPPING) { + // We don't really wait until all datatypes have been fully stopped, which + // is only required (and in fact waited for) when Initialize() is called. + StopDatatype(SyncError(), metadata_fate, dtc, base::DoNothing()); DVLOG(1) << "ModelAssociationManager: Stopped " << dtc->name(); } } @@ -319,7 +339,8 @@ DVLOG(1) << "ModelAssociationManager: Type encountered an error."; desired_types_.Remove(type); DataTypeController* dtc = controllers_->find(type)->second.get(); - StopDatatype(local_merge_result.error(), KEEP_METADATA, dtc); + StopDatatype(local_merge_result.error(), KEEP_METADATA, dtc, + base::DoNothing()); NotifyDelegateIfReadyForConfigure(); // Update configuration result. @@ -386,14 +407,15 @@ it != controllers_->end(); ++it) { DataTypeController* dtc = (*it).second.get(); if (associating_types_.Has(dtc->type()) && - dtc->state() != DataTypeController::NOT_RUNNING) { + dtc->state() != DataTypeController::NOT_RUNNING && + dtc->state() != DataTypeController::STOPPING) { // TODO(wychen): enum uma should be strongly typed. crbug.com/661401 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed", ModelTypeToHistogramInt(dtc->type()), static_cast<int>(MODEL_TYPE_COUNT)); StopDatatype(SyncError(FROM_HERE, SyncError::DATATYPE_ERROR, "Association timed out.", dtc->type()), - KEEP_METADATA, dtc); + KEEP_METADATA, dtc, base::DoNothing()); } }
diff --git a/components/sync/driver/model_association_manager.h b/components/sync/driver/model_association_manager.h index 34d04ca..96e43f1 100644 --- a/components/sync/driver/model_association_manager.h +++ b/components/sync/driver/model_association_manager.h
@@ -111,10 +111,6 @@ // round of association. void ResetForNextAssociation(); - // Called by Initialize() to stop types that are not in |desired_types_|. - // For types that not in |preferred_types| also clears sync metadata. - void StopDisabledTypes(ModelTypeSet preferred_types); - // Start loading non-running types that are in |desired_types_|. void LoadEnabledTypes(); @@ -138,7 +134,8 @@ // A helper to stop an individual datatype. void StopDatatype(const SyncError& error, SyncStopMetadataFate metadata_fate, - DataTypeController* dtc); + DataTypeController* dtc, + DataTypeController::StopCallback callback); // Calls delegate's OnAllDataTypesReadyForConfigure when all datatypes from // desired_types_ are ready for configure. Ensures that for every call to
diff --git a/components/sync/driver/model_type_controller.cc b/components/sync/driver/model_type_controller.cc index d618830..d47c32e 100644 --- a/components/sync/driver/model_type_controller.cc +++ b/components/sync/driver/model_type_controller.cc
@@ -85,6 +85,19 @@ std::move(task).Run(delegate); } +// Takes the strictest policy for clearing sync metadata. +SyncStopMetadataFate TakeStrictestMetadataFate(SyncStopMetadataFate fate1, + SyncStopMetadataFate fate2) { + switch (fate1) { + case CLEAR_METADATA: + return CLEAR_METADATA; + case KEEP_METADATA: + return fate2; + } + NOTREACHED(); + return KEEP_METADATA; +} + } // namespace ModelTypeController::ModelTypeController( @@ -146,15 +159,27 @@ void ModelTypeController::LoadModelsDone(ConfigureResult result, const SyncError& error) { DCHECK(CalledOnValidThread()); + DCHECK_NE(NOT_RUNNING, state_); - if (state_ == NOT_RUNNING) { - // The callback arrived on the UI thread after the type has been already - // stopped. + if (state_ == STOPPING) { + DCHECK(!model_stop_callbacks_.empty()); + // This reply to OnSyncStarting() has arrived after the type has been + // requested to stop. DVLOG(1) << "Sync start completion received late for " << ModelTypeToString(type()) << ", it has been stopped meanwhile"; - // TODO(mastiz): Call to Stop() here, but think through if that's enough, - // because perhaps the datatype was reenabled. RecordStartFailure(ABORTED); + PostModelTask(FROM_HERE, base::BindOnce(&StopSyncHelperOnModelThread, + model_stop_metadata_fate_)); + state_ = NOT_RUNNING; + + // We make a copy in case running the callbacks has side effects and + // modifies the vector, although we don't expect that in practice. + std::vector<StopCallback> model_stop_callbacks = + std::move(model_stop_callbacks_); + DCHECK(model_stop_callbacks_.empty()); + for (StopCallback& stop_callback : model_stop_callbacks) { + std::move(stop_callback).Run(); + } return; } @@ -234,13 +259,13 @@ } } -void ModelTypeController::Stop(SyncStopMetadataFate metadata_fate) { +void ModelTypeController::Stop(SyncStopMetadataFate metadata_fate, + StopCallback callback) { DCHECK(CalledOnValidThread()); switch (state()) { case ASSOCIATING: - case STOPPING: - // We don't really use these states in this class. + // We don't really use this state in this class. NOTREACHED(); break; @@ -251,9 +276,23 @@ // realistic scenario (disable sync during shutdown?). return; + case STOPPING: + DCHECK(!model_stop_callbacks_.empty()); + model_stop_metadata_fate_ = + TakeStrictestMetadataFate(model_stop_metadata_fate_, metadata_fate); + model_stop_callbacks_.push_back(std::move(callback)); + break; + case MODEL_STARTING: - DLOG(WARNING) << "Shortcutting stop for " << ModelTypeToString(type()) + DCHECK(!model_load_callback_.is_null()); + DCHECK(model_stop_callbacks_.empty()); + DLOG(WARNING) << "Deferring stop for " << ModelTypeToString(type()) << " because it's still starting"; + model_stop_metadata_fate_ = metadata_fate; + model_stop_callbacks_.push_back(std::move(callback)); + // The actual stop will be executed in LoadModelsDone(), when the starting + // process is finished. + state_ = STOPPING; break; case MODEL_LOADED: @@ -261,10 +300,10 @@ DVLOG(1) << "Stopping sync for " << ModelTypeToString(type()); PostModelTask(FROM_HERE, base::BindOnce(&StopSyncHelperOnModelThread, metadata_fate)); + state_ = NOT_RUNNING; + std::move(callback).Run(); break; } - - state_ = NOT_RUNNING; } DataTypeController::State ModelTypeController::state() const {
diff --git a/components/sync/driver/model_type_controller.h b/components/sync/driver/model_type_controller.h index 6a67ed6..f96bf88 100644 --- a/components/sync/driver/model_type_controller.h +++ b/components/sync/driver/model_type_controller.h
@@ -7,6 +7,7 @@ #include <memory> #include <string> +#include <vector> #include "base/callback.h" #include "base/macros.h" @@ -46,7 +47,7 @@ void StartAssociating(const StartCallback& start_callback) override; void ActivateDataType(ModelTypeConfigurer* configurer) override; void DeactivateDataType(ModelTypeConfigurer* configurer) override; - void Stop(SyncStopMetadataFate metadata_fate) override; + void Stop(SyncStopMetadataFate metadata_fate, StopCallback callback) override; State state() const override; void GetAllNodes(const AllNodesCallback& callback) override; void GetStatusCounters(const StatusCountersCallback& callback) override; @@ -89,9 +90,19 @@ // State of this datatype controller. State state_; - // Callbacks for use when starting the datatype. + // Callback for use when starting the datatype (usually MODEL_STARTING, but + // STOPPING if abort requested while starting). ModelLoadCallback model_load_callback_; + // Callbacks for use when stopping the datatype (STOPPING), which also + // includes aborting a start. This is important because STOPPING is a state + // used to make sure we don't request two starts in parallel to the delegate, + // which is hard to support (most notably in ClientTagBasedProcessor). We + // use a vector because it's allowed to call Stop() multiple times (i.e. while + // STOPPING). + std::vector<StopCallback> model_stop_callbacks_; + SyncStopMetadataFate model_stop_metadata_fate_; + // Controller receives |activation_response_| from // ClientTagBasedModelTypeProcessor callback and must temporarily own it until // ActivateDataType is called.
diff --git a/components/sync/driver/model_type_controller_unittest.cc b/components/sync/driver/model_type_controller_unittest.cc index 28a944c..61f5be92 100644 --- a/components/sync/driver/model_type_controller_unittest.cc +++ b/components/sync/driver/model_type_controller_unittest.cc
@@ -228,7 +228,7 @@ void DeactivateDataTypeAndStop(SyncStopMetadataFate metadata_fate) { controller_->DeactivateDataType(&configurer_); - controller_->Stop(metadata_fate); + controller_->Stop(metadata_fate, base::DoNothing()); } // These threads can ping-pong for a bit so we run the model thread twice. @@ -378,7 +378,7 @@ TEST_F(ModelTypeControllerTest, StopBeforeLoadModels) { EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state()); - controller()->Stop(CLEAR_METADATA); + controller()->Stop(CLEAR_METADATA, base::DoNothing()); EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state()); // Ensure that DisableSync is not called.
diff --git a/components/sync/driver/proxy_data_type_controller.cc b/components/sync/driver/proxy_data_type_controller.cc index 98d1dc9d..138e603a 100644 --- a/components/sync/driver/proxy_data_type_controller.cc +++ b/components/sync/driver/proxy_data_type_controller.cc
@@ -4,6 +4,8 @@ #include "components/sync/driver/proxy_data_type_controller.h" +#include <utility> + #include "base/values.h" #include "components/sync/engine/model_safe_worker.h" #include "components/sync/engine/model_type_configurer.h" @@ -53,8 +55,10 @@ syncer_merge_result); } -void ProxyDataTypeController::Stop(SyncStopMetadataFate metadata_fate) { +void ProxyDataTypeController::Stop(SyncStopMetadataFate metadata_fate, + StopCallback callback) { state_ = NOT_RUNNING; + std::move(callback).Run(); } DataTypeController::State ProxyDataTypeController::state() const {
diff --git a/components/sync/driver/proxy_data_type_controller.h b/components/sync/driver/proxy_data_type_controller.h index e7add5eb..3f70df6 100644 --- a/components/sync/driver/proxy_data_type_controller.h +++ b/components/sync/driver/proxy_data_type_controller.h
@@ -28,7 +28,7 @@ void RegisterWithBackend(base::Callback<void(bool)> set_downloaded, ModelTypeConfigurer* configurer) override; void StartAssociating(const StartCallback& start_callback) override; - void Stop(SyncStopMetadataFate metadata_fate) override; + void Stop(SyncStopMetadataFate metadata_fate, StopCallback callback) override; State state() const override; void ActivateDataType(ModelTypeConfigurer* configurer) override; void DeactivateDataType(ModelTypeConfigurer* configurer) override;
diff --git a/components/sync_bookmarks/BUILD.gn b/components/sync_bookmarks/BUILD.gn index 22b3775..8e3935dc 100644 --- a/components/sync_bookmarks/BUILD.gn +++ b/components/sync_bookmarks/BUILD.gn
@@ -10,12 +10,16 @@ "bookmark_change_processor.h", "bookmark_data_type_controller.cc", "bookmark_data_type_controller.h", + "bookmark_local_changes_builder.cc", + "bookmark_local_changes_builder.h", "bookmark_model_associator.cc", "bookmark_model_associator.h", "bookmark_model_observer_impl.cc", "bookmark_model_observer_impl.h", "bookmark_model_type_processor.cc", "bookmark_model_type_processor.h", + "bookmark_remote_updates_handler.cc", + "bookmark_remote_updates_handler.h", "bookmark_sync_service.cc", "bookmark_sync_service.h", "synced_bookmark_tracker.cc", @@ -41,6 +45,7 @@ "bookmark_data_type_controller_unittest.cc", "bookmark_model_observer_impl_unittest.cc", "bookmark_model_type_processor_unittest.cc", + "bookmark_remote_updates_handler_unittest.cc", "synced_bookmark_tracker_unittest.cc", ]
diff --git a/components/sync_bookmarks/bookmark_local_changes_builder.cc b/components/sync_bookmarks/bookmark_local_changes_builder.cc new file mode 100644 index 0000000..5800eb4 --- /dev/null +++ b/components/sync_bookmarks/bookmark_local_changes_builder.cc
@@ -0,0 +1,106 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/sync_bookmarks/bookmark_local_changes_builder.h" + +#include <string> +#include <utility> + +#include "base/strings/utf_string_conversions.h" +#include "components/bookmarks/browser/bookmark_node.h" +#include "components/sync/base/time.h" +#include "components/sync/protocol/bookmark_model_metadata.pb.h" +#include "components/sync_bookmarks/synced_bookmark_tracker.h" + +namespace sync_bookmarks { + +namespace { + +sync_pb::EntitySpecifics SpecificsFromBookmarkNode( + const bookmarks::BookmarkNode* node) { + sync_pb::EntitySpecifics specifics; + sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark(); + bm_specifics->set_url(node->url().spec()); + // TODO(crbug.com/516866): Set the favicon. + bm_specifics->set_title(base::UTF16ToUTF8(node->GetTitle())); + bm_specifics->set_creation_time_us( + node->date_added().ToDeltaSinceWindowsEpoch().InMicroseconds()); + + bm_specifics->set_icon_url(node->icon_url() ? node->icon_url()->spec() + : std::string()); + + for (const std::pair<std::string, std::string>& pair : + *node->GetMetaInfoMap()) { + sync_pb::MetaInfo* meta_info = bm_specifics->add_meta_info(); + meta_info->set_key(pair.first); + meta_info->set_value(pair.second); + } + return specifics; +} + +} // namespace + +BookmarkLocalChangesBuilder::BookmarkLocalChangesBuilder( + const SyncedBookmarkTracker* const bookmark_tracker) + : bookmark_tracker_(bookmark_tracker) { + DCHECK(bookmark_tracker); +} + +std::vector<syncer::CommitRequestData> +BookmarkLocalChangesBuilder::BuildCommitRequests(size_t max_entries) const { + DCHECK(bookmark_tracker_); + const std::vector<const SyncedBookmarkTracker::Entity*> + entities_with_local_changes = + bookmark_tracker_->GetEntitiesWithLocalChanges(max_entries); + DCHECK_LE(entities_with_local_changes.size(), max_entries); + + std::vector<syncer::CommitRequestData> commit_requests; + for (const SyncedBookmarkTracker::Entity* entity : + entities_with_local_changes) { + DCHECK(entity->IsUnsynced()); + const sync_pb::EntityMetadata* metadata = entity->metadata(); + + syncer::CommitRequestData request; + syncer::EntityData data; + data.id = metadata->server_id(); + data.creation_time = syncer::ProtoTimeToTime(metadata->creation_time()); + data.modification_time = + syncer::ProtoTimeToTime(metadata->modification_time()); + if (!metadata->is_deleted()) { + const bookmarks::BookmarkNode* node = entity->bookmark_node(); + DCHECK(node); + const bookmarks::BookmarkNode* parent = node->parent(); + const SyncedBookmarkTracker::Entity* parent_entity = + bookmark_tracker_->GetEntityForBookmarkNode(parent); + DCHECK(parent_entity); + data.parent_id = parent_entity->metadata()->server_id(); + // TODO(crbug.com/516866): Double check that custom passphrase works well + // with this implementation, because: + // 1. NonBlockingTypeCommitContribution::AdjustCommitProto() clears the + // title out. + // 2. Bookmarks (maybe ancient legacy bookmarks only?) use/used |name| to + // encode the title. + data.is_folder = node->is_folder(); + // TODO(crbug.com/516866): Set the non_unique_name similar to directory + // implementation. + // https://cs.chromium.org/chromium/src/components/sync/syncable/write_node.cc?l=41&rcl=1675007db1e0eb03417e81442688bb11cd181f58 + data.non_unique_name = base::UTF16ToUTF8(node->GetTitle()); + data.unique_position = parent_entity->metadata()->unique_position(); + // In case of deletion, make an EntityData with empty specifics to + // indicate deletion. + data.specifics = SpecificsFromBookmarkNode(node); + } + request.entity = data.PassToPtr(); + request.sequence_number = metadata->sequence_number(); + request.base_version = metadata->server_version(); + // Specifics hash has been computed in the tracker when this entity has been + // added/updated. + request.specifics_hash = metadata->specifics_hash(); + + commit_requests.push_back(std::move(request)); + } + return commit_requests; +} + +} // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_local_changes_builder.h b/components/sync_bookmarks/bookmark_local_changes_builder.h new file mode 100644 index 0000000..0a5fd06 --- /dev/null +++ b/components/sync_bookmarks/bookmark_local_changes_builder.h
@@ -0,0 +1,33 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_LOCAL_CHANGES_BUILDER_H_ +#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_LOCAL_CHANGES_BUILDER_H_ + +#include <vector> + +#include "components/sync/engine/non_blocking_sync_common.h" + +namespace sync_bookmarks { + +class SyncedBookmarkTracker; + +class BookmarkLocalChangesBuilder { + public: + // |bookmark_tracker| must not be null and must outlive this object. + explicit BookmarkLocalChangesBuilder( + const SyncedBookmarkTracker* bookmark_tracker); + // Builds the commit requests list. + std::vector<syncer::CommitRequestData> BuildCommitRequests( + size_t max_entries) const; + + private: + const SyncedBookmarkTracker* const bookmark_tracker_; + + DISALLOW_COPY_AND_ASSIGN(BookmarkLocalChangesBuilder); +}; + +} // namespace sync_bookmarks + +#endif // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_LOCAL_CHANGES_BUILDER_H_
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.cc b/components/sync_bookmarks/bookmark_model_type_processor.cc index a3e4578..c685b55 100644 --- a/components/sync_bookmarks/bookmark_model_type_processor.cc +++ b/components/sync_bookmarks/bookmark_model_type_processor.cc
@@ -7,138 +7,24 @@ #include <utility> #include "base/callback.h" -#include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "components/bookmarks/browser/bookmark_model.h" #include "components/bookmarks/browser/bookmark_node.h" #include "components/bookmarks/browser/bookmark_utils.h" #include "components/sync/base/model_type.h" -#include "components/sync/base/time.h" #include "components/sync/engine/commit_queue.h" #include "components/sync/engine/model_type_processor_proxy.h" #include "components/sync/model/data_type_activation_request.h" #include "components/sync/protocol/bookmark_model_metadata.pb.h" +#include "components/sync_bookmarks/bookmark_local_changes_builder.h" #include "components/sync_bookmarks/bookmark_model_observer_impl.h" +#include "components/sync_bookmarks/bookmark_remote_updates_handler.h" #include "components/undo/bookmark_undo_utils.h" namespace sync_bookmarks { namespace { -// The sync protocol identifies top-level entities by means of well-known tags, -// (aka server defined tags) which should not be confused with titles or client -// tags that aren't supported by bookmarks (at the time of writing). Each tag -// corresponds to a singleton instance of a particular top-level node in a -// user's share; the tags are consistent across users. The tags allow us to -// locate the specific folders whose contents we care about synchronizing, -// without having to do a lookup by name or path. The tags should not be made -// user-visible. For example, the tag "bookmark_bar" represents the permanent -// node for bookmarks bar in Chrome. The tag "other_bookmarks" represents the -// permanent folder Other Bookmarks in Chrome. -// -// It is the responsibility of something upstream (at time of writing, the sync -// server) to create these tagged nodes when initializing sync for the first -// time for a user. Thus, once the backend finishes initializing, the -// ProfileSyncService can rely on the presence of tagged nodes. -const char kBookmarkBarTag[] = "bookmark_bar"; -const char kMobileBookmarksTag[] = "synced_bookmarks"; -const char kOtherBookmarksTag[] = "other_bookmarks"; - -// Id is created by concatenating the specifics field number and the server tag -// similar to LookbackServerEntity::CreateId() that uses -// GetSpecificsFieldNumberFromModelType() to compute the field number. -static const char kBookmarksRootId[] = "32904_google_chrome_bookmarks"; - -// |sync_entity| must contain a bookmark specifics. -// Metainfo entries must have unique keys. -bookmarks::BookmarkNode::MetaInfoMap GetBookmarkMetaInfo( - const syncer::EntityData& sync_entity) { - const sync_pb::BookmarkSpecifics& specifics = - sync_entity.specifics.bookmark(); - bookmarks::BookmarkNode::MetaInfoMap meta_info_map; - for (const sync_pb::MetaInfo& meta_info : specifics.meta_info()) { - meta_info_map[meta_info.key()] = meta_info.value(); - } - DCHECK_EQ(static_cast<size_t>(specifics.meta_info_size()), - meta_info_map.size()); - return meta_info_map; -} - -// Creates a bookmark node under the given parent node from the given sync node. -// Returns the newly created node. |sync_entity| must contain a bookmark -// specifics with Metainfo entries having unique keys. -const bookmarks::BookmarkNode* CreateBookmarkNode( - const syncer::EntityData& sync_entity, - const bookmarks::BookmarkNode* parent, - bookmarks::BookmarkModel* model, - int index) { - DCHECK(parent); - DCHECK(model); - - const sync_pb::BookmarkSpecifics& specifics = - sync_entity.specifics.bookmark(); - bookmarks::BookmarkNode::MetaInfoMap metainfo = - GetBookmarkMetaInfo(sync_entity); - if (sync_entity.is_folder) { - return model->AddFolderWithMetaInfo( - parent, index, base::UTF8ToUTF16(specifics.title()), &metainfo); - } - // 'creation_time_us' was added in M24. Assume a time of 0 means now. - const int64_t create_time_us = specifics.creation_time_us(); - base::Time create_time = - (create_time_us == 0) - ? base::Time::Now() - : base::Time::FromDeltaSinceWindowsEpoch( - // Use FromDeltaSinceWindowsEpoch because create_time_us has - // always used the Windows epoch. - base::TimeDelta::FromMicroseconds(create_time_us)); - return model->AddURLWithCreationTimeAndMetaInfo( - parent, index, base::UTF8ToUTF16(specifics.title()), - GURL(specifics.url()), create_time, &metainfo); - // TODO(crbug.com/516866): Add the favicon related code. -} - -// Check whether an incoming specifics represent a valid bookmark or not. -// |is_folder| is whether this specifics is for a folder or not. -// Folders and tomstones entail different validation conditions. -bool IsValidBookmark(const sync_pb::BookmarkSpecifics& specifics, - bool is_folder) { - if (specifics.ByteSize() == 0) { - DLOG(ERROR) << "Invalid bookmark: empty specifics."; - return false; - } - if (is_folder) { - return true; - } - if (!GURL(specifics.url()).is_valid()) { - DLOG(ERROR) << "Invalid bookmark: invalid url in the specifics."; - return false; - } - return true; -} - -sync_pb::EntitySpecifics SpecificsFromBookmarkNode( - const bookmarks::BookmarkNode* node) { - sync_pb::EntitySpecifics specifics; - sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark(); - bm_specifics->set_url(node->url().spec()); - // TODO(crbug.com/516866): Set the favicon. - bm_specifics->set_title(base::UTF16ToUTF8(node->GetTitle())); - bm_specifics->set_creation_time_us( - node->date_added().ToDeltaSinceWindowsEpoch().InMicroseconds()); - - bm_specifics->set_icon_url(node->icon_url() ? node->icon_url()->spec() - : std::string()); - - for (const std::pair<std::string, std::string>& pair : - *node->GetMetaInfoMap()) { - sync_pb::MetaInfo* meta_info = bm_specifics->add_meta_info(); - meta_info->set_key(pair.first); - meta_info->set_value(pair.second); - } - return specifics; -} - class ScopedRemoteUpdateBookmarks { public: // |bookmark_model|, |bookmark_undo_service| and |observer| must not be null @@ -178,6 +64,7 @@ DISALLOW_COPY_AND_ASSIGN(ScopedRemoteUpdateBookmarks); }; + } // namespace BookmarkModelTypeProcessor::BookmarkModelTypeProcessor( @@ -213,8 +100,9 @@ size_t max_entries, const GetLocalChangesCallback& callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + BookmarkLocalChangesBuilder builder(bookmark_tracker_.get()); std::vector<syncer::CommitRequestData> local_changes = - BuildCommitRequestsForLocalChanges(max_entries); + builder.BuildCommitRequests(max_entries); callback.Run(std::move(local_changes)); } @@ -256,36 +144,11 @@ ScopedRemoteUpdateBookmarks update_bookmarks( bookmark_model_, bookmark_undo_service_, bookmark_model_observer_.get()); - - for (const syncer::UpdateResponseData* update : ReorderUpdates(updates)) { - const syncer::EntityData& update_entity = update->entity.value(); - // TODO(crbug.com/516866): Check |update_entity| for sanity. - // 1. Has bookmark specifics or no specifics in case of delete. - // 2. All meta info entries in the specifics have unique keys. - const SyncedBookmarkTracker::Entity* tracked_entity = - bookmark_tracker_->GetEntityForSyncId(update_entity.id); - if (update_entity.is_deleted()) { - ProcessRemoteDelete(update_entity, tracked_entity); - continue; - } - if (!tracked_entity) { - ProcessRemoteCreate(*update); - continue; - } - // Ignore changes to the permanent nodes (e.g. bookmarks bar). We only care - // about their children. - if (bookmark_model_->is_permanent_node(tracked_entity->bookmark_node())) { - continue; - } - ProcessRemoteUpdate(*update, tracked_entity); - } -} - -// static -std::vector<const syncer::UpdateResponseData*> -BookmarkModelTypeProcessor::ReorderUpdatesForTest( - const syncer::UpdateResponseDataList& updates) { - return ReorderUpdates(updates); + BookmarkRemoteUpdatesHandler updates_handler(bookmark_model_, + bookmark_tracker_.get()); + updates_handler.Process(updates); + // Schedule save just in case one is needed. + schedule_save_closure_.Run(); } const SyncedBookmarkTracker* BookmarkModelTypeProcessor::GetTrackerForTest() @@ -293,216 +156,6 @@ return bookmark_tracker_.get(); } -// static -std::vector<const syncer::UpdateResponseData*> -BookmarkModelTypeProcessor::ReorderUpdates( - const syncer::UpdateResponseDataList& updates) { - // TODO(crbug.com/516866): This is a very simple (hacky) reordering algorithm - // that assumes no folders exist except the top level permanent ones. This - // should be fixed before enabling USS for bookmarks. - std::vector<const syncer::UpdateResponseData*> ordered_updates; - for (const syncer::UpdateResponseData& update : updates) { - const syncer::EntityData& update_entity = update.entity.value(); - if (update_entity.parent_id == "0") { - continue; - } - if (update_entity.parent_id == kBookmarksRootId) { - ordered_updates.push_back(&update); - } - } - for (const syncer::UpdateResponseData& update : updates) { - const syncer::EntityData& update_entity = update.entity.value(); - // Deletions should come last. - if (update_entity.is_deleted()) { - continue; - } - if (update_entity.parent_id != "0" && - update_entity.parent_id != kBookmarksRootId) { - ordered_updates.push_back(&update); - } - } - // Now add deletions. - for (const syncer::UpdateResponseData& update : updates) { - const syncer::EntityData& update_entity = update.entity.value(); - if (!update_entity.is_deleted()) { - continue; - } - if (update_entity.parent_id != "0" && - update_entity.parent_id != kBookmarksRootId) { - ordered_updates.push_back(&update); - } - } - return ordered_updates; -} - -void BookmarkModelTypeProcessor::ProcessRemoteCreate( - const syncer::UpdateResponseData& update) { - // Because the Synced Bookmarks node can be created server side, it's possible - // it'll arrive at the client as an update. In that case it won't have been - // associated at startup, the GetChromeNodeFromSyncId call above will return - // null, and we won't detect it as a permanent node, resulting in us trying to - // create it here (which will fail). Therefore, we add special logic here just - // to detect the Synced Bookmarks folder. - const syncer::EntityData& update_entity = update.entity.value(); - DCHECK(!update_entity.is_deleted()); - if (update_entity.parent_id == kBookmarksRootId) { - // Associate permanent folders. - // TODO(crbug.com/516866): Method documentation says this method should be - // used in initial sync only. Make sure this is the case. - AssociatePermanentFolder(update); - return; - } - if (!IsValidBookmark(update_entity.specifics.bookmark(), - update_entity.is_folder)) { - // Ignore creations with invalid specifics. - DLOG(ERROR) << "Couldn't add bookmark with an invalid specifics."; - return; - } - const bookmarks::BookmarkNode* parent_node = GetParentNode(update_entity); - if (!parent_node) { - // If we cannot find the parent, we can do nothing. - DLOG(ERROR) << "Could not find parent of node being added." - << " Node title: " << update_entity.specifics.bookmark().title() - << ", parent id = " << update_entity.parent_id; - return; - } - // TODO(crbug.com/516866): This code appends the code to the very end of the - // list of the children by assigning the index to the - // parent_node->child_count(). It should instead compute the exact using the - // unique position information of the new node as well as the siblings. - const bookmarks::BookmarkNode* bookmark_node = CreateBookmarkNode( - update_entity, parent_node, bookmark_model_, parent_node->child_count()); - if (!bookmark_node) { - // We ignore bookmarks we can't add. - DLOG(ERROR) << "Failed to create bookmark node with title " - << update_entity.specifics.bookmark().title() << " and url " - << update_entity.specifics.bookmark().url(); - return; - } - bookmark_tracker_->Add(update_entity.id, bookmark_node, - update.response_version, update_entity.creation_time, - update_entity.unique_position, - update_entity.specifics); -} - -void BookmarkModelTypeProcessor::ProcessRemoteUpdate( - const syncer::UpdateResponseData& update, - const SyncedBookmarkTracker::Entity* tracked_entity) { - const syncer::EntityData& update_entity = update.entity.value(); - // Can only update existing nodes. - DCHECK(tracked_entity); - DCHECK_EQ(tracked_entity, - bookmark_tracker_->GetEntityForSyncId(update_entity.id)); - // Must not be a deletion. - DCHECK(!update_entity.is_deleted()); - if (!IsValidBookmark(update_entity.specifics.bookmark(), - update_entity.is_folder)) { - // Ignore updates with invalid specifics. - DLOG(ERROR) << "Couldn't update bookmark with an invalid specifics."; - return; - } - if (tracked_entity->IsUnsynced()) { - // TODO(crbug.com/516866): Handle conflict resolution. - return; - } - if (tracked_entity->MatchesData(update_entity)) { - bookmark_tracker_->Update(update_entity.id, update.response_version, - update_entity.modification_time, - update_entity.specifics); - // Since there is no change in the model data, we should trigger data - // persistence here to save latest metadata. - schedule_save_closure_.Run(); - return; - } - const bookmarks::BookmarkNode* node = tracked_entity->bookmark_node(); - if (update_entity.is_folder != node->is_folder()) { - DLOG(ERROR) << "Could not update node. Remote node is a " - << (update_entity.is_folder ? "folder" : "bookmark") - << " while local node is a " - << (node->is_folder() ? "folder" : "bookmark"); - return; - } - const sync_pb::BookmarkSpecifics& specifics = - update_entity.specifics.bookmark(); - if (!update_entity.is_folder) { - bookmark_model_->SetURL(node, GURL(specifics.url())); - } - - bookmark_model_->SetTitle(node, base::UTF8ToUTF16(specifics.title())); - // TODO(crbug.com/516866): Add the favicon related code. - bookmark_model_->SetNodeMetaInfoMap(node, GetBookmarkMetaInfo(update_entity)); - bookmark_tracker_->Update(update_entity.id, update.response_version, - update_entity.modification_time, - update_entity.specifics); - // TODO(crbug.com/516866): Handle reparenting. - // TODO(crbug.com/516866): Handle the case of moving the bookmark to a new - // position under the same parent (i.e. change in the unique position) -} - -void BookmarkModelTypeProcessor::ProcessRemoteDelete( - const syncer::EntityData& update_entity, - const SyncedBookmarkTracker::Entity* tracked_entity) { - DCHECK(update_entity.is_deleted()); - - DCHECK_EQ(tracked_entity, - bookmark_tracker_->GetEntityForSyncId(update_entity.id)); - - // Handle corner cases first. - if (tracked_entity == nullptr) { - // Local entity doesn't exist and update is tombstone. - DLOG(WARNING) << "Received remote delete for a non-existing item."; - return; - } - - const bookmarks::BookmarkNode* node = tracked_entity->bookmark_node(); - // Ignore changes to the permanent top-level nodes. We only care about - // their children. - if (bookmark_model_->is_permanent_node(node)) { - return; - } - // TODO(crbug.com/516866): Allow deletions of non-empty direcoties if makes - // sense, and recursively delete children. - if (node->child_count() > 0) { - DLOG(WARNING) << "Trying to delete a non-empty folder."; - return; - } - - bookmark_model_->Remove(node); - bookmark_tracker_->Remove(update_entity.id); -} - -const bookmarks::BookmarkNode* BookmarkModelTypeProcessor::GetParentNode( - const syncer::EntityData& update_entity) const { - const SyncedBookmarkTracker::Entity* parent_entity = - bookmark_tracker_->GetEntityForSyncId(update_entity.parent_id); - if (!parent_entity) { - return nullptr; - } - return parent_entity->bookmark_node(); -} - -void BookmarkModelTypeProcessor::AssociatePermanentFolder( - const syncer::UpdateResponseData& update) { - const syncer::EntityData& update_entity = update.entity.value(); - DCHECK_EQ(update_entity.parent_id, kBookmarksRootId); - - const bookmarks::BookmarkNode* permanent_node = nullptr; - if (update_entity.server_defined_unique_tag == kBookmarkBarTag) { - permanent_node = bookmark_model_->bookmark_bar_node(); - } else if (update_entity.server_defined_unique_tag == kOtherBookmarksTag) { - permanent_node = bookmark_model_->other_node(); - } else if (update_entity.server_defined_unique_tag == kMobileBookmarksTag) { - permanent_node = bookmark_model_->mobile_node(); - } - - if (permanent_node != nullptr) { - bookmark_tracker_->Add(update_entity.id, permanent_node, - update.response_version, update_entity.creation_time, - update_entity.unique_position, - update_entity.specifics); - } -} - std::string BookmarkModelTypeProcessor::EncodeSyncMetadata() const { std::string metadata_str; if (bookmark_tracker_) { @@ -513,7 +166,7 @@ return metadata_str; } -void BookmarkModelTypeProcessor::DecodeSyncMetadata( +void BookmarkModelTypeProcessor::ModelReadyToSync( const std::string& metadata_str, const base::RepeatingClosure& schedule_save_closure, bookmarks::BookmarkModel* model) { @@ -652,63 +305,6 @@ } } -std::vector<syncer::CommitRequestData> -BookmarkModelTypeProcessor::BuildCommitRequestsForLocalChanges( - size_t max_entries) { - DCHECK(bookmark_tracker_); - const std::vector<const SyncedBookmarkTracker::Entity*> - entities_with_local_changes = - bookmark_tracker_->GetEntitiesWithLocalChanges(max_entries); - DCHECK_LE(entities_with_local_changes.size(), max_entries); - - std::vector<syncer::CommitRequestData> commit_requests; - for (const SyncedBookmarkTracker::Entity* entity : - entities_with_local_changes) { - DCHECK(entity->IsUnsynced()); - const sync_pb::EntityMetadata* metadata = entity->metadata(); - - syncer::CommitRequestData request; - syncer::EntityData data; - data.id = metadata->server_id(); - data.creation_time = syncer::ProtoTimeToTime(metadata->creation_time()); - data.modification_time = - syncer::ProtoTimeToTime(metadata->modification_time()); - if (!metadata->is_deleted()) { - const bookmarks::BookmarkNode* node = entity->bookmark_node(); - DCHECK(node); - const bookmarks::BookmarkNode* parent = node->parent(); - const SyncedBookmarkTracker::Entity* parent_entity = - bookmark_tracker_->GetEntityForBookmarkNode(parent); - DCHECK(parent_entity); - data.parent_id = parent_entity->metadata()->server_id(); - // TODO(crbug.com/516866): Double check that custom passphrase works well - // with this implementation, because: - // 1. NonBlockingTypeCommitContribution::AdjustCommitProto() clears the - // title out. - // 2. Bookmarks (maybe ancient legacy bookmarks only?) use/used |name| to - // encode the title. - data.is_folder = node->is_folder(); - // TODO(crbug.com/516866): Set the non_unique_name similar to directory - // implementation. - // https://cs.chromium.org/chromium/src/components/sync/syncable/write_node.cc?l=41&rcl=1675007db1e0eb03417e81442688bb11cd181f58 - data.non_unique_name = base::UTF16ToUTF8(node->GetTitle()); - data.unique_position = parent_entity->metadata()->unique_position(); - // In case of deletion, make an EntityData with empty specifics to - // indicate deletion. - data.specifics = SpecificsFromBookmarkNode(node); - } - request.entity = data.PassToPtr(); - request.sequence_number = metadata->sequence_number(); - request.base_version = metadata->server_version(); - // Specifics hash has been computed in the tracker when this entity has been - // added/updated. - request.specifics_hash = metadata->specifics_hash(); - - commit_requests.push_back(std::move(request)); - } - return commit_requests; -} - void BookmarkModelTypeProcessor::StartTrackingMetadata( std::vector<NodeMetadataPair> nodes_metadata, std::unique_ptr<sync_pb::ModelTypeState> model_type_state) {
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.h b/components/sync_bookmarks/bookmark_model_type_processor.h index 420f6531..5dc106eb 100644 --- a/components/sync_bookmarks/bookmark_model_type_processor.h +++ b/components/sync_bookmarks/bookmark_model_type_processor.h
@@ -22,7 +22,6 @@ namespace bookmarks { class BookmarkModel; -class BookmarkNode; } namespace sync_bookmarks { @@ -57,7 +56,7 @@ void RecordMemoryUsageHistogram() override; // Encodes all sync metadata into a string, representing a state that can be - // restored via DecodeSyncMetadata() below. + // restored via ModelReadyToSync() below. std::string EncodeSyncMetadata() const; // It mainly decodes a BookmarkModelMetadata proto seralized in @@ -67,13 +66,9 @@ // used for further model operations. |schedule_save_closure| is a repeating // closure used to schedule a save of the bookmark model together with the // metadata. - void DecodeSyncMetadata(const std::string& metadata_str, - const base::RepeatingClosure& schedule_save_closure, - bookmarks::BookmarkModel* model); - - // Public for testing. - static std::vector<const syncer::UpdateResponseData*> ReorderUpdatesForTest( - const syncer::UpdateResponseDataList& updates); + void ModelReadyToSync(const std::string& metadata_str, + const base::RepeatingClosure& schedule_save_closure, + bookmarks::BookmarkModel* model); const SyncedBookmarkTracker* GetTrackerForTest() const; @@ -82,51 +77,6 @@ private: SEQUENCE_CHECKER(sequence_checker_); - // Reorders incoming updates such that parent creation is before child - // creation and child deletion is before parent deletion, and deletions should - // come last. The returned pointers point to the elements in the original - // |updates|. - static std::vector<const syncer::UpdateResponseData*> ReorderUpdates( - const syncer::UpdateResponseDataList& updates); - - // Given a remote update entity, it returns the parent bookmark node of the - // corresponding node. It returns null if the parent node cannot be found. - const bookmarks::BookmarkNode* GetParentNode( - const syncer::EntityData& update_entity) const; - - // Processes a remote creation of a bookmark node. - // 1. For permanent folders, they are only registered in |bookmark_tracker_|. - // 2. If the nodes parent cannot be found, the remote creation update is - // ignored. - // 3. Otherwise, a new node is created in the local bookmark model and - // registered in |bookmark_tracker_|. - void ProcessRemoteCreate(const syncer::UpdateResponseData& update); - - // Processes a remote update of a bookmark node. |update| must not be a - // deletion, and the server_id must be already tracked, otherwise, it is a - // creation that gets handeled in ProcessRemoteCreate(). |tracked_entity| is - // the tracked entity for that server_id. It is passed as a dependency instead - // of performing a lookup inside ProcessRemoteUpdate() to avoid wasting CPU - // cycles for doing another lookup (this code runs on the UI thread). - void ProcessRemoteUpdate(const syncer::UpdateResponseData& update, - const SyncedBookmarkTracker::Entity* tracked_entity); - - // Process a remote delete of a bookmark node. |update_entity| must not be a - // deletion. |tracked_entity| is the tracked entity for that server_id. It is - // passed as a dependency instead of performing a lookup inside - // ProcessRemoteDelete() to avoid wasting CPU cycles for doing another lookup - // (this code runs on the UI thread). - void ProcessRemoteDelete(const syncer::EntityData& update_entity, - const SyncedBookmarkTracker::Entity* tracked_entity); - - // Associates the permanent bookmark folders with the corresponding server - // side ids and registers the association in |bookmark_tracker_|. - // |update_entity| must contain server_defined_unique_tag that is used to - // determine the corresponding permanent node. All permanent nodes are assumed - // to be directly children nodes of |kBookmarksRootId|. This method is used in - // the initial sync cycle only. - void AssociatePermanentFolder(const syncer::UpdateResponseData& update); - // If preconditions are met, inform sync that we are ready to connect. void ConnectIfReady(); @@ -135,10 +85,6 @@ // entities. void NudgeForCommitIfNeeded(); - // Builds the commit requests list. - std::vector<syncer::CommitRequestData> BuildCommitRequestsForLocalChanges( - size_t max_entries); - // Instantiates the required objects to track metadata and starts observing // changes from the bookmark model. void StartTrackingMetadata( @@ -146,11 +92,11 @@ std::unique_ptr<sync_pb::ModelTypeState> model_type_state); // Stores the start callback in between OnSyncStarting() and - // DecodeSyncMetadata(). + // ModelReadyToSync(). StartCallback start_callback_; // The bookmark model we are processing local changes from and forwarding - // remote changes to. It is set during DecodeSyncMetadata(), which is called + // remote changes to. It is set during ModelReadyToSync(), which is called // during startup, as part of the bookmark-loading process. bookmarks::BookmarkModel* bookmark_model_ = nullptr; @@ -172,8 +118,10 @@ std::unique_ptr<syncer::CommitQueue> worker_; // Keeps the mapping between server ids and bookmarks nodes together with sync - // metadata. It is constructed and set during DecodeSyncMetadata(), which is - // called during startup, as part of the bookmark-loading process. + // metadata. It is constructed and set during ModelReadyToSync(), if the + // loaded bookmarks JSON contained previous sync metadata, or upon completion + // of initial sync, which is called during startup, as part of the + // bookmark-loading process. std::unique_ptr<SyncedBookmarkTracker> bookmark_tracker_; // GUID string that identifies the sync client and is received from the sync
diff --git a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc index 1b52f13..63af6deb 100644 --- a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc +++ b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
@@ -25,9 +25,6 @@ namespace { -// The parent tag for children of the root entity. Entities with this parent are -// referred to as top level enities. -const char kRootParentTag[] = "0"; const char kBookmarkBarTag[] = "bookmark_bar"; const char kBookmarkBarId[] = "bookmark_bar_id"; const char kBookmarksRootId[] = "32904_google_chrome_bookmarks"; @@ -119,12 +116,6 @@ return response_data; } -syncer::UpdateResponseData CreateBookmarkRootUpdateData() { - return CreateUpdateData({syncer::ModelTypeToRootTag(syncer::BOOKMARKS), - std::string(), std::string(), kRootParentTag, - syncer::ModelTypeToRootTag(syncer::BOOKMARKS)}); -} - class TestSyncClient : public syncer::FakeSyncClient { public: explicit TestSyncClient(bookmarks::BookmarkModel* bookmark_model) @@ -147,8 +138,9 @@ // TODO(crbug.com/516866): This class assumes model is loaded and sync has // started before running tests. We should test other variations (i.e. model // isn't loaded yet and/or sync didn't start yet). - processor_.DecodeSyncMetadata(std::string(), schedule_save_closure_.Get(), - bookmark_model_.get()); + processor_.ModelReadyToSync(/*metadata_str=*/std::string(), + schedule_save_closure_.Get(), + bookmark_model_.get()); syncer::DataTypeActivationRequest request; request.cache_guid = kCacheGuid; processor_.OnSyncStarting(request, base::DoNothing()); @@ -169,38 +161,6 @@ BookmarkModelTypeProcessor processor_; }; -TEST(BookmarkModelTypeProcessorReorderUpdatesTest, ShouldIgnoreRootNodes) { - syncer::UpdateResponseDataList updates; - updates.push_back(CreateBookmarkRootUpdateData()); - std::vector<const syncer::UpdateResponseData*> ordered_updates = - BookmarkModelTypeProcessor::ReorderUpdatesForTest(updates); - // Root node update should be filtered out. - EXPECT_THAT(ordered_updates.size(), Eq(0U)); -} - -// TODO(crbug.com/516866): This should change to cover the general case of -// parents before children for non-deletions, and another test should be added -// for children before parents for deletions. -TEST(BookmarkModelTypeProcessorReorderUpdatesTest, - ShouldPlacePermanentNodesFirstForNonDeletions) { - const std::string kNode1Id = "node1"; - const std::string kNode2Id = "node2"; - syncer::UpdateResponseDataList updates; - updates.push_back(CreateUpdateData( - {kNode1Id, std::string(), std::string(), kNode2Id, std::string()})); - updates.push_back(CreateUpdateData({kNode2Id, std::string(), std::string(), - kBookmarksRootId, kBookmarkBarTag})); - std::vector<const syncer::UpdateResponseData*> ordered_updates = - BookmarkModelTypeProcessor::ReorderUpdatesForTest(updates); - - // No update should be dropped. - ASSERT_THAT(ordered_updates.size(), Eq(2U)); - - // Updates should be ordered such that parent node update comes first. - EXPECT_THAT(ordered_updates[0]->entity.value().id, Eq(kNode2Id)); - EXPECT_THAT(ordered_updates[1]->entity.value().id, Eq(kNode1Id)); -} - TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteCreation) { syncer::UpdateResponseDataList updates; // Add update for the permanent folder "Bookmarks bar". @@ -219,9 +179,6 @@ bookmark_model()->bookmark_bar_node(); EXPECT_TRUE(bookmarkbar->empty()); - // Save will be scheduled in the model upon model change. No save should be - // scheduled from the processor. - EXPECT_CALL(*schedule_save_closure(), Run()).Times(0); processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates); ASSERT_THAT(bookmarkbar->GetChild(0), NotNull()); @@ -255,9 +212,6 @@ CreateUpdateData({kNodeId, kNewTitle, kNewUrl, kBookmarkBarId, /*server_tag=*/std::string()})); - // Save will be scheduled in the model upon model change. No save should be - // scheduled from the processor. - EXPECT_CALL(*schedule_save_closure(), Run()).Times(0); processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates); // Check if the bookmark has been updated properly. @@ -347,9 +301,6 @@ updates.push_back(CreateTombstone(kTitle1Id)); updates.push_back(CreateTombstone(kFolder1Id)); - // Save will be scheduled in the model upon model change. No save should be - // scheduled from the processor. - EXPECT_CALL(*schedule_save_closure(), Run()).Times(0); processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates); // The structure should be @@ -472,8 +423,8 @@ sync_client()->GetBookmarkUndoServiceIfExists()); std::string metadata_str; model_metadata.SerializeToString(&metadata_str); - new_processor.DecodeSyncMetadata(metadata_str, base::DoNothing(), - bookmark_model()); + new_processor.ModelReadyToSync(metadata_str, base::DoNothing(), + bookmark_model()); AssertState(&new_processor, bookmarks); } @@ -505,8 +456,8 @@ BookmarkModelTypeProcessor new_processor( sync_client()->GetBookmarkUndoServiceIfExists()); model_metadata.SerializeToString(&metadata_str); - new_processor.DecodeSyncMetadata(metadata_str, base::DoNothing(), - bookmark_model()); + new_processor.ModelReadyToSync(metadata_str, base::DoNothing(), + bookmark_model()); AssertState(&new_processor, bookmarks); }
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler.cc b/components/sync_bookmarks/bookmark_remote_updates_handler.cc new file mode 100644 index 0000000..cbd9674 --- /dev/null +++ b/components/sync_bookmarks/bookmark_remote_updates_handler.cc
@@ -0,0 +1,357 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/sync_bookmarks/bookmark_remote_updates_handler.h" + +#include "base/strings/utf_string_conversions.h" +#include "components/bookmarks/browser/bookmark_model.h" +#include "components/bookmarks/browser/bookmark_node.h" + +namespace sync_bookmarks { + +namespace { + +// The sync protocol identifies top-level entities by means of well-known tags, +// (aka server defined tags) which should not be confused with titles or client +// tags that aren't supported by bookmarks (at the time of writing). Each tag +// corresponds to a singleton instance of a particular top-level node in a +// user's share; the tags are consistent across users. The tags allow us to +// locate the specific folders whose contents we care about synchronizing, +// without having to do a lookup by name or path. The tags should not be made +// user-visible. For example, the tag "bookmark_bar" represents the permanent +// node for bookmarks bar in Chrome. The tag "other_bookmarks" represents the +// permanent folder Other Bookmarks in Chrome. +// +// It is the responsibility of something upstream (at time of writing, the sync +// server) to create these tagged nodes when initializing sync for the first +// time for a user. Thus, once the backend finishes initializing, the +// ProfileSyncService can rely on the presence of tagged nodes. +const char kBookmarkBarTag[] = "bookmark_bar"; +const char kMobileBookmarksTag[] = "synced_bookmarks"; +const char kOtherBookmarksTag[] = "other_bookmarks"; + +// Id is created by concatenating the specifics field number and the server tag +// similar to LookbackServerEntity::CreateId() that uses +// GetSpecificsFieldNumberFromModelType() to compute the field number. +const char kBookmarksRootId[] = "32904_google_chrome_bookmarks"; + +// |sync_entity| must contain a bookmark specifics. +// Metainfo entries must have unique keys. +bookmarks::BookmarkNode::MetaInfoMap GetBookmarkMetaInfo( + const syncer::EntityData& sync_entity) { + const sync_pb::BookmarkSpecifics& specifics = + sync_entity.specifics.bookmark(); + bookmarks::BookmarkNode::MetaInfoMap meta_info_map; + for (const sync_pb::MetaInfo& meta_info : specifics.meta_info()) { + meta_info_map[meta_info.key()] = meta_info.value(); + } + DCHECK_EQ(static_cast<size_t>(specifics.meta_info_size()), + meta_info_map.size()); + return meta_info_map; +} + +// Creates a bookmark node under the given parent node from the given sync node. +// Returns the newly created node. |sync_entity| must contain a bookmark +// specifics with Metainfo entries having unique keys. +const bookmarks::BookmarkNode* CreateBookmarkNode( + const syncer::EntityData& sync_entity, + const bookmarks::BookmarkNode* parent, + bookmarks::BookmarkModel* model, + int index) { + DCHECK(parent); + DCHECK(model); + + const sync_pb::BookmarkSpecifics& specifics = + sync_entity.specifics.bookmark(); + bookmarks::BookmarkNode::MetaInfoMap metainfo = + GetBookmarkMetaInfo(sync_entity); + if (sync_entity.is_folder) { + return model->AddFolderWithMetaInfo( + parent, index, base::UTF8ToUTF16(specifics.title()), &metainfo); + } + // 'creation_time_us' was added in M24. Assume a time of 0 means now. + const int64_t create_time_us = specifics.creation_time_us(); + base::Time create_time = + (create_time_us == 0) + ? base::Time::Now() + : base::Time::FromDeltaSinceWindowsEpoch( + // Use FromDeltaSinceWindowsEpoch because create_time_us has + // always used the Windows epoch. + base::TimeDelta::FromMicroseconds(create_time_us)); + return model->AddURLWithCreationTimeAndMetaInfo( + parent, index, base::UTF8ToUTF16(specifics.title()), + GURL(specifics.url()), create_time, &metainfo); + // TODO(crbug.com/516866): Add the favicon related code. +} + +// Check whether an incoming specifics represent a valid bookmark or not. +// |is_folder| is whether this specifics is for a folder or not. +// Folders and tomstones entail different validation conditions. +bool IsValidBookmark(const sync_pb::BookmarkSpecifics& specifics, + bool is_folder) { + if (specifics.ByteSize() == 0) { + DLOG(ERROR) << "Invalid bookmark: empty specifics."; + return false; + } + if (is_folder) { + return true; + } + if (!GURL(specifics.url()).is_valid()) { + DLOG(ERROR) << "Invalid bookmark: invalid url in the specifics."; + return false; + } + return true; +} + +} // namespace + +BookmarkRemoteUpdatesHandler::BookmarkRemoteUpdatesHandler( + bookmarks::BookmarkModel* bookmark_model, + SyncedBookmarkTracker* bookmark_tracker) + : bookmark_model_(bookmark_model), bookmark_tracker_(bookmark_tracker) { + DCHECK(bookmark_model); + DCHECK(bookmark_tracker); +} + +void BookmarkRemoteUpdatesHandler::Process( + const syncer::UpdateResponseDataList& updates) { + for (const syncer::UpdateResponseData* update : ReorderUpdates(updates)) { + const syncer::EntityData& update_entity = update->entity.value(); + // TODO(crbug.com/516866): Check |update_entity| for sanity. + // 1. Has bookmark specifics or no specifics in case of delete. + // 2. All meta info entries in the specifics have unique keys. + const SyncedBookmarkTracker::Entity* tracked_entity = + bookmark_tracker_->GetEntityForSyncId(update_entity.id); + if (update_entity.is_deleted()) { + ProcessRemoteDelete(update_entity, tracked_entity); + continue; + } + if (!tracked_entity) { + ProcessRemoteCreate(*update); + continue; + } + // Ignore changes to the permanent nodes (e.g. bookmarks bar). We only care + // about their children. + if (bookmark_model_->is_permanent_node(tracked_entity->bookmark_node())) { + continue; + } + ProcessRemoteUpdate(*update, tracked_entity); + } +} + +// static +std::vector<const syncer::UpdateResponseData*> +BookmarkRemoteUpdatesHandler::ReorderUpdatesForTest( + const syncer::UpdateResponseDataList& updates) { + return ReorderUpdates(updates); +} + +// static +std::vector<const syncer::UpdateResponseData*> +BookmarkRemoteUpdatesHandler::ReorderUpdates( + const syncer::UpdateResponseDataList& updates) { + // TODO(crbug.com/516866): This is a very simple (hacky) reordering algorithm + // that assumes no folders exist except the top level permanent ones. This + // should be fixed before enabling USS for bookmarks. + std::vector<const syncer::UpdateResponseData*> ordered_updates; + for (const syncer::UpdateResponseData& update : updates) { + const syncer::EntityData& update_entity = update.entity.value(); + if (update_entity.parent_id == "0") { + continue; + } + if (update_entity.parent_id == kBookmarksRootId) { + ordered_updates.push_back(&update); + } + } + for (const syncer::UpdateResponseData& update : updates) { + const syncer::EntityData& update_entity = update.entity.value(); + // Deletions should come last. + if (update_entity.is_deleted()) { + continue; + } + if (update_entity.parent_id != "0" && + update_entity.parent_id != kBookmarksRootId) { + ordered_updates.push_back(&update); + } + } + // Now add deletions. + for (const syncer::UpdateResponseData& update : updates) { + const syncer::EntityData& update_entity = update.entity.value(); + if (!update_entity.is_deleted()) { + continue; + } + if (update_entity.parent_id != "0" && + update_entity.parent_id != kBookmarksRootId) { + ordered_updates.push_back(&update); + } + } + return ordered_updates; +} + +void BookmarkRemoteUpdatesHandler::ProcessRemoteCreate( + const syncer::UpdateResponseData& update) { + // Because the Synced Bookmarks node can be created server side, it's possible + // it'll arrive at the client as an update. In that case it won't have been + // associated at startup, the GetChromeNodeFromSyncId call above will return + // null, and we won't detect it as a permanent node, resulting in us trying to + // create it here (which will fail). Therefore, we add special logic here just + // to detect the Synced Bookmarks folder. + const syncer::EntityData& update_entity = update.entity.value(); + DCHECK(!update_entity.is_deleted()); + if (update_entity.parent_id == kBookmarksRootId) { + // Associate permanent folders. + // TODO(crbug.com/516866): Method documentation says this method should be + // used in initial sync only. Make sure this is the case. + AssociatePermanentFolder(update); + return; + } + if (!IsValidBookmark(update_entity.specifics.bookmark(), + update_entity.is_folder)) { + // Ignore creations with invalid specifics. + DLOG(ERROR) << "Couldn't add bookmark with an invalid specifics."; + return; + } + const bookmarks::BookmarkNode* parent_node = GetParentNode(update_entity); + if (!parent_node) { + // If we cannot find the parent, we can do nothing. + DLOG(ERROR) << "Could not find parent of node being added." + << " Node title: " << update_entity.specifics.bookmark().title() + << ", parent id = " << update_entity.parent_id; + return; + } + // TODO(crbug.com/516866): This code appends the code to the very end of the + // list of the children by assigning the index to the + // parent_node->child_count(). It should instead compute the exact using the + // unique position information of the new node as well as the siblings. + const bookmarks::BookmarkNode* bookmark_node = CreateBookmarkNode( + update_entity, parent_node, bookmark_model_, parent_node->child_count()); + if (!bookmark_node) { + // We ignore bookmarks we can't add. + DLOG(ERROR) << "Failed to create bookmark node with title " + << update_entity.specifics.bookmark().title() << " and url " + << update_entity.specifics.bookmark().url(); + return; + } + bookmark_tracker_->Add(update_entity.id, bookmark_node, + update.response_version, update_entity.creation_time, + update_entity.unique_position, + update_entity.specifics); +} + +void BookmarkRemoteUpdatesHandler::ProcessRemoteUpdate( + const syncer::UpdateResponseData& update, + const SyncedBookmarkTracker::Entity* tracked_entity) { + const syncer::EntityData& update_entity = update.entity.value(); + // Can only update existing nodes. + DCHECK(tracked_entity); + DCHECK_EQ(tracked_entity, + bookmark_tracker_->GetEntityForSyncId(update_entity.id)); + // Must not be a deletion. + DCHECK(!update_entity.is_deleted()); + if (!IsValidBookmark(update_entity.specifics.bookmark(), + update_entity.is_folder)) { + // Ignore updates with invalid specifics. + DLOG(ERROR) << "Couldn't update bookmark with an invalid specifics."; + return; + } + if (tracked_entity->IsUnsynced()) { + // TODO(crbug.com/516866): Handle conflict resolution. + return; + } + if (tracked_entity->MatchesData(update_entity)) { + bookmark_tracker_->Update(update_entity.id, update.response_version, + update_entity.modification_time, + update_entity.specifics); + return; + } + const bookmarks::BookmarkNode* node = tracked_entity->bookmark_node(); + if (update_entity.is_folder != node->is_folder()) { + DLOG(ERROR) << "Could not update node. Remote node is a " + << (update_entity.is_folder ? "folder" : "bookmark") + << " while local node is a " + << (node->is_folder() ? "folder" : "bookmark"); + return; + } + const sync_pb::BookmarkSpecifics& specifics = + update_entity.specifics.bookmark(); + if (!update_entity.is_folder) { + bookmark_model_->SetURL(node, GURL(specifics.url())); + } + + bookmark_model_->SetTitle(node, base::UTF8ToUTF16(specifics.title())); + // TODO(crbug.com/516866): Add the favicon related code. + bookmark_model_->SetNodeMetaInfoMap(node, GetBookmarkMetaInfo(update_entity)); + bookmark_tracker_->Update(update_entity.id, update.response_version, + update_entity.modification_time, + update_entity.specifics); + // TODO(crbug.com/516866): Handle reparenting. + // TODO(crbug.com/516866): Handle the case of moving the bookmark to a new + // position under the same parent (i.e. change in the unique position) +} + +void BookmarkRemoteUpdatesHandler::ProcessRemoteDelete( + const syncer::EntityData& update_entity, + const SyncedBookmarkTracker::Entity* tracked_entity) { + DCHECK(update_entity.is_deleted()); + + DCHECK_EQ(tracked_entity, + bookmark_tracker_->GetEntityForSyncId(update_entity.id)); + + // Handle corner cases first. + if (tracked_entity == nullptr) { + // Local entity doesn't exist and update is tombstone. + DLOG(WARNING) << "Received remote delete for a non-existing item."; + return; + } + + const bookmarks::BookmarkNode* node = tracked_entity->bookmark_node(); + // Ignore changes to the permanent top-level nodes. We only care about + // their children. + if (bookmark_model_->is_permanent_node(node)) { + return; + } + // TODO(crbug.com/516866): Allow deletions of non-empty direcoties if makes + // sense, and recursively delete children. + if (node->child_count() > 0) { + DLOG(WARNING) << "Trying to delete a non-empty folder."; + return; + } + + bookmark_model_->Remove(node); + bookmark_tracker_->Remove(update_entity.id); +} + +const bookmarks::BookmarkNode* BookmarkRemoteUpdatesHandler::GetParentNode( + const syncer::EntityData& update_entity) const { + const SyncedBookmarkTracker::Entity* parent_entity = + bookmark_tracker_->GetEntityForSyncId(update_entity.parent_id); + if (!parent_entity) { + return nullptr; + } + return parent_entity->bookmark_node(); +} + +void BookmarkRemoteUpdatesHandler::AssociatePermanentFolder( + const syncer::UpdateResponseData& update) { + const syncer::EntityData& update_entity = update.entity.value(); + DCHECK_EQ(update_entity.parent_id, kBookmarksRootId); + + const bookmarks::BookmarkNode* permanent_node = nullptr; + if (update_entity.server_defined_unique_tag == kBookmarkBarTag) { + permanent_node = bookmark_model_->bookmark_bar_node(); + } else if (update_entity.server_defined_unique_tag == kOtherBookmarksTag) { + permanent_node = bookmark_model_->other_node(); + } else if (update_entity.server_defined_unique_tag == kMobileBookmarksTag) { + permanent_node = bookmark_model_->mobile_node(); + } + + if (permanent_node != nullptr) { + bookmark_tracker_->Add(update_entity.id, permanent_node, + update.response_version, update_entity.creation_time, + update_entity.unique_position, + update_entity.specifics); + } +} + +} // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler.h b/components/sync_bookmarks/bookmark_remote_updates_handler.h new file mode 100644 index 0000000..ed6ad22 --- /dev/null +++ b/components/sync_bookmarks/bookmark_remote_updates_handler.h
@@ -0,0 +1,89 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_REMOTE_UPDATES_HANDLER_H_ +#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_REMOTE_UPDATES_HANDLER_H_ + +#include <vector> + +#include "components/sync/engine/non_blocking_sync_common.h" +#include "components/sync_bookmarks/synced_bookmark_tracker.h" + +namespace bookmarks { +class BookmarkModel; +class BookmarkNode; +} // namespace bookmarks + +namespace sync_bookmarks { + +// Responsible for processing remote updates received from the sync server. +class BookmarkRemoteUpdatesHandler { + public: + // |bookmark_model| and |bookmark_tracker| must not be null and most outlive + // this object. + BookmarkRemoteUpdatesHandler(bookmarks::BookmarkModel* bookmark_model, + SyncedBookmarkTracker* bookmark_tracker); + // Processes the updates received from the sync server in |updates| and + // updates the |bookmark_model_| and |bookmark_tracker_| accordingly. + void Process(const syncer::UpdateResponseDataList& updates); + + // Public for testing. + static std::vector<const syncer::UpdateResponseData*> ReorderUpdatesForTest( + const syncer::UpdateResponseDataList& updates); + + private: + // Reorders incoming updates such that parent creation is before child + // creation and child deletion is before parent deletion, and deletions should + // come last. The returned pointers point to the elements in the original + // |updates|. + static std::vector<const syncer::UpdateResponseData*> ReorderUpdates( + const syncer::UpdateResponseDataList& updates); + + // Given a remote update entity, it returns the parent bookmark node of the + // corresponding node. It returns null if the parent node cannot be found. + const bookmarks::BookmarkNode* GetParentNode( + const syncer::EntityData& update_entity) const; + + // Processes a remote creation of a bookmark node. + // 1. For permanent folders, they are only registered in |bookmark_tracker_|. + // 2. If the nodes parent cannot be found, the remote creation update is + // ignored. + // 3. Otherwise, a new node is created in the local bookmark model and + // registered in |bookmark_tracker_|. + void ProcessRemoteCreate(const syncer::UpdateResponseData& update); + + // Processes a remote update of a bookmark node. |update| must not be a + // deletion, and the server_id must be already tracked, otherwise, it is a + // creation that gets handeled in ProcessRemoteCreate(). |tracked_entity| is + // the tracked entity for that server_id. It is passed as a dependency instead + // of performing a lookup inside ProcessRemoteUpdate() to avoid wasting CPU + // cycles for doing another lookup (this code runs on the UI thread). + void ProcessRemoteUpdate(const syncer::UpdateResponseData& update, + const SyncedBookmarkTracker::Entity* tracked_entity); + + // Process a remote delete of a bookmark node. |update_entity| must not be a + // deletion. |tracked_entity| is the tracked entity for that server_id. It is + // passed as a dependency instead of performing a lookup inside + // ProcessRemoteDelete() to avoid wasting CPU cycles for doing another lookup + // (this code runs on the UI thread). + void ProcessRemoteDelete(const syncer::EntityData& update_entity, + const SyncedBookmarkTracker::Entity* tracked_entity); + + // Associates the permanent bookmark folders with the corresponding server + // side ids and registers the association in |bookmark_tracker_|. + // |update_entity| must contain server_defined_unique_tag that is used to + // determine the corresponding permanent node. All permanent nodes are assumed + // to be directly children nodes of |kBookmarksRootId|. This method is used in + // the initial sync cycle only. + void AssociatePermanentFolder(const syncer::UpdateResponseData& update); + + bookmarks::BookmarkModel* const bookmark_model_; + SyncedBookmarkTracker* const bookmark_tracker_; + + DISALLOW_COPY_AND_ASSIGN(BookmarkRemoteUpdatesHandler); +}; + +} // namespace sync_bookmarks + +#endif // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_REMOTE_UPDATES_HANDLER_H_
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc new file mode 100644 index 0000000..b641fa1 --- /dev/null +++ b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
@@ -0,0 +1,95 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/sync_bookmarks/bookmark_remote_updates_handler.h" + +#include <string> + +#include "components/sync/base/model_type.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Eq; + +namespace sync_bookmarks { + +namespace { + +// The parent tag for children of the root entity. Entities with this parent are +// referred to as top level enities. +const char kRootParentTag[] = "0"; +const char kBookmarkBarTag[] = "bookmark_bar"; +const char kBookmarksRootId[] = "32904_google_chrome_bookmarks"; + +struct BookmarkInfo { + std::string server_id; + std::string title; + std::string url; // empty for folders. + std::string parent_id; + std::string server_tag; +}; + +syncer::UpdateResponseData CreateUpdateData(const BookmarkInfo& bookmark_info) { + syncer::EntityData data; + data.id = bookmark_info.server_id; + data.parent_id = bookmark_info.parent_id; + data.server_defined_unique_tag = bookmark_info.server_tag; + + sync_pb::BookmarkSpecifics* bookmark_specifics = + data.specifics.mutable_bookmark(); + bookmark_specifics->set_title(bookmark_info.title); + if (bookmark_info.url.empty()) { + data.is_folder = true; + } else { + bookmark_specifics->set_url(bookmark_info.url); + } + + syncer::UpdateResponseData response_data; + response_data.entity = data.PassToPtr(); + // Similar to what's done in the loopback_server. + response_data.response_version = 0; + return response_data; +} + +syncer::UpdateResponseData CreateBookmarkRootUpdateData() { + return CreateUpdateData({syncer::ModelTypeToRootTag(syncer::BOOKMARKS), + std::string(), std::string(), kRootParentTag, + syncer::ModelTypeToRootTag(syncer::BOOKMARKS)}); +} + +TEST(BookmarkRemoteUpdatesHandlerReorderUpdatesTest, ShouldIgnoreRootNodes) { + syncer::UpdateResponseDataList updates; + updates.push_back(CreateBookmarkRootUpdateData()); + std::vector<const syncer::UpdateResponseData*> ordered_updates = + BookmarkRemoteUpdatesHandler::ReorderUpdatesForTest(updates); + // Root node update should be filtered out. + EXPECT_THAT(ordered_updates.size(), Eq(0U)); +} + +// TODO(crbug.com/516866): This should change to cover the general case of +// parents before children for non-deletions, and another test should be added +// for children before parents for deletions. +TEST(BookmarkRemoteUpdatesHandlerReorderUpdatesTest, + ShouldPlacePermanentNodesFirstForNonDeletions) { + const std::string kNode1Id = "node1"; + const std::string kNode2Id = "node2"; + syncer::UpdateResponseDataList updates; + updates.push_back(CreateUpdateData( + {kNode1Id, std::string(), std::string(), kNode2Id, std::string()})); + updates.push_back(CreateUpdateData({kNode2Id, std::string(), std::string(), + kBookmarksRootId, kBookmarkBarTag})); + std::vector<const syncer::UpdateResponseData*> ordered_updates = + BookmarkRemoteUpdatesHandler::ReorderUpdatesForTest(updates); + + // No update should be dropped. + ASSERT_THAT(ordered_updates.size(), Eq(2U)); + + // Updates should be ordered such that parent node update comes first. + EXPECT_THAT(ordered_updates[0]->entity.value().id, Eq(kNode2Id)); + EXPECT_THAT(ordered_updates[1]->entity.value().id, Eq(kNode1Id)); +} + +} // namespace + +} // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_sync_service.cc b/components/sync_bookmarks/bookmark_sync_service.cc index 3ef8eac..8a3d8e50 100644 --- a/components/sync_bookmarks/bookmark_sync_service.cc +++ b/components/sync_bookmarks/bookmark_sync_service.cc
@@ -38,7 +38,7 @@ const base::RepeatingClosure& schedule_save_closure, bookmarks::BookmarkModel* model) { if (bookmark_model_type_processor_) { - bookmark_model_type_processor_->DecodeSyncMetadata( + bookmark_model_type_processor_->ModelReadyToSync( metadata_str, schedule_save_closure, model); } }
diff --git a/components/unified_consent/BUILD.gn b/components/unified_consent/BUILD.gn index 5cc7533..0f1f33dd 100644 --- a/components/unified_consent/BUILD.gn +++ b/components/unified_consent/BUILD.gn
@@ -9,8 +9,8 @@ "unified_consent_service.cc", "unified_consent_service.h", "unified_consent_service_client.h", - "url_keyed_anonymized_data_collection_consent_helper.cc", - "url_keyed_anonymized_data_collection_consent_helper.h", + "url_keyed_data_collection_consent_helper.cc", + "url_keyed_data_collection_consent_helper.h", ] deps = [ "//base", @@ -27,7 +27,7 @@ testonly = true sources = [ "unified_consent_service_unittest.cc", - "url_keyed_anonymized_data_collection_consent_helper_unittest.cc", + "url_keyed_data_collection_consent_helper_unittest.cc", ] deps = [ ":unified_consent",
diff --git a/components/unified_consent/pref_names.cc b/components/unified_consent/pref_names.cc index 7d4a2c9..8aa2fcd 100644 --- a/components/unified_consent/pref_names.cc +++ b/components/unified_consent/pref_names.cc
@@ -4,6 +4,7 @@ #include "components/unified_consent/pref_names.h" +namespace unified_consent { namespace prefs { const char kUnifiedConsentGiven[] = "unified_consent_given"; @@ -12,3 +13,4 @@ "url_keyed_anonymized_data_collection.enabled"; } // namespace prefs +} // namespace unified_consent
diff --git a/components/unified_consent/pref_names.h b/components/unified_consent/pref_names.h index ac20a73..a5a3ecd5 100644 --- a/components/unified_consent/pref_names.h +++ b/components/unified_consent/pref_names.h
@@ -5,11 +5,13 @@ #ifndef COMPONENTS_UNIFIED_CONSENT_PREF_NAMES_H_ #define COMPONENTS_UNIFIED_CONSENT_PREF_NAMES_H_ +namespace unified_consent { namespace prefs { extern const char kUnifiedConsentGiven[]; extern const char kUrlKeyedAnonymizedDataCollectionEnabled[]; } // namespace prefs +} // namespace unified_consent #endif // COMPONENTS_UNIFIED_CONSENT_PREF_NAMES_H_
diff --git a/components/unified_consent/unified_consent_service.cc b/components/unified_consent/unified_consent_service.cc index 6ba5d58..dc8fe992 100644 --- a/components/unified_consent/unified_consent_service.cc +++ b/components/unified_consent/unified_consent_service.cc
@@ -13,6 +13,8 @@ #include "components/unified_consent/pref_names.h" #include "components/unified_consent/unified_consent_service_client.h" +namespace unified_consent { + UnifiedConsentService::UnifiedConsentService( UnifiedConsentServiceClient* service_client, PrefService* pref_service, @@ -92,3 +94,5 @@ service_client_->SetSafeBrowsingExtendedReportingEnabled(true); service_client_->SetNetworkPredictionEnabled(true); } + +} // namespace unified_consent
diff --git a/components/unified_consent/unified_consent_service.h b/components/unified_consent/unified_consent_service.h index 162d2c9..a8098f1 100644 --- a/components/unified_consent/unified_consent_service.h +++ b/components/unified_consent/unified_consent_service.h
@@ -24,6 +24,8 @@ class SyncService; } +namespace unified_consent { + class UnifiedConsentServiceClient; // A browser-context keyed service that is used to manage the user consent @@ -64,4 +66,6 @@ DISALLOW_COPY_AND_ASSIGN(UnifiedConsentService); }; +} // namespace unified_consent + #endif // COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_H_
diff --git a/components/unified_consent/unified_consent_service_client.h b/components/unified_consent/unified_consent_service_client.h index 583c1f8..6eaf95a 100644 --- a/components/unified_consent/unified_consent_service_client.h +++ b/components/unified_consent/unified_consent_service_client.h
@@ -5,6 +5,8 @@ #ifndef COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_H_ #define COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_H_ +namespace unified_consent { + class UnifiedConsentServiceClient { public: virtual ~UnifiedConsentServiceClient() {} @@ -23,4 +25,6 @@ virtual void SetNetworkPredictionEnabled(bool enabled) = 0; }; +} // namespace unified_consent + #endif // COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_H_
diff --git a/components/unified_consent/unified_consent_service_unittest.cc b/components/unified_consent/unified_consent_service_unittest.cc index b30d4cb1..b81a988 100644 --- a/components/unified_consent/unified_consent_service_unittest.cc +++ b/components/unified_consent/unified_consent_service_unittest.cc
@@ -15,6 +15,7 @@ #include "services/identity/public/cpp/identity_test_environment.h" #include "testing/gtest/include/gtest/gtest.h" +namespace unified_consent { namespace { class TestSyncService : public syncer::FakeSyncService { @@ -142,3 +143,4 @@ #endif // !defined(OS_CHROMEOS) } // namespace +} // namespace unified_consent
diff --git a/components/unified_consent/url_keyed_anonymized_data_collection_consent_helper.h b/components/unified_consent/url_keyed_anonymized_data_collection_consent_helper.h deleted file mode 100644 index 9c92098..0000000 --- a/components/unified_consent/url_keyed_anonymized_data_collection_consent_helper.h +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_UNIFIED_CONSENT_URL_KEYED_ANONYMIZED_DATA_COLLECTION_CONSENT_HELPER_H_ -#define COMPONENTS_UNIFIED_CONSENT_URL_KEYED_ANONYMIZED_DATA_COLLECTION_CONSENT_HELPER_H_ - -#include <memory> - -#include "base/observer_list.h" - -class PrefService; -namespace syncer { -class SyncService; -} - -namespace unified_consent { - -// Helper class that allows clients to check whether the user has consented -// for URL-keyed anonymized data collection. -class UrlKeyedAnonymizedDataCollectionConsentHelper { - public: - class Observer { - public: - // Called when the state of the URL-keyed anonymized data collection - // changes. - virtual void OnUrlKeyedDataCollectionConsentStateChanged(bool enabled) = 0; - }; - - // Creates a new |UrlKeyedAnonymizedDataCollectionConsentHelper| instance. We - // distinguish the following cases: - // 1. If |is_unified_consent_enabled| true, then the instance is backed by - // |pref_service|. Url-keyed data collection is enabled if the preference - // |prefs::kUrlKeyedAnonymizedDataCollectionEnabled| is set to true. - // - // 2. If |is_unified_consent_enabled| is false, then the instance is backed by - // the sync service. Url-keyed data collection is enabled if sync is active - // and if sync history is enabled. - // - // Note: |pref_service| must outlive the retuned instance. - static std::unique_ptr<UrlKeyedAnonymizedDataCollectionConsentHelper> - NewInstance(bool is_unified_consent_enabled, - PrefService* pref_service, - syncer::SyncService* sync_service); - - virtual ~UrlKeyedAnonymizedDataCollectionConsentHelper(); - - // Returns true if the user has consented for URL keyed anonymized data - // collection. - virtual bool IsEnabled() = 0; - - // Methods to register or remove observers. - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); - - protected: - UrlKeyedAnonymizedDataCollectionConsentHelper(); - - // Fires |OnUrlKeyedDataCollectionConsentStateChanged| on all the observers. - void FireOnStateChanged(); - - private: - base::ObserverList<Observer, true> observer_list_; - - DISALLOW_COPY_AND_ASSIGN(UrlKeyedAnonymizedDataCollectionConsentHelper); -}; - -} // namespace unified_consent - -#endif // COMPONENTS_UNIFIED_CONSENT_URL_KEYED_ANONYMIZED_DATA_COLLECTION_CONSENT_HELPER_H_
diff --git a/components/unified_consent/url_keyed_anonymized_data_collection_consent_helper_unittest.cc b/components/unified_consent/url_keyed_anonymized_data_collection_consent_helper_unittest.cc deleted file mode 100644 index 620c33b..0000000 --- a/components/unified_consent/url_keyed_anonymized_data_collection_consent_helper_unittest.cc +++ /dev/null
@@ -1,67 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/unified_consent/url_keyed_anonymized_data_collection_consent_helper.h" - -#include <vector> - -#include "components/sync/driver/fake_sync_service.h" -#include "components/sync_preferences/testing_pref_service_syncable.h" -#include "components/unified_consent/pref_names.h" -#include "components/unified_consent/unified_consent_service.h" -#include "testing/gtest/include/gtest/gtest.h" - -using unified_consent::UrlKeyedAnonymizedDataCollectionConsentHelper; - -namespace { -class UrlKeyedDataCollectionConsentHelperTest - : public testing::Test, - public UrlKeyedAnonymizedDataCollectionConsentHelper::Observer { - public: - // testing::Test: - void SetUp() override { - UnifiedConsentService::RegisterPrefs(pref_service_.registry()); - } - - void OnUrlKeyedDataCollectionConsentStateChanged(bool enabled) override { - state_changed_notifications.push_back(enabled); - } - - protected: - sync_preferences::TestingPrefServiceSyncable pref_service_; - std::vector<bool> state_changed_notifications; - syncer::FakeSyncService sync_service_; -}; - -TEST_F(UrlKeyedDataCollectionConsentHelperTest, UnifiedConsentEnabled) { - std::unique_ptr<UrlKeyedAnonymizedDataCollectionConsentHelper> helper = - UrlKeyedAnonymizedDataCollectionConsentHelper::NewInstance( - true, &pref_service_, &sync_service_); - helper->AddObserver(this); - EXPECT_FALSE(helper->IsEnabled()); - EXPECT_TRUE(state_changed_notifications.empty()); - - pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled, - true); - EXPECT_TRUE(helper->IsEnabled()); - EXPECT_EQ(1U, state_changed_notifications.size()); - EXPECT_TRUE(state_changed_notifications[0]); - - state_changed_notifications.clear(); - pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled, - false); - EXPECT_FALSE(helper->IsEnabled()); - EXPECT_EQ(1U, state_changed_notifications.size()); - EXPECT_FALSE(state_changed_notifications[0]); - helper->RemoveObserver(this); -} - -TEST_F(UrlKeyedDataCollectionConsentHelperTest, UnifiedConsentDisabled) { - std::unique_ptr<UrlKeyedAnonymizedDataCollectionConsentHelper> helper = - UrlKeyedAnonymizedDataCollectionConsentHelper::NewInstance( - false, &pref_service_, &sync_service_); - EXPECT_FALSE(helper->IsEnabled()); -} - -} // namespace
diff --git a/components/unified_consent/url_keyed_anonymized_data_collection_consent_helper.cc b/components/unified_consent/url_keyed_data_collection_consent_helper.cc similarity index 63% rename from components/unified_consent/url_keyed_anonymized_data_collection_consent_helper.cc rename to components/unified_consent/url_keyed_data_collection_consent_helper.cc index ed19096..a504fdb 100644 --- a/components/unified_consent/url_keyed_anonymized_data_collection_consent_helper.cc +++ b/components/unified_consent/url_keyed_data_collection_consent_helper.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/unified_consent/url_keyed_anonymized_data_collection_consent_helper.h" +#include "components/unified_consent/url_keyed_data_collection_consent_helper.h" #include "base/bind.h" #include "components/prefs/pref_change_registrar.h" @@ -18,13 +18,13 @@ namespace { class PrefBasedUrlKeyedDataCollectionConsentHelper - : public UrlKeyedAnonymizedDataCollectionConsentHelper { + : public UrlKeyedDataCollectionConsentHelper { public: explicit PrefBasedUrlKeyedDataCollectionConsentHelper( PrefService* pref_service); ~PrefBasedUrlKeyedDataCollectionConsentHelper() override = default; - // UrlKeyedAnonymizedDataCollectionConsentHelper: + // UrlKeyedDataCollectionConsentHelper: bool IsEnabled() override; private: @@ -36,14 +36,15 @@ }; class SyncBasedUrlKeyedDataCollectionConsentHelper - : public UrlKeyedAnonymizedDataCollectionConsentHelper, + : public UrlKeyedDataCollectionConsentHelper, syncer::SyncServiceObserver { public: - explicit SyncBasedUrlKeyedDataCollectionConsentHelper( - syncer::SyncService* sync_service); + SyncBasedUrlKeyedDataCollectionConsentHelper( + syncer::SyncService* sync_service, + syncer::ModelType sync_data_type); ~SyncBasedUrlKeyedDataCollectionConsentHelper() override; - // UrlKeyedAnonymizedDataCollectionConsentHelper: + // UrlKeyedDataCollectionConsentHelper: bool IsEnabled() override; // syncer::SyncServiceObserver: @@ -52,7 +53,8 @@ private: syncer::SyncService* sync_service_; - syncer::UploadState sync_history_upload_state_; + syncer::ModelType sync_data_type_; + syncer::UploadState sync_data_type_upload_state_; DISALLOW_COPY_AND_ASSIGN(SyncBasedUrlKeyedDataCollectionConsentHelper); }; @@ -79,11 +81,12 @@ SyncBasedUrlKeyedDataCollectionConsentHelper:: SyncBasedUrlKeyedDataCollectionConsentHelper( - syncer::SyncService* sync_service) + syncer::SyncService* sync_service, + syncer::ModelType sync_data_type) : sync_service_(sync_service), - sync_history_upload_state_(syncer::GetUploadToGoogleState( - sync_service_, - syncer::ModelType::HISTORY_DELETE_DIRECTIVES)) { + sync_data_type_(sync_data_type), + sync_data_type_upload_state_( + syncer::GetUploadToGoogleState(sync_service_, sync_data_type_)) { DCHECK(sync_service_); sync_service_->AddObserver(this); } @@ -95,15 +98,15 @@ } bool SyncBasedUrlKeyedDataCollectionConsentHelper::IsEnabled() { - return sync_history_upload_state_ == syncer::UploadState::ACTIVE; + return sync_data_type_upload_state_ == syncer::UploadState::ACTIVE; } void SyncBasedUrlKeyedDataCollectionConsentHelper::OnStateChanged( syncer::SyncService* sync_service) { DCHECK_EQ(sync_service_, sync_service); bool enabled_before_state_updated = IsEnabled(); - sync_history_upload_state_ = syncer::GetUploadToGoogleState( - sync_service_, syncer::ModelType::HISTORY_DELETE_DIRECTIVES); + sync_data_type_upload_state_ = + syncer::GetUploadToGoogleState(sync_service_, sync_data_type_); if (enabled_before_state_updated != IsEnabled()) FireOnStateChanged(); @@ -118,13 +121,14 @@ } // namespace -UrlKeyedAnonymizedDataCollectionConsentHelper:: - UrlKeyedAnonymizedDataCollectionConsentHelper() = default; -UrlKeyedAnonymizedDataCollectionConsentHelper:: - ~UrlKeyedAnonymizedDataCollectionConsentHelper() = default; +UrlKeyedDataCollectionConsentHelper::UrlKeyedDataCollectionConsentHelper() = + default; +UrlKeyedDataCollectionConsentHelper::~UrlKeyedDataCollectionConsentHelper() = + default; -std::unique_ptr<UrlKeyedAnonymizedDataCollectionConsentHelper> -UrlKeyedAnonymizedDataCollectionConsentHelper::NewInstance( +// static +std::unique_ptr<UrlKeyedDataCollectionConsentHelper> +UrlKeyedDataCollectionConsentHelper::NewAnonymizedDataCollectionConsentHelper( bool is_unified_consent_enabled, PrefService* pref_service, syncer::SyncService* sync_service) { @@ -134,22 +138,31 @@ } return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>( - sync_service); + sync_service, syncer::ModelType::HISTORY_DELETE_DIRECTIVES); } -void UrlKeyedAnonymizedDataCollectionConsentHelper::AddObserver( - Observer* observer) { +// static +std::unique_ptr<UrlKeyedDataCollectionConsentHelper> +UrlKeyedDataCollectionConsentHelper::NewPersonalizedDataCollectionConsentHelper( + bool is_unified_consent_enabled, + syncer::SyncService* sync_service) { + syncer::ModelType sync_type = + is_unified_consent_enabled ? syncer::ModelType::USER_EVENTS + : syncer::ModelType::HISTORY_DELETE_DIRECTIVES; + return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>( + sync_service, sync_type); +} + +void UrlKeyedDataCollectionConsentHelper::AddObserver(Observer* observer) { observer_list_.AddObserver(observer); } -void UrlKeyedAnonymizedDataCollectionConsentHelper::RemoveObserver( - Observer* observer) { +void UrlKeyedDataCollectionConsentHelper::RemoveObserver(Observer* observer) { observer_list_.RemoveObserver(observer); } -void UrlKeyedAnonymizedDataCollectionConsentHelper::FireOnStateChanged() { - bool is_enabled = IsEnabled(); +void UrlKeyedDataCollectionConsentHelper::FireOnStateChanged() { for (auto& observer : observer_list_) - observer.OnUrlKeyedDataCollectionConsentStateChanged(is_enabled); + observer.OnUrlKeyedDataCollectionConsentStateChanged(this); } } // namespace unified_consent
diff --git a/components/unified_consent/url_keyed_data_collection_consent_helper.h b/components/unified_consent/url_keyed_data_collection_consent_helper.h new file mode 100644 index 0000000..f4d8486 --- /dev/null +++ b/components/unified_consent/url_keyed_data_collection_consent_helper.h
@@ -0,0 +1,88 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UNIFIED_CONSENT_URL_KEYED_DATA_COLLECTION_CONSENT_HELPER_H_ +#define COMPONENTS_UNIFIED_CONSENT_URL_KEYED_DATA_COLLECTION_CONSENT_HELPER_H_ + +#include <memory> + +#include "base/observer_list.h" + +class PrefService; +namespace syncer { +class SyncService; +} + +namespace unified_consent { + +// Helper class that allows clients to check whether the user has consented +// for URL-keyed data collection. +class UrlKeyedDataCollectionConsentHelper { + public: + class Observer { + public: + // Called when the state of the URL-keyed data collection changes. + virtual void OnUrlKeyedDataCollectionConsentStateChanged( + UrlKeyedDataCollectionConsentHelper* consent_helper) = 0; + }; + + // Creates a new |UrlKeyedDataCollectionConsentHelper| instance that checks + // whether *anonymized* data collection is enabled. This should be used when + // the client needs to check whether the user has granted consent for + // *anonymized* URL-keyed data collection. + // + // Implementation-wise we distinguish the following cases: + // 1. If |is_unified_consent_enabled| true, then the instance is backed by + // |pref_service|. Url-keyed data collection is enabled if the preference + // |prefs::kUrlKeyedAnonymizedDataCollectionEnabled| is set to true. + // + // 2. If |is_unified_consent_enabled| is false, then the instance is backed by + // the sync service. Url-keyed data collection is enabled if sync is active + // and if sync history is enabled. + // + // Note: |pref_service| must outlive the retuned instance. + static std::unique_ptr<UrlKeyedDataCollectionConsentHelper> + NewAnonymizedDataCollectionConsentHelper(bool is_unified_consent_enabled, + PrefService* pref_service, + syncer::SyncService* sync_service); + + // Creates a new |UrlKeyedDataCollectionConsentHelper| instance that checks + // whether *personalized* data collection is enabled. This should be used when + // the client needs to check whether the user has granted consent for + // URL-keyed data collection keyed by their Google account. + // + // Implementation-wise we distinguish the following cases: + // 1. If |is_unified_consent_enabled| is true then URL-keyed data collection + // is enabled if sync is active and if sync event logger is enabled. + // 2. If |is_unified_consent_enabled| is false then URL-keyed data collection + // is enabled if sync is active and if sync history is enabled. + static std::unique_ptr<UrlKeyedDataCollectionConsentHelper> + NewPersonalizedDataCollectionConsentHelper(bool is_unified_consent_enabled, + syncer::SyncService* sync_service); + + virtual ~UrlKeyedDataCollectionConsentHelper(); + + // Returns true if the user has consented for URL keyed anonymized data + // collection. + virtual bool IsEnabled() = 0; + + // Methods to register or remove observers. + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + + protected: + UrlKeyedDataCollectionConsentHelper(); + + // Fires |OnUrlKeyedDataCollectionConsentStateChanged| on all the observers. + void FireOnStateChanged(); + + private: + base::ObserverList<Observer, true> observer_list_; + + DISALLOW_COPY_AND_ASSIGN(UrlKeyedDataCollectionConsentHelper); +}; + +} // namespace unified_consent + +#endif // COMPONENTS_UNIFIED_CONSENT_URL_KEYED_DATA_COLLECTION_CONSENT_HELPER_H_
diff --git a/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc b/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc new file mode 100644 index 0000000..fe58e83d --- /dev/null +++ b/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
@@ -0,0 +1,176 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/unified_consent/url_keyed_data_collection_consent_helper.h" + +#include <vector> + +#include "components/sync/driver/fake_sync_service.h" +#include "components/sync/engine/cycle/sync_cycle_snapshot.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "components/unified_consent/pref_names.h" +#include "components/unified_consent/unified_consent_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace unified_consent { +namespace { + +class TestSyncService : public syncer::FakeSyncService { + public: + void set_sync_initialized(bool sync_initialized) { + sync_initialized_ = sync_initialized; + } + void set_sync_active_data_type(syncer::ModelType type) { + sync_active_data_type_ = type; + } + void FireOnStateChangeOnAllObservers() { + for (auto& observer : observers_) + observer.OnStateChanged(this); + } + + // syncer::FakeSyncService: + bool IsSyncAllowed() const override { return true; } + bool CanSyncStart() const override { return true; } + syncer::ModelTypeSet GetPreferredDataTypes() const override { + return syncer::ModelTypeSet(syncer::ModelType::HISTORY_DELETE_DIRECTIVES, + syncer::ModelType::USER_EVENTS); + } + bool IsSyncActive() const override { return true; } + bool ConfigurationDone() const override { return true; } + + syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override { + if (!sync_initialized_) + return syncer::SyncCycleSnapshot(); + return syncer::SyncCycleSnapshot( + syncer::ModelNeutralState(), syncer::ProgressMarkerMap(), false, 5, 2, + 7, false, 0, base::Time::Now(), base::Time::Now(), + std::vector<int>(syncer::MODEL_TYPE_COUNT, 0), + std::vector<int>(syncer::MODEL_TYPE_COUNT, 0), + sync_pb::SyncEnums::UNKNOWN_ORIGIN, + /*short_poll_interval=*/base::TimeDelta::FromMinutes(30), + /*long_poll_interval=*/base::TimeDelta::FromMinutes(180), + /*has_remaining_local_changes=*/false); + } + + syncer::ModelTypeSet GetActiveDataTypes() const override { + if (sync_active_data_type_ != syncer::ModelType::UNSPECIFIED) { + return syncer::ModelTypeSet(sync_active_data_type_); + } + return syncer::ModelTypeSet(); + } + + void AddObserver(syncer::SyncServiceObserver* observer) override { + observers_.AddObserver(observer); + } + void RemoveObserver(syncer::SyncServiceObserver* observer) override { + observers_.RemoveObserver(observer); + } + + private: + bool sync_initialized_ = false; + syncer::ModelType sync_active_data_type_ = syncer::ModelType::UNSPECIFIED; + base::ObserverList<syncer::SyncServiceObserver> observers_; +}; + +class UrlKeyedDataCollectionConsentHelperTest + : public testing::Test, + public UrlKeyedDataCollectionConsentHelper::Observer { + public: + // testing::Test: + void SetUp() override { + UnifiedConsentService::RegisterPrefs(pref_service_.registry()); + } + + void OnUrlKeyedDataCollectionConsentStateChanged( + UrlKeyedDataCollectionConsentHelper* consent_helper) override { + state_changed_notifications.push_back(consent_helper->IsEnabled()); + } + + protected: + sync_preferences::TestingPrefServiceSyncable pref_service_; + std::vector<bool> state_changed_notifications; + TestSyncService sync_service_; +}; + +TEST_F(UrlKeyedDataCollectionConsentHelperTest, + AnonymizedDataCollection_UnifiedConsentEnabled) { + std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper = + UrlKeyedDataCollectionConsentHelper:: + NewAnonymizedDataCollectionConsentHelper(true, &pref_service_, + &sync_service_); + helper->AddObserver(this); + EXPECT_FALSE(helper->IsEnabled()); + EXPECT_TRUE(state_changed_notifications.empty()); + + pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled, + true); + EXPECT_TRUE(helper->IsEnabled()); + ASSERT_EQ(1U, state_changed_notifications.size()); + EXPECT_TRUE(state_changed_notifications[0]); + + state_changed_notifications.clear(); + pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled, + false); + EXPECT_FALSE(helper->IsEnabled()); + ASSERT_EQ(1U, state_changed_notifications.size()); + EXPECT_FALSE(state_changed_notifications[0]); + helper->RemoveObserver(this); +} + +TEST_F(UrlKeyedDataCollectionConsentHelperTest, + AnonymizedDataCollection_UnifiedConsentDisabled) { + std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper = + UrlKeyedDataCollectionConsentHelper:: + NewAnonymizedDataCollectionConsentHelper(false, &pref_service_, + &sync_service_); + helper->AddObserver(this); + EXPECT_FALSE(helper->IsEnabled()); + EXPECT_TRUE(state_changed_notifications.empty()); + + sync_service_.set_sync_initialized(true); + sync_service_.set_sync_active_data_type( + syncer::ModelType::HISTORY_DELETE_DIRECTIVES); + sync_service_.FireOnStateChangeOnAllObservers(); + EXPECT_TRUE(helper->IsEnabled()); + ASSERT_EQ(1U, state_changed_notifications.size()); + helper->RemoveObserver(this); +} + +TEST_F(UrlKeyedDataCollectionConsentHelperTest, + PersonalizeddDataCollection_UnifiedConsentEnabled) { + std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper = + UrlKeyedDataCollectionConsentHelper:: + NewPersonalizedDataCollectionConsentHelper(true, &sync_service_); + helper->AddObserver(this); + EXPECT_FALSE(helper->IsEnabled()); + EXPECT_TRUE(state_changed_notifications.empty()); + + sync_service_.set_sync_initialized(true); + sync_service_.set_sync_active_data_type(syncer::ModelType::USER_EVENTS); + sync_service_.FireOnStateChangeOnAllObservers(); + EXPECT_TRUE(helper->IsEnabled()); + ASSERT_EQ(1U, state_changed_notifications.size()); + helper->RemoveObserver(this); +} + +TEST_F(UrlKeyedDataCollectionConsentHelperTest, + PersonalizedDataCollection_UnifiedConsentDisabled) { + std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper = + UrlKeyedDataCollectionConsentHelper:: + NewPersonalizedDataCollectionConsentHelper(false, &sync_service_); + helper->AddObserver(this); + EXPECT_FALSE(helper->IsEnabled()); + EXPECT_TRUE(state_changed_notifications.empty()); + + sync_service_.set_sync_initialized(true); + sync_service_.set_sync_active_data_type( + syncer::ModelType::HISTORY_DELETE_DIRECTIVES); + sync_service_.FireOnStateChangeOnAllObservers(); + EXPECT_TRUE(helper->IsEnabled()); + ASSERT_EQ(1U, state_changed_notifications.size()); + helper->RemoveObserver(this); +} + +} // namespace +} // namespace unified_consent
diff --git a/components/zucchini/buffer_source.cc b/components/zucchini/buffer_source.cc index 721588a..d72d329 100644 --- a/components/zucchini/buffer_source.cc +++ b/components/zucchini/buffer_source.cc
@@ -80,7 +80,7 @@ for (int shift = 0; shift < shift_lim; shift += 7, ++cur) { uint32_t b = *cur; // When |shift == 28|, |(b & 0x7F) << shift| discards the "???" bits. - value |= static_cast<int32_t>(b & 0x7F) << shift; + value |= static_cast<int32_t>(static_cast<uint32_t>(b & 0x7F) << shift); if (!(b & 0x80)) { *ret = (shift == 28) ? value : SignExtend(shift + 6, value); seek(cur + 1);
diff --git a/components/zucchini/fuzzers/imposed_ensemble_matcher_fuzzer.cc b/components/zucchini/fuzzers/imposed_ensemble_matcher_fuzzer.cc index 5c129a3..bbb06cab 100644 --- a/components/zucchini/fuzzers/imposed_ensemble_matcher_fuzzer.cc +++ b/components/zucchini/fuzzers/imposed_ensemble_matcher_fuzzer.cc
@@ -63,7 +63,7 @@ // as it is a failure in Zucchini's patch performance that is worth // investigating. size_t patch_size = patch_writer.SerializedSize(); - CHECK_LE(patch_size, kMaxImageSize * 2); + CHECK_LE(patch_size, kMaxImageSize * 3); // Write to buffer to avoid IO. std::unique_ptr<uint8_t[]> patch_data(new uint8_t[patch_size]);
diff --git a/content/DEPS b/content/DEPS index 1ec0d62..b35c12a 100644 --- a/content/DEPS +++ b/content/DEPS
@@ -24,6 +24,7 @@ # settings, packaging details, installation or crash reporting. "+components/services/filesystem", + "+components/services/font", "+crypto", "+grit/blink_resources.h",
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc index 1fec0c7..998a8a8 100644 --- a/content/app/content_main_runner_impl.cc +++ b/content/app/content_main_runner_impl.cc
@@ -97,13 +97,11 @@ #if defined(OS_LINUX) #include "base/native_library.h" #include "base/rand_util.h" -#include "content/common/font_config_ipc_linux.h" #include "services/service_manager/zygote/common/common_sandbox_support_linux.h" #include "third_party/blink/public/platform/web_font_render_style.h" #include "third_party/boringssl/src/include/openssl/crypto.h" #include "third_party/boringssl/src/include/openssl/rand.h" #include "third_party/skia/include/core/SkFontMgr.h" -#include "third_party/skia/include/ports/SkFontConfigInterface.h" #include "third_party/skia/include/ports/SkFontMgr_android.h" #include "third_party/webrtc_overrides/init_webrtc.h" // nogncheck @@ -389,9 +387,6 @@ #endif InitializeWebRtcModule(); - SkFontConfigInterface::SetGlobal( - sk_make_sp<FontConfigIPC>(service_manager::GetSandboxFD())); - // Set the android SkFontMgr for blink. We need to ensure this is done // before the sandbox is initialized to allow the font manager to access // font configuration files on disk.
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 5ec37808..7eb6668 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -690,6 +690,8 @@ "devtools/shared_worker_devtools_agent_host.h", "devtools/shared_worker_devtools_manager.cc", "devtools/shared_worker_devtools_manager.h", + "devtools/target_registry.cc", + "devtools/target_registry.h", "discardable_shared_memory_manager.cc", "dom_storage/dom_storage_area.cc", "dom_storage/dom_storage_area.h", @@ -1245,8 +1247,6 @@ "renderer_host/event_with_latency_info.h", "renderer_host/file_utilities_host_impl.cc", "renderer_host/file_utilities_host_impl.h", - "renderer_host/font_utils_linux.cc", - "renderer_host/font_utils_linux.h", "renderer_host/frame_connector_delegate.cc", "renderer_host/frame_connector_delegate.h", "renderer_host/frame_metadata_util.cc", @@ -2042,6 +2042,9 @@ if (use_pangocairo) { sources += [ "renderer_host/pepper/pepper_truetype_font_list_pango.cc" ] } + if (is_linux && !is_android) { + deps += [ "//components/services/font:ppapi_fontconfig_matching" ] + } } if (enable_library_cdms) {
diff --git a/content/browser/appcache/appcache_navigation_handle_core.cc b/content/browser/appcache/appcache_navigation_handle_core.cc index a76f2dcb..57d00b1 100644 --- a/content/browser/appcache/appcache_navigation_handle_core.cc +++ b/content/browser/appcache/appcache_navigation_handle_core.cc
@@ -93,7 +93,30 @@ void AppCacheNavigationHandleCore::AddRequestToDebugLog(const GURL& url) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (debug_log_) - debug_log_->emplace_back("Req:" + url.spec().substr(0, 64)); + debug_log_->emplace_back("Req:host=" + HostToString() + + ",url=" + url.spec().substr(0, 64)); +} + +void AppCacheNavigationHandleCore::AddDefaultFactoryRunToDebugLog( + bool was_request_intercepted) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (debug_log_) { + debug_log_->emplace_back( + base::StringPrintf("Fac:host=%s,int=%s", HostToString().c_str(), + was_request_intercepted ? "T" : "F")); + } +} + +void AppCacheNavigationHandleCore::AddCreateURLLoaderToDebugLog() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (debug_log_) + debug_log_->emplace_back("Load:host=" + HostToString()); +} + +void AppCacheNavigationHandleCore::AddNavigationStartToDebugLog() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (debug_log_) + debug_log_->emplace_back("Start:host=" + HostToString()); } std::string AppCacheNavigationHandleCore::GetDebugLog() { @@ -161,4 +184,9 @@ DCHECK(false); } +std::string AppCacheNavigationHandleCore::HostToString() { + return precreated_host_ ? std::to_string(precreated_host_->host_id()) + : "null"; +} + } // namespace content
diff --git a/content/browser/appcache/appcache_navigation_handle_core.h b/content/browser/appcache/appcache_navigation_handle_core.h index d93d91b..bb7eddc7 100644 --- a/content/browser/appcache/appcache_navigation_handle_core.h +++ b/content/browser/appcache/appcache_navigation_handle_core.h
@@ -48,6 +48,9 @@ AppCacheServiceImpl* GetAppCacheService(); void AddRequestToDebugLog(const GURL& url); + void AddDefaultFactoryRunToDebugLog(bool was_request_intercepted); + void AddCreateURLLoaderToDebugLog(); + void AddNavigationStartToDebugLog(); std::string GetDebugLog(); protected: @@ -74,6 +77,8 @@ network::mojom::URLLoaderFactoryPtr url_loader_factory) override; private: + std::string HostToString(); + std::unique_ptr<AppCacheHost> precreated_host_; scoped_refptr<ChromeAppCacheService> appcache_service_; int appcache_host_id_;
diff --git a/content/browser/background_fetch/storage/README.md b/content/browser/background_fetch/storage/README.md index e21e9c3..a21b6bc 100644 --- a/content/browser/background_fetch/storage/README.md +++ b/content/browser/background_fetch/storage/README.md
@@ -45,6 +45,19 @@ value: "<serialized content::proto::BackgroundFetchCompletedRequest>" ``` +## Cache Storage UserData schema + +The downloaded responses of every fetch will be stored in their own private cache. + +### Cache identifiers +* `origin`: `<origin>` +* `owner`: `CacheStorageOwner::kBackgroundFetch` +* `cache_name`: `<unique_id>` + +The cache will contain all the Request/Response key/value pairs of the fetch. +Note that the Request value stored isn't comprehensive, and only the url value is +used to as a key to find the matching Response. + ### Expansions * `<unique_id>` is a GUID (v4) that identifies a background fetch registration. E.g. `17467386-60b4-4c5b-b66c-aabf793fd39b`
diff --git a/content/browser/devtools/browser_devtools_agent_host.cc b/content/browser/devtools/browser_devtools_agent_host.cc index 7e4243c..d361bc1 100644 --- a/content/browser/devtools/browser_devtools_agent_host.cc +++ b/content/browser/devtools/browser_devtools_agent_host.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/guid.h" +#include "base/json/json_reader.h" #include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "content/browser/devtools/devtools_session.h" @@ -18,6 +19,7 @@ #include "content/browser/devtools/protocol/target_handler.h" #include "content/browser/devtools/protocol/tethering_handler.h" #include "content/browser/devtools/protocol/tracing_handler.h" +#include "content/browser/devtools/target_registry.h" #include "content/browser/frame_host/frame_tree_node.h" namespace content { @@ -48,30 +50,37 @@ BrowserDevToolsAgentHost::~BrowserDevToolsAgentHost() { } -bool BrowserDevToolsAgentHost::AttachSession(DevToolsSession* session) { +bool BrowserDevToolsAgentHost::AttachSession(DevToolsSession* session, + TargetRegistry* parent_registry) { + DCHECK(!parent_registry); + if (session->restricted()) return false; + auto registry = std::make_unique<TargetRegistry>(session); + TargetRegistry* registry_ptr = registry.get(); + target_registries_[session->client()] = std::move(registry); session->SetBrowserOnly(true); - session->AddHandler(base::WrapUnique( - new protocol::TargetHandler(true /* browser_only */, GetId()))); + session->AddHandler(std::make_unique<protocol::TargetHandler>( + true /* browser_only */, GetId(), registry_ptr)); if (only_discovery_) return true; - session->AddHandler(base::WrapUnique(new protocol::BrowserHandler())); - session->AddHandler(base::WrapUnique(new protocol::IOHandler( - GetIOContext()))); - session->AddHandler(base::WrapUnique(new protocol::MemoryHandler())); - session->AddHandler(base::WrapUnique(new protocol::SecurityHandler())); - session->AddHandler(base::WrapUnique(new protocol::SystemInfoHandler())); - session->AddHandler(base::WrapUnique(new protocol::TetheringHandler( - socket_callback_, tethering_task_runner_))); + session->AddHandler(std::make_unique<protocol::BrowserHandler>()); + session->AddHandler(std::make_unique<protocol::IOHandler>(GetIOContext())); + session->AddHandler(std::make_unique<protocol::MemoryHandler>()); + session->AddHandler(std::make_unique<protocol::SecurityHandler>()); + session->AddHandler(std::make_unique<protocol::SystemInfoHandler>()); + session->AddHandler(std::make_unique<protocol::TetheringHandler>( + socket_callback_, tethering_task_runner_)); session->AddHandler( - base::WrapUnique(new protocol::TracingHandler(nullptr, GetIOContext()))); + std::make_unique<protocol::TracingHandler>(nullptr, GetIOContext())); return true; } -void BrowserDevToolsAgentHost::DetachSession(DevToolsSession* session) {} +void BrowserDevToolsAgentHost::DetachSession(DevToolsSession* session) { + target_registries_.erase(session->client()); +} std::string BrowserDevToolsAgentHost::GetType() { return kTypeBrowser; @@ -96,10 +105,15 @@ void BrowserDevToolsAgentHost::Reload() { } -void BrowserDevToolsAgentHost::DispatchProtocolMessage( - DevToolsSession* session, - const std::string& message) { - session->DispatchProtocolMessage(message); +bool BrowserDevToolsAgentHost::DispatchProtocolMessage( + DevToolsAgentHostClient* client, + const std::string& message, + base::DictionaryValue* parsed_message) { + auto* target_registry = target_registries_[client].get(); + if (target_registry->DispatchMessageOnAgentHost(message, parsed_message)) + return true; + return DevToolsAgentHostImpl::DispatchProtocolMessage(client, message, + parsed_message); } } // content
diff --git a/content/browser/devtools/browser_devtools_agent_host.h b/content/browser/devtools/browser_devtools_agent_host.h index 7022e2d1..2996348 100644 --- a/content/browser/devtools/browser_devtools_agent_host.h +++ b/content/browser/devtools/browser_devtools_agent_host.h
@@ -5,10 +5,13 @@ #ifndef CONTENT_BROWSER_DEVTOOLS_BROWSER_DEVTOOLS_AGENT_HOST_H_ #define CONTENT_BROWSER_DEVTOOLS_BROWSER_DEVTOOLS_AGENT_HOST_H_ +#include "base/containers/flat_map.h" #include "content/browser/devtools/devtools_agent_host_impl.h" namespace content { +class TargetRegistry; + class BrowserDevToolsAgentHost : public DevToolsAgentHostImpl { private: friend class DevToolsAgentHost; @@ -18,11 +21,13 @@ bool only_discovery); ~BrowserDevToolsAgentHost() override; - // DevToolsAgentHostImpl implementation. - bool AttachSession(DevToolsSession* session) override; + // DevToolsAgentHostImpl overrides. + bool AttachSession(DevToolsSession* session, + TargetRegistry* registry) override; void DetachSession(DevToolsSession* session) override; - void DispatchProtocolMessage(DevToolsSession* session, - const std::string& message) override; + bool DispatchProtocolMessage(DevToolsAgentHostClient* client, + const std::string& message, + base::DictionaryValue* parsed_message) override; // DevToolsAgentHost implementation. std::string GetType() override; @@ -35,6 +40,8 @@ scoped_refptr<base::SingleThreadTaskRunner> tethering_task_runner_; CreateServerSocketCallback socket_callback_; bool only_discovery_; + base::flat_map<DevToolsAgentHostClient*, std::unique_ptr<TargetRegistry>> + target_registries_; }; } // namespace content
diff --git a/content/browser/devtools/devtools_agent_host_impl.cc b/content/browser/devtools/devtools_agent_host_impl.cc index 8d4373c3..273ca40 100644 --- a/content/browser/devtools/devtools_agent_host_impl.cc +++ b/content/browser/devtools/devtools_agent_host_impl.cc
@@ -8,6 +8,7 @@ #include <vector> #include "base/bind.h" +#include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/lazy_instance.h" #include "base/observer_list.h" @@ -176,12 +177,13 @@ } bool DevToolsAgentHostImpl::InnerAttachClient(DevToolsAgentHostClient* client, + TargetRegistry* registry, bool restricted) { scoped_refptr<DevToolsAgentHostImpl> protect(this); DevToolsSession* session = new DevToolsSession(this, client, restricted); sessions_.insert(session); session_by_client_[client].reset(session); - if (!AttachSession(session)) { + if (!AttachSession(session, registry)) { sessions_.erase(session); session_by_client_.erase(client); return false; @@ -198,14 +200,22 @@ void DevToolsAgentHostImpl::AttachClient(DevToolsAgentHostClient* client) { if (SessionByClient(client)) return; - InnerAttachClient(client, false /* restricted */); + InnerAttachClient(client, nullptr, false /* restricted */); +} + +void DevToolsAgentHostImpl::AttachSubtargetClient( + DevToolsAgentHostClient* client, + TargetRegistry* registry) { + if (SessionByClient(client)) + return; + InnerAttachClient(client, registry, false /* restricted */); } bool DevToolsAgentHostImpl::AttachRestrictedClient( DevToolsAgentHostClient* client) { if (SessionByClient(client)) return false; - return InnerAttachClient(client, true /* restricted */); + return InnerAttachClient(client, nullptr, true /* restricted */); } bool DevToolsAgentHostImpl::DetachClient(DevToolsAgentHostClient* client) { @@ -220,16 +230,29 @@ bool DevToolsAgentHostImpl::DispatchProtocolMessage( DevToolsAgentHostClient* client, const std::string& message) { + std::unique_ptr<base::Value> value = base::JSONReader::Read(message); + if (value && !value->is_dict()) + value.reset(); + return DispatchProtocolMessage( + client, message, static_cast<base::DictionaryValue*>(value.get())); +} + +bool DevToolsAgentHostImpl::DispatchProtocolMessage( + DevToolsAgentHostClient* client, + const std::string& message, + base::DictionaryValue* parsed_message) { DevToolsSession* session = SessionByClient(client); if (!session) return false; - DispatchProtocolMessage(session, message); + session->DispatchProtocolMessage(message, parsed_message); return true; } void DevToolsAgentHostImpl::InnerDetachClient(DevToolsAgentHostClient* client) { std::unique_ptr<DevToolsSession> session = std::move(session_by_client_[client]); + // Make sure we dispose session prior to reporting it to the host. + session->Dispose(); sessions_.erase(session.get()); session_by_client_.erase(client); DetachSession(session.get()); @@ -321,16 +344,13 @@ } } -bool DevToolsAgentHostImpl::AttachSession(DevToolsSession* session) { +bool DevToolsAgentHostImpl::AttachSession(DevToolsSession* session, + TargetRegistry* registry) { return false; } void DevToolsAgentHostImpl::DetachSession(DevToolsSession* session) {} -void DevToolsAgentHostImpl::DispatchProtocolMessage( - DevToolsSession* session, - const std::string& message) {} - // static void DevToolsAgentHost::DetachAllClients() { if (!g_devtools_instances.IsCreated())
diff --git a/content/browser/devtools/devtools_agent_host_impl.h b/content/browser/devtools/devtools_agent_host_impl.h index a58af1fa..b258f84 100644 --- a/content/browser/devtools/devtools_agent_host_impl.h +++ b/content/browser/devtools/devtools_agent_host_impl.h
@@ -23,6 +23,7 @@ class BrowserContext; class DevToolsSession; +class TargetRegistry; // Describes interface for managing devtools agents from the browser process. class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost { @@ -65,10 +66,13 @@ static bool ShouldForceCreation(); // Returning |false| will block the attach. - virtual bool AttachSession(DevToolsSession* session); + virtual bool AttachSession(DevToolsSession* session, + TargetRegistry* registry); virtual void DetachSession(DevToolsSession* session); - virtual void DispatchProtocolMessage(DevToolsSession* session, - const std::string& message); + + virtual bool DispatchProtocolMessage(DevToolsAgentHostClient* client, + const std::string& message, + base::DictionaryValue* parsed_message); void NotifyCreated(); void NotifyNavigated(); @@ -81,15 +85,23 @@ base::flat_set<DevToolsSession*>& sessions() { return sessions_; } private: - friend class DevToolsAgentHost; // for static methods + friend class DevToolsAgentHost; // for static methods friend class DevToolsSession; - bool InnerAttachClient(DevToolsAgentHostClient* client, bool restricted); + friend class TargetRegistry; // for subtarget management + + bool InnerAttachClient(DevToolsAgentHostClient* client, + TargetRegistry* registry, + bool restricted); void InnerDetachClient(DevToolsAgentHostClient* client); void NotifyAttached(); void NotifyDetached(); void NotifyDestroyed(); DevToolsSession* SessionByClient(DevToolsAgentHostClient* client); + // TargetRegistry API for subtarget management. + void AttachSubtargetClient(DevToolsAgentHostClient* client, + TargetRegistry* registry); + const std::string id_; base::flat_set<DevToolsSession*> sessions_; base::flat_map<DevToolsAgentHostClient*, std::unique_ptr<DevToolsSession>>
diff --git a/content/browser/devtools/devtools_session.cc b/content/browser/devtools/devtools_session.cc index 10a84a5..a82213af 100644 --- a/content/browser/devtools/devtools_session.cc +++ b/content/browser/devtools/devtools_session.cc
@@ -46,6 +46,13 @@ } DevToolsSession::~DevToolsSession() { + // It is Ok for session to be deleted without the dispose - + // it can be kicked out by an extension connect / disconnect. + if (dispatcher_) + Dispose(); +} + +void DevToolsSession::Dispose() { dispatcher_.reset(); for (auto& pair : handlers_) pair.second->Disable(); @@ -116,22 +123,19 @@ io_session_ptr_.reset(); } -void DevToolsSession::DispatchProtocolMessage(const std::string& message) { - std::unique_ptr<base::Value> value = base::JSONReader::Read(message); - +void DevToolsSession::DispatchProtocolMessage( + const std::string& message, + base::DictionaryValue* parsed_message) { DevToolsManagerDelegate* delegate = DevToolsManager::GetInstance()->delegate(); - if (value && value->is_dict() && delegate) { - base::DictionaryValue* dict_value = - static_cast<base::DictionaryValue*>(value.get()); - - if (delegate->HandleCommand(agent_host_, client_, dict_value)) - return; + if (delegate && parsed_message && + delegate->HandleCommand(agent_host_, client_, parsed_message)) { + return; } int call_id; std::string method; - if (dispatcher_->dispatch(protocol::toProtocolValue(value.get(), 1000), + if (dispatcher_->dispatch(protocol::toProtocolValue(parsed_message, 1000), &call_id, &method) != protocol::Response::kFallThrough) { return;
diff --git a/content/browser/devtools/devtools_session.h b/content/browser/devtools/devtools_session.h index 9ae28ad..2b2bccf 100644 --- a/content/browser/devtools/devtools_session.h +++ b/content/browser/devtools/devtools_session.h
@@ -28,8 +28,10 @@ DevToolsAgentHostClient* client, bool restricted); ~DevToolsSession() override; + void Dispose(); bool restricted() { return restricted_; } + DevToolsAgentHost* agent_host() { return agent_host_; }; DevToolsAgentHostClient* client() { return client_; }; // Browser-only sessions do not talk to mojom::DevToolsAgent, but instead @@ -41,7 +43,8 @@ void SetRenderer(int process_host_id, RenderFrameHostImpl* frame_host); void AttachToAgent(const blink::mojom::DevToolsAgentAssociatedPtr& agent); - void DispatchProtocolMessage(const std::string& message); + void DispatchProtocolMessage(const std::string& message, + base::DictionaryValue* parsed_message); void SuspendSendingMessagesToAgent(); void ResumeSendingMessagesToAgent();
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc index c96429d..4b9a8d2 100644 --- a/content/browser/devtools/protocol/target_handler.cc +++ b/content/browser/devtools/protocol/target_handler.cc
@@ -9,11 +9,13 @@ #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/strings/stringprintf.h" +#include "base/unguessable_token.h" #include "base/values.h" #include "build/build_config.h" #include "content/browser/devtools/browser_devtools_agent_host.h" #include "content/browser/devtools/devtools_manager.h" #include "content/browser/devtools/devtools_session.h" +#include "content/browser/devtools/target_registry.h" #include "content/browser/frame_host/navigation_handle_impl.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/devtools_agent_host_client.h" @@ -231,28 +233,41 @@ public: static std::string Attach(TargetHandler* handler, DevToolsAgentHost* agent_host, - bool waiting_for_debugger) { - std::string id = base::StringPrintf("%s:%d", agent_host->GetId().c_str(), - ++handler->last_session_id_); - Session* session = new Session(handler, agent_host, id); + bool waiting_for_debugger, + bool flatten_protocol) { + std::string id = base::UnguessableToken::Create().ToString(); + Session* session = new Session(handler, agent_host, id, flatten_protocol); handler->attached_sessions_[id].reset(session); - agent_host->AttachClient(session); + DevToolsAgentHostImpl* agent_host_impl = + static_cast<DevToolsAgentHostImpl*>(agent_host); + if (flatten_protocol) { + handler->target_registry_->AttachSubtargetSession(id, agent_host_impl, + session); + } else { + agent_host_impl->AttachClient(session); + } handler->frontend_->AttachedToTarget(id, CreateInfo(agent_host), waiting_for_debugger); return id; } ~Session() override { - if (agent_host_) - agent_host_->DetachClient(this); + if (!agent_host_) + return; + if (handler_->target_registry_) + handler_->target_registry_->DetachSubtargetSession(id_); + agent_host_->DetachClient(this); } void Detach(bool host_closed) { handler_->frontend_->DetachedFromTarget(id_, agent_host_->GetId()); if (host_closed) handler_->auto_attacher_.AgentHostClosed(agent_host_.get()); - else + else { + if (handler_->target_registry_) + handler_->target_registry_->DetachSubtargetSession(id_); agent_host_->DetachClient(this); + } handler_->auto_attached_sessions_.erase(agent_host_.get()); agent_host_ = nullptr; handler_->attached_sessions_.erase(id_); @@ -281,15 +296,26 @@ } private: + friend class TargetHandler; + Session(TargetHandler* handler, DevToolsAgentHost* agent_host, - const std::string& id) - : handler_(handler), agent_host_(agent_host), id_(id) {} + const std::string& id, + bool flatten_protocol) + : handler_(handler), + agent_host_(agent_host), + id_(id), + flatten_protocol_(flatten_protocol) {} // DevToolsAgentHostClient implementation. void DispatchProtocolMessage(DevToolsAgentHost* agent_host, const std::string& message) override { DCHECK(agent_host == agent_host_.get()); + if (flatten_protocol_) { + handler_->target_registry_->SendMessageToClient(id_, message); + return; + } + handler_->frontend_->ReceivedMessageFromTarget(id_, message, agent_host_->GetId()); } @@ -302,6 +328,7 @@ TargetHandler* handler_; scoped_refptr<DevToolsAgentHost> agent_host_; std::string id_; + bool flatten_protocol_; Throttle* throttle_ = nullptr; DISALLOW_COPY_AND_ASSIGN(Session); @@ -366,7 +393,8 @@ } TargetHandler::TargetHandler(bool browser_only, - const std::string& owner_target_id) + const std::string& owner_target_id, + TargetRegistry* target_registry) : DevToolsDomainHandler(Target::Metainfo::domainName), auto_attacher_( base::Bind(&TargetHandler::AutoAttach, base::Unretained(this)), @@ -374,6 +402,7 @@ discover_(false), browser_only_(browser_only), owner_target_id_(owner_target_id), + target_registry_(target_registry), weak_factory_(this) {} TargetHandler::~TargetHandler() { @@ -425,7 +454,8 @@ void TargetHandler::AutoAttach(DevToolsAgentHost* host, bool waiting_for_debugger) { - std::string session_id = Session::Attach(this, host, waiting_for_debugger); + std::string session_id = + Session::Attach(this, host, waiting_for_debugger, false); auto_attached_sessions_[host] = attached_sessions_[session_id].get(); } @@ -501,13 +531,19 @@ } Response TargetHandler::AttachToTarget(const std::string& target_id, + Maybe<bool> flatten, std::string* out_session_id) { // TODO(dgozman): only allow reported hosts. scoped_refptr<DevToolsAgentHost> agent_host = DevToolsAgentHost::GetForId(target_id); if (!agent_host) return Response::InvalidParams("No target with given id found"); - *out_session_id = Session::Attach(this, agent_host.get(), false); + if (flatten.fromMaybe(false) && !target_registry_) { + return Response::InvalidParams( + "Will only provide flatten access for browser endpoint"); + } + *out_session_id = + Session::Attach(this, agent_host.get(), false, flatten.fromMaybe(false)); return Response::OK(); } @@ -530,6 +566,11 @@ FindSession(std::move(session_id), std::move(target_id), &session, true); if (!response.isSuccess()) return response; + if (session->flatten_protocol_) { + return Response::Error( + "When using flat protocol, messages are routed to the target " + "via the sessionId attribute."); + } session->SendMessageToAgentHost(message); return Response::OK(); }
diff --git a/content/browser/devtools/protocol/target_handler.h b/content/browser/devtools/protocol/target_handler.h index 3f68113..3a49945 100644 --- a/content/browser/devtools/protocol/target_handler.h +++ b/content/browser/devtools/protocol/target_handler.h
@@ -21,6 +21,7 @@ class NavigationHandle; class NavigationThrottle; class RenderFrameHostImpl; +class TargetRegistry; namespace protocol { @@ -28,7 +29,9 @@ public Target::Backend, public DevToolsAgentHostObserver { public: - TargetHandler(bool browser_only, const std::string& owner_target_id); + TargetHandler(bool browser_only, + const std::string& owner_target_id, + TargetRegistry* target_registry); ~TargetHandler() override; static std::vector<TargetHandler*> ForAgentHost(DevToolsAgentHostImpl* host); @@ -50,6 +53,7 @@ Response SetRemoteLocations( std::unique_ptr<protocol::Array<Target::RemoteLocation>>) override; Response AttachToTarget(const std::string& target_id, + Maybe<bool> flatten, std::string* out_session_id) override; Response DetachFromTarget(Maybe<std::string> session_id, Maybe<std::string> target_id) override; @@ -106,9 +110,9 @@ std::map<std::string, std::unique_ptr<Session>> attached_sessions_; std::map<DevToolsAgentHost*, Session*> auto_attached_sessions_; std::set<DevToolsAgentHost*> reported_hosts_; - int last_session_id_ = 0; bool browser_only_; std::string owner_target_id_; + TargetRegistry* target_registry_; base::flat_set<Throttle*> throttles_; base::WeakPtrFactory<TargetHandler> weak_factory_;
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc index 85377848..db70cad 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.cc +++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -426,7 +426,8 @@ return web_contents(); } -bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) { +bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session, + TargetRegistry* registry) { const bool is_webui = frame_host_ && (frame_host_->web_ui() || frame_host_->pending_web_ui()); if (!session->client()->MayAttachToRenderer(frame_host_, is_webui)) @@ -455,8 +456,8 @@ session->AddHandler(base::WrapUnique(new protocol::ServiceWorkerHandler())); session->AddHandler(base::WrapUnique(new protocol::StorageHandler())); if (!session->restricted()) { - session->AddHandler(base::WrapUnique( - new protocol::TargetHandler(false /* browser_only */, GetId()))); + session->AddHandler(base::WrapUnique(new protocol::TargetHandler( + false /* browser_only */, GetId(), registry))); } session->AddHandler( base::WrapUnique(new protocol::PageHandler(emulation_handler))); @@ -495,12 +496,6 @@ } } -void RenderFrameDevToolsAgentHost::DispatchProtocolMessage( - DevToolsSession* session, - const std::string& message) { - session->DispatchProtocolMessage(message); -} - void RenderFrameDevToolsAgentHost::InspectElement(RenderFrameHost* frame_host, int x, int y) {
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h index bfc4b0ab..b4184bb 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.h +++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -161,11 +161,10 @@ ~RenderFrameDevToolsAgentHost() override; // DevToolsAgentHostImpl overrides. - bool AttachSession(DevToolsSession* session) override; + bool AttachSession(DevToolsSession* session, + TargetRegistry* registry) override; void DetachSession(DevToolsSession* session) override; void InspectElement(RenderFrameHost* frame_host, int x, int y) override; - void DispatchProtocolMessage(DevToolsSession* session, - const std::string& message) override; // WebContentsObserver overrides. void DidStartNavigation(NavigationHandle* navigation_handle) override;
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.cc b/content/browser/devtools/service_worker_devtools_agent_host.cc index c78e468c..32f191e7 100644 --- a/content/browser/devtools/service_worker_devtools_agent_host.cc +++ b/content/browser/devtools/service_worker_devtools_agent_host.cc
@@ -115,7 +115,8 @@ ServiceWorkerDevToolsManager::GetInstance()->AgentHostDestroyed(this); } -bool ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) { +bool ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session, + TargetRegistry* registry) { if (state_ == WORKER_READY) { if (sessions().size() == 1) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, @@ -141,12 +142,6 @@ } } -void ServiceWorkerDevToolsAgentHost::DispatchProtocolMessage( - DevToolsSession* session, - const std::string& message) { - session->DispatchProtocolMessage(message); -} - void ServiceWorkerDevToolsAgentHost::WorkerReadyForInspection( blink::mojom::DevToolsAgentAssociatedPtrInfo devtools_agent_ptr_info) { DCHECK_EQ(WORKER_NOT_READY, state_);
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.h b/content/browser/devtools/service_worker_devtools_agent_host.h index 453f52c..b261209 100644 --- a/content/browser/devtools/service_worker_devtools_agent_host.h +++ b/content/browser/devtools/service_worker_devtools_agent_host.h
@@ -47,10 +47,9 @@ bool Close() override; // DevToolsAgentHostImpl overrides. - bool AttachSession(DevToolsSession* session) override; + bool AttachSession(DevToolsSession* session, + TargetRegistry* registry) override; void DetachSession(DevToolsSession* session) override; - void DispatchProtocolMessage(DevToolsSession* session, - const std::string& message) override; void WorkerRestarted(int worker_process_id, int worker_route_id); void WorkerReadyForInspection(
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.cc b/content/browser/devtools/shared_worker_devtools_agent_host.cc index 12c913b..c09e04a 100644 --- a/content/browser/devtools/shared_worker_devtools_agent_host.cc +++ b/content/browser/devtools/shared_worker_devtools_agent_host.cc
@@ -66,7 +66,8 @@ return true; } -bool SharedWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) { +bool SharedWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session, + TargetRegistry* registry) { session->AddHandler(std::make_unique<protocol::InspectorHandler>()); session->AddHandler(std::make_unique<protocol::NetworkHandler>( GetId(), devtools_worker_token_, GetIOContext())); @@ -81,12 +82,6 @@ // Destroying session automatically detaches in renderer. } -void SharedWorkerDevToolsAgentHost::DispatchProtocolMessage( - DevToolsSession* session, - const std::string& message) { - session->DispatchProtocolMessage(message); -} - bool SharedWorkerDevToolsAgentHost::Matches(SharedWorkerHost* worker_host) { return instance_->Matches(*worker_host->instance()); }
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.h b/content/browser/devtools/shared_worker_devtools_agent_host.h index 6a1d8007..c0a867de 100644 --- a/content/browser/devtools/shared_worker_devtools_agent_host.h +++ b/content/browser/devtools/shared_worker_devtools_agent_host.h
@@ -33,10 +33,9 @@ bool Close() override; // DevToolsAgentHostImpl overrides. - bool AttachSession(DevToolsSession* session) override; + bool AttachSession(DevToolsSession* session, + TargetRegistry* registry) override; void DetachSession(DevToolsSession* session) override; - void DispatchProtocolMessage(DevToolsSession* session, - const std::string& message) override; bool Matches(SharedWorkerHost* worker_host); void WorkerReadyForInspection();
diff --git a/content/browser/devtools/target_registry.cc b/content/browser/devtools/target_registry.cc new file mode 100644 index 0000000..07d168a --- /dev/null +++ b/content/browser/devtools/target_registry.cc
@@ -0,0 +1,56 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/devtools/target_registry.h" + +#include "base/strings/stringprintf.h" +#include "content/browser/devtools/devtools_session.h" + +namespace content { + +TargetRegistry::TargetRegistry(DevToolsSession* root_session) + : root_session_(root_session) {} + +TargetRegistry::~TargetRegistry() {} + +void TargetRegistry::AttachSubtargetSession(const std::string& session_id, + DevToolsAgentHostImpl* agent_host, + DevToolsAgentHostClient* client) { + sessions_[session_id] = std::make_pair(agent_host, client); + agent_host->AttachSubtargetClient(client, this); +} +void TargetRegistry::DetachSubtargetSession(const std::string& session_id) { + sessions_.erase(session_id); +} + +bool TargetRegistry::DispatchMessageOnAgentHost( + const std::string& message, + base::DictionaryValue* parsed_message) { + std::string session_id; + if (!parsed_message->GetString("sessionId", &session_id)) + return false; + auto it = sessions_.find(session_id); + if (it == sessions_.end()) { + LOG(ERROR) << "Unknown session " << session_id; + return true; + } + scoped_refptr<DevToolsAgentHostImpl> agent_host = it->second.first; + DevToolsAgentHostClient* client = it->second.second; + return agent_host->DispatchProtocolMessage(client, message, parsed_message); +} + +void TargetRegistry::SendMessageToClient(const std::string& session_id, + const std::string& message) { + DCHECK(message[message.length() - 1] == '}'); + std::string suffix = + base::StringPrintf(", \"sessionId\": \"%s\"}", session_id.c_str()); + std::string patched; + patched.reserve(message.length() + suffix.length() - 1); + patched.append(message.data(), message.length() - 1); + patched.append(suffix); + root_session_->client()->DispatchProtocolMessage(root_session_->agent_host(), + patched); +} + +} // namespace content
diff --git a/content/browser/devtools/target_registry.h b/content/browser/devtools/target_registry.h new file mode 100644 index 0000000..d915e1f --- /dev/null +++ b/content/browser/devtools/target_registry.h
@@ -0,0 +1,44 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DEVTOOLS_TARGET_REGISTRY_H_ +#define CONTENT_BROWSER_DEVTOOLS_TARGET_REGISTRY_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "base/containers/flat_map.h" +#include "base/values.h" +#include "content/browser/devtools/devtools_agent_host_impl.h" + +namespace content { + +class DevToolsSession; + +class TargetRegistry { + public: + explicit TargetRegistry(DevToolsSession* root_session); + ~TargetRegistry(); + + void AttachSubtargetSession(const std::string& session_id, + DevToolsAgentHostImpl* agent_host, + DevToolsAgentHostClient* client); + void DetachSubtargetSession(const std::string& session_id); + bool DispatchMessageOnAgentHost(const std::string& message, + base::DictionaryValue* parsed_message); + void SendMessageToClient(const std::string& session_id, + const std::string& message); + + private: + DevToolsSession* root_session_; + base::flat_map< + std::string, + std::pair<scoped_refptr<DevToolsAgentHostImpl>, DevToolsAgentHostClient*>> + sessions_; + DISALLOW_COPY_AND_ASSIGN(TargetRegistry); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVTOOLS_TARGET_REGISTRY_H_
diff --git a/content/browser/indexed_db/indexed_db_pre_close_task_queue_unittest.cc b/content/browser/indexed_db/indexed_db_pre_close_task_queue_unittest.cc index 138b8d1..48bf2ed 100644 --- a/content/browser/indexed_db/indexed_db_pre_close_task_queue_unittest.cc +++ b/content/browser/indexed_db/indexed_db_pre_close_task_queue_unittest.cc
@@ -80,7 +80,7 @@ bool done_called = false; bool metadata_called = false; - base::MockTimer* fake_timer = new base::MockTimer(true, false); + base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer; IndexedDBPreCloseTaskQueue queue( std::list<std::unique_ptr<PreCloseTask>>(), base::BindOnce(&SetBoolValue, &done_called, true), kTestMaxRunTime, @@ -104,7 +104,7 @@ EXPECT_CALL(*task, SetMetadata(testing::Pointee(testing::ContainerEq(metadata_)))); - base::MockTimer* fake_timer = new base::MockTimer(true, false); + base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer; std::list<std::unique_ptr<PreCloseTask>> tasks; tasks.push_back(base::WrapUnique(task)); IndexedDBPreCloseTaskQueue queue( @@ -134,7 +134,7 @@ EXPECT_CALL(*task, SetMetadata(testing::Pointee(testing::ContainerEq(metadata_)))); - base::MockTimer* fake_timer = new base::MockTimer(true, false); + base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer; std::list<std::unique_ptr<PreCloseTask>> tasks; tasks.push_back(base::WrapUnique(task)); IndexedDBPreCloseTaskQueue queue( @@ -178,7 +178,7 @@ EXPECT_CALL(*task1, SetMetadata(testing::Pointee(testing::ContainerEq(metadata_)))); - base::MockTimer* fake_timer = new base::MockTimer(true, false); + base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer; std::list<std::unique_ptr<PreCloseTask>> tasks; tasks.push_back(base::WrapUnique(task1)); tasks.push_back(base::WrapUnique(task2)); @@ -227,7 +227,7 @@ EXPECT_CALL(*task1, SetMetadata(testing::Pointee(testing::ContainerEq(metadata_)))); - base::MockTimer* fake_timer = new base::MockTimer(true, false); + base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer; std::list<std::unique_ptr<PreCloseTask>> tasks; tasks.push_back(base::WrapUnique(task1)); tasks.push_back(base::WrapUnique(task2)); @@ -260,7 +260,7 @@ EXPECT_CALL(*task, SetMetadata(testing::Pointee(testing::ContainerEq(metadata_)))); - base::MockTimer* fake_timer = new base::MockTimer(true, false); + base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer; std::list<std::unique_ptr<PreCloseTask>> tasks; tasks.push_back(base::WrapUnique(task)); IndexedDBPreCloseTaskQueue queue( @@ -301,7 +301,7 @@ EXPECT_CALL(*task1, SetMetadata(testing::Pointee(testing::ContainerEq(metadata_)))); - base::MockTimer* fake_timer = new base::MockTimer(true, false); + base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer; std::list<std::unique_ptr<PreCloseTask>> tasks; tasks.push_back(base::WrapUnique(task1)); tasks.push_back(base::WrapUnique(task2)); @@ -345,7 +345,7 @@ EXPECT_CALL(*task1, SetMetadata(testing::Pointee(testing::ContainerEq(metadata_)))); - base::MockTimer* fake_timer = new base::MockTimer(true, false); + base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer; std::list<std::unique_ptr<PreCloseTask>> tasks; tasks.push_back(base::WrapUnique(task1)); tasks.push_back(base::WrapUnique(task2)); @@ -387,7 +387,7 @@ MockPreCloseTask* task1 = new testing::StrictMock<MockPreCloseTask>(); MockPreCloseTask* task2 = new testing::StrictMock<MockPreCloseTask>(); - base::MockTimer* fake_timer = new base::MockTimer(true, false); + base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer; std::list<std::unique_ptr<PreCloseTask>> tasks; tasks.push_back(base::WrapUnique(task1)); tasks.push_back(base::WrapUnique(task2));
diff --git a/content/browser/linux_ipc_browsertest.cc b/content/browser/linux_ipc_browsertest.cc deleted file mode 100644 index 7be0589..0000000 --- a/content/browser/linux_ipc_browsertest.cc +++ /dev/null
@@ -1,91 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <map> -#include <set> -#include <string> - -#include "base/command_line.h" -#include "base/macros.h" -#include "base/synchronization/lock.h" -#include "content/browser/sandbox_ipc_linux.h" -#include "content/public/common/content_switches.h" -#include "content/public/test/content_browser_test.h" -#include "content/public/test/content_browser_test_utils.h" -#include "services/service_manager/sandbox/switches.h" -#include "testing/gmock/include/gmock/gmock-matchers.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -class LinuxIPCBrowserTest : public ContentBrowserTest, - public SandboxIPCHandler::TestObserver, - public testing::WithParamInterface<std::string> { - public: - LinuxIPCBrowserTest() { - SandboxIPCHandler::SetObserverForTests(this); - } - ~LinuxIPCBrowserTest() override {} - - protected: - void SetUpCommandLine(base::CommandLine* command_line) override { - ContentBrowserTest::SetUpCommandLine(command_line); - if (GetParam() == "no-zygote") { - command_line->AppendSwitch(service_manager::switches::kNoSandbox); - command_line->AppendSwitch(switches::kNoZygote); - } - } - - void OnFontOpen(int id) override { - base::AutoLock lock(lock_); - opened_fonts_.insert(font_names_[id]); - } - - void OnGetFallbackFontForChar(UChar32 c, std::string name, int id) override { - base::AutoLock lock(lock_); - fallback_fonts_[c] = name; - font_names_[id] = name; - } - - std::string FallbackFontName(UChar32 c) { - base::AutoLock lock(lock_); - return fallback_fonts_[c]; - } - - std::set<std::string> OpenedFonts() { - base::AutoLock lock(lock_); - return opened_fonts_; - } - - // These variables are accessed on the IPC thread and the test thread. - // All accesses on the IPC thread should be before the renderer process - // completes navigation, and all accesses on the test thread should be after. - // However we still need a mutex for the accesses to be sequenced according to - // the C++ memory model. - base::Lock lock_; - std::map<UChar32, std::string> fallback_fonts_; - std::map<int, std::string> font_names_; - std::set<std::string> opened_fonts_; - - DISALLOW_COPY_AND_ASSIGN(LinuxIPCBrowserTest); -}; - -// Tests that Linux IPC font fallback code runs round-trip when Zygote is -// disabled. It doesn't care what font is chosen, just that the IPC messages are -// flowing. This test assumes that U+65E5 (CJK "Sun" character) will trigger the -// font fallback codepath. -IN_PROC_BROWSER_TEST_P(LinuxIPCBrowserTest, FontFallbackIPCWorks) { - GURL test_url = GetTestUrl("font", "font_fallback.html"); - EXPECT_TRUE(NavigateToURL(shell(), test_url)); - EXPECT_THAT(FallbackFontName(U'\U000065E5'), testing::Ne("")); - EXPECT_THAT(OpenedFonts(), - testing::Contains(FallbackFontName(U'\U000065E5'))); -} - -INSTANTIATE_TEST_CASE_P(LinuxIPCBrowserTest, - LinuxIPCBrowserTest, - ::testing::Values("zygote", "no-zygote")); - -} // namespace
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc index fa74eb01..d8eeafef 100644 --- a/content/browser/loader/navigation_url_loader_impl.cc +++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -14,6 +14,7 @@ #include "base/trace_event/trace_event.h" #include "components/download/public/common/download_stats.h" #include "content/browser/appcache/appcache_navigation_handle.h" +#include "content/browser/appcache/appcache_navigation_handle_core.h" #include "content/browser/appcache/appcache_request_handler.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/devtools/render_frame_devtools_agent_host.h" @@ -358,6 +359,9 @@ ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core, AppCacheNavigationHandleCore* appcache_handle_core, bool was_request_intercepted) const { + if (appcache_handle_core) + appcache_handle_core->AddDefaultFactoryRunToDebugLog( + was_request_intercepted); return base::BindOnce( &URLLoaderRequestController::CreateNonNetworkServiceURLLoader, weak_factory_.GetWeakPtr(), @@ -388,6 +392,9 @@ DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (appcache_handle_core) + appcache_handle_core->AddCreateURLLoaderToDebugLog(); + default_loader_used_ = true; if (signed_exchange_utils::IsSignedExchangeHandlingEnabled()) { DCHECK(!default_url_loader_factory_getter_); @@ -452,6 +459,9 @@ DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(!started_); + if (appcache_handle_core) + appcache_handle_core->AddNavigationStartToDebugLog(); + started_ = true; request_info_ = std::move(request_info); frame_tree_node_id_ = request_info_->frame_tree_node_id;
diff --git a/content/browser/renderer_host/font_utils_linux.cc b/content/browser/renderer_host/font_utils_linux.cc deleted file mode 100644 index e5fd81a..0000000 --- a/content/browser/renderer_host/font_utils_linux.cc +++ /dev/null
@@ -1,265 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <fcntl.h> -#include <fontconfig/fontconfig.h> -#include <stddef.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <string> - -#include "base/posix/eintr_wrapper.h" - -// TODO(crbug/685022): Guard the inclusion of ppapi headers with -// BUILDFLAG(ENABLE_PLUGINS). -#include "ppapi/c/private/pp_private_font_charset.h" // nogncheck -#include "ppapi/c/trusted/ppb_browser_font_trusted.h" // nogncheck - -namespace { - -// MSCharSetToFontconfig translates a Microsoft charset identifier to a -// fontconfig language set by appending to |langset|. -// Returns true if |langset| is Latin/Greek/Cyrillic. -bool MSCharSetToFontconfig(FcLangSet* langset, unsigned fdwCharSet) { - // We have need to translate raw fdwCharSet values into terms that - // fontconfig can understand. (See the description of fdwCharSet in the MSDN - // documentation for CreateFont: - // http://msdn.microsoft.com/en-us/library/dd183499(VS.85).aspx ) - // - // Although the argument is /called/ 'charset', the actual values conflate - // character sets (which are sets of Unicode code points) and character - // encodings (which are algorithms for turning a series of bits into a - // series of code points.) Sometimes the values will name a language, - // sometimes they'll name an encoding. In the latter case I'm assuming that - // they mean the set of code points in the domain of that encoding. - // - // fontconfig deals with ISO 639-1 language codes: - // http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes - // - // So, for each of the documented fdwCharSet values I've had to take a - // guess at the set of ISO 639-1 languages intended. - - bool is_lgc = false; - switch (fdwCharSet) { - case PP_PRIVATEFONTCHARSET_ANSI: - // These values I don't really know what to do with, so I'm going to map - // them to English also. - case PP_PRIVATEFONTCHARSET_DEFAULT: - case PP_PRIVATEFONTCHARSET_MAC: - case PP_PRIVATEFONTCHARSET_OEM: - case PP_PRIVATEFONTCHARSET_SYMBOL: - is_lgc = true; - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("en")); - break; - case PP_PRIVATEFONTCHARSET_BALTIC: - // The three baltic languages. - is_lgc = true; - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("et")); - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lv")); - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lt")); - break; - case PP_PRIVATEFONTCHARSET_CHINESEBIG5: - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-tw")); - break; - case PP_PRIVATEFONTCHARSET_GB2312: - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-cn")); - break; - case PP_PRIVATEFONTCHARSET_EASTEUROPE: - // A scattering of eastern European languages. - is_lgc = true; - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("pl")); - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("cs")); - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("sk")); - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hu")); - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hr")); - break; - case PP_PRIVATEFONTCHARSET_GREEK: - is_lgc = true; - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("el")); - break; - case PP_PRIVATEFONTCHARSET_HANGUL: - case PP_PRIVATEFONTCHARSET_JOHAB: - // Korean - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ko")); - break; - case PP_PRIVATEFONTCHARSET_RUSSIAN: - is_lgc = true; - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ru")); - break; - case PP_PRIVATEFONTCHARSET_SHIFTJIS: - // Japanese - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja")); - break; - case PP_PRIVATEFONTCHARSET_TURKISH: - is_lgc = true; - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("tr")); - break; - case PP_PRIVATEFONTCHARSET_VIETNAMESE: - is_lgc = true; - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("vi")); - break; - case PP_PRIVATEFONTCHARSET_ARABIC: - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ar")); - break; - case PP_PRIVATEFONTCHARSET_HEBREW: - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("he")); - break; - case PP_PRIVATEFONTCHARSET_THAI: - FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("th")); - break; - // default: - // Don't add any languages in that case that we don't recognise the - // constant. - } - return is_lgc; -} - -} // namespace - -namespace content { - -int MatchFontFaceWithFallback(const std::string& face, - bool is_bold, - bool is_italic, - uint32_t charset, - uint32_t fallback_family) { - FcLangSet* langset = FcLangSetCreate(); - bool is_lgc = MSCharSetToFontconfig(langset, charset); - FcPattern* pattern = FcPatternCreate(); - FcPatternAddString( - pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(face.c_str())); - - // TODO(thestig) Check if we can access Chrome's per-script font preference - // here and select better default fonts for non-LGC case. - std::string generic_font_name; - if (is_lgc) { - switch (fallback_family) { - case PP_BROWSERFONT_TRUSTED_FAMILY_SERIF: - generic_font_name = "Times New Roman"; - break; - case PP_BROWSERFONT_TRUSTED_FAMILY_SANSSERIF: - generic_font_name = "Arial"; - break; - case PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE: - generic_font_name = "Courier New"; - break; - } - } - if (!generic_font_name.empty()) { - const FcChar8* fc_generic_font_name = - reinterpret_cast<const FcChar8*>(generic_font_name.c_str()); - FcPatternAddString(pattern, FC_FAMILY, fc_generic_font_name); - } - - if (is_bold) - FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); - if (is_italic) - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); - FcPatternAddLangSet(pattern, FC_LANG, langset); - FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); - FcConfigSubstitute(nullptr, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - FcResult result; - FcFontSet* font_set = FcFontSort(nullptr, pattern, 0, nullptr, &result); - int font_fd = -1; - int good_enough_index = -1; - bool good_enough_index_set = false; - - if (font_set) { - for (int i = 0; i < font_set->nfont; ++i) { - FcPattern* current = font_set->fonts[i]; - - // Older versions of fontconfig have a bug where they cannot select - // only scalable fonts so we have to manually filter the results. - FcBool is_scalable; - if (FcPatternGetBool(current, FC_SCALABLE, 0, &is_scalable) != - FcResultMatch || - !is_scalable) { - continue; - } - - FcChar8* c_filename; - if (FcPatternGetString(current, FC_FILE, 0, &c_filename) != - FcResultMatch) { - continue; - } - - // We only want to return sfnt (TrueType) based fonts. We don't have a - // very good way of detecting this so we'll filter based on the - // filename. - bool is_sfnt = false; - static const char kSFNTExtensions[][5] = {".ttf", ".otc", ".TTF", ".ttc", - ""}; - const size_t filename_len = strlen(reinterpret_cast<char*>(c_filename)); - for (unsigned j = 0;; j++) { - if (kSFNTExtensions[j][0] == 0) { - // None of the extensions matched. - break; - } - const size_t ext_len = strlen(kSFNTExtensions[j]); - if (filename_len > ext_len && - memcmp(c_filename + filename_len - ext_len, - kSFNTExtensions[j], - ext_len) == 0) { - is_sfnt = true; - break; - } - } - - if (!is_sfnt) - continue; - - // This font is good enough to pass muster, but we might be able to do - // better with subsequent ones. - if (!good_enough_index_set) { - good_enough_index = i; - good_enough_index_set = true; - } - - FcValue matrix; - bool have_matrix = FcPatternGet(current, FC_MATRIX, 0, &matrix) == 0; - - if (is_italic && have_matrix) { - // we asked for an italic font, but fontconfig is giving us a - // non-italic font with a transformation matrix. - continue; - } - - FcValue embolden; - const bool have_embolden = - FcPatternGet(current, FC_EMBOLDEN, 0, &embolden) == 0; - - if (is_bold && have_embolden) { - // we asked for a bold font, but fontconfig gave us a non-bold font - // and asked us to apply fake bolding. - continue; - } - - font_fd = - HANDLE_EINTR(open(reinterpret_cast<char*>(c_filename), O_RDONLY)); - if (font_fd >= 0) - break; - } - } - - if (font_fd == -1 && good_enough_index_set) { - // We didn't find a font that we liked, so we fallback to something - // acceptable. - FcPattern* current = font_set->fonts[good_enough_index]; - FcChar8* c_filename; - FcPatternGetString(current, FC_FILE, 0, &c_filename); - font_fd = HANDLE_EINTR(open(reinterpret_cast<char*>(c_filename), O_RDONLY)); - } - - if (font_set) - FcFontSetDestroy(font_set); - FcPatternDestroy(pattern); - - return font_fd; -} - -} // namespace content
diff --git a/content/browser/renderer_host/font_utils_linux.h b/content/browser/renderer_host/font_utils_linux.h deleted file mode 100644 index 33b31af6..0000000 --- a/content/browser/renderer_host/font_utils_linux.h +++ /dev/null
@@ -1,22 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_RENDERER_HOST_FONT_UTILS_LINUX_H_ -#define CONTENT_BROWSER_RENDERER_HOST_FONT_UTILS_LINUX_H_ - -#include <stdint.h> - -#include <string> - -namespace content { - -int MatchFontFaceWithFallback(const std::string& face, - bool is_bold, - bool is_italic, - uint32_t charset, - uint32_t fallback_family); - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_FONT_UTILS_LINUX_H_
diff --git a/content/browser/renderer_host/pepper/pepper_truetype_font_linux.cc b/content/browser/renderer_host/pepper/pepper_truetype_font_linux.cc index c9a8d3f..c71954e 100644 --- a/content/browser/renderer_host/pepper/pepper_truetype_font_linux.cc +++ b/content/browser/renderer_host/pepper/pepper_truetype_font_linux.cc
@@ -14,8 +14,10 @@ #include "base/macros.h" #include "base/numerics/safe_conversions.h" #include "base/sys_byteorder.h" -#include "content/browser/renderer_host/font_utils_linux.h" +#include "build/build_config.h" +#include "components/services/font/ppapi_fontconfig_matching.h" #include "content/public/common/common_sandbox_support_linux.h" +#include "ppapi/buildflags/buildflags.h" #include "ppapi/c/dev/ppb_truetype_font_dev.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/trusted/ppb_browser_font_trusted.h" @@ -74,12 +76,10 @@ } } - fd_.reset( - MatchFontFaceWithFallback(desc->family, - desc->weight >= PP_TRUETYPEFONTWEIGHT_BOLD, - desc->style & PP_TRUETYPEFONTSTYLE_ITALIC, - desc->charset, - PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT)); + fd_.reset(font_service::MatchFontFaceWithFallback( + desc->family, desc->weight >= PP_TRUETYPEFONTWEIGHT_BOLD, + desc->style & PP_TRUETYPEFONTSTYLE_ITALIC, desc->charset, + PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT)); // TODO(bbudge) Modify content API to return results of font matching and // fallback, so we can update |desc| to reflect that. return fd_.is_valid() ? PP_OK : PP_ERROR_FAILED;
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h index 982ae3e1..582d8037 100644 --- a/content/browser/renderer_host/render_process_host_impl.h +++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -218,6 +218,8 @@ bool HostHasNotBeenUsed() override; void LockToOrigin(const GURL& lock_url) override; + void BindCacheStorage(blink::mojom::CacheStorageRequest request, + const url::Origin& origin) override; mojom::RouteProvider* GetRemoteRouteProvider(); @@ -405,11 +407,6 @@ bool is_initialized() const { return is_initialized_; } - // Binds Mojo request to Mojo implementation CacheStorageDispatcherHost - // instance, binding is sent to IO thread. - void BindCacheStorage(blink::mojom::CacheStorageRequest request, - const url::Origin& origin); - // Ensures that this process is kept alive for the specified amount of time. // This is used to ensure that unload handlers have a chance to execute // before the process shuts down.
diff --git a/content/browser/sandbox_ipc_linux.cc b/content/browser/sandbox_ipc_linux.cc index c37d0a53..a893d1b 100644 --- a/content/browser/sandbox_ipc_linux.cc +++ b/content/browser/sandbox_ipc_linux.cc
@@ -21,69 +21,15 @@ #include "base/posix/unix_domain_socket.h" #include "base/process/launch.h" #include "base/strings/string_number_conversions.h" -#include "content/browser/renderer_host/font_utils_linux.h" -#include "content/common/font_config_ipc_linux.h" #include "content/public/common/content_switches.h" #include "sandbox/linux/services/libc_interceptor.h" #include "services/service_manager/sandbox/linux/sandbox_linux.h" -#include "skia/ext/skia_utils_base.h" -#include "third_party/skia/include/ports/SkFontConfigInterface.h" -#include "ui/gfx/font.h" -#include "ui/gfx/font_fallback_linux.h" -#include "ui/gfx/font_render_params.h" namespace content { -namespace { - -SandboxIPCHandler::TestObserver* g_test_observer = nullptr; - -// Converts gfx::FontRenderParams::Hinting to WebFontRenderStyle::hintStyle. -// Returns an int for serialization, but the underlying Blink type is a char. -int ConvertHinting(gfx::FontRenderParams::Hinting hinting) { - switch (hinting) { - case gfx::FontRenderParams::HINTING_NONE: - return 0; - case gfx::FontRenderParams::HINTING_SLIGHT: - return 1; - case gfx::FontRenderParams::HINTING_MEDIUM: - return 2; - case gfx::FontRenderParams::HINTING_FULL: - return 3; - } - NOTREACHED() << "Unexpected hinting value " << hinting; - return 0; -} - -// Converts gfx::FontRenderParams::SubpixelRendering to -// WebFontRenderStyle::useSubpixelRendering. Returns an int for serialization, -// but the underlying Blink type is a char. -int ConvertSubpixelRendering( - gfx::FontRenderParams::SubpixelRendering rendering) { - switch (rendering) { - case gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE: - return 0; - case gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB: - return 1; - case gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR: - return 1; - case gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB: - return 1; - case gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR: - return 1; - } - NOTREACHED() << "Unexpected subpixel rendering value " << rendering; - return 0; -} - -} // namespace +const size_t kMaxSandboxIPCMessagePayloadSize = 64; // static -void SandboxIPCHandler::SetObserverForTests( - SandboxIPCHandler::TestObserver* observer) { - g_test_observer = observer; -} - SandboxIPCHandler::SandboxIPCHandler(int lifeline_fd, int browser_socket) : lifeline_fd_(lifeline_fd), browser_socket_(browser_socket) {} @@ -136,17 +82,22 @@ // A FontConfigIPC::METHOD_MATCH message could be kMaxFontFamilyLength // bytes long (this is the largest message type). + // The size limit used to be FontConfigIPC::kMaxFontFamilyLength which was + // 2048, but we do not receive FontConfig IPC here anymore. The only payloads + // here are service_manager::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT + // and HandleLocalTime from libc_interceptor for which + // kMaxSandboxIPCMessagePayloadSize set to 64 should be plenty. // 128 bytes padding are necessary so recvmsg() does not return MSG_TRUNC // error for a maximum length message. - char buf[FontConfigIPC::kMaxFontFamilyLength + 128]; + char buf[kMaxSandboxIPCMessagePayloadSize + 128]; const ssize_t len = base::UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds); if (len == -1) { // TODO: should send an error reply, or the sender might block forever. if (errno == EMSGSIZE) { - NOTREACHED() - << "Sandbox host message is larger than kMaxFontFamilyLength"; + NOTREACHED() << "Sandbox host message is larger than " + "kMaxSandboxIPCMessagePayloadSize"; } else { PLOG(ERROR) << "Recvmsg failed"; NOTREACHED(); @@ -168,167 +119,12 @@ if (sandbox::HandleInterceptedCall(kind, fd, iter, fds)) return; - if (kind == FontConfigIPC::METHOD_MATCH) { - HandleFontMatchRequest(fd, iter, fds); - } else if (kind == FontConfigIPC::METHOD_OPEN) { - HandleFontOpenRequest(fd, iter, fds); - } else if (kind == - service_manager::SandboxLinux::METHOD_GET_FALLBACK_FONT_FOR_CHAR) { - HandleGetFallbackFontForChar(fd, iter, fds); - } else if (kind == - service_manager::SandboxLinux::METHOD_GET_STYLE_FOR_STRIKE) { - HandleGetStyleForStrike(fd, iter, fds); - } else if (kind == - service_manager::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT) { + if (kind == + service_manager::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT) { HandleMakeSharedMemorySegment(fd, iter, fds); - } else if (kind == - service_manager::SandboxLinux::METHOD_MATCH_WITH_FALLBACK) { - HandleMatchWithFallback(fd, iter, fds); - } -} - -int SandboxIPCHandler::FindOrAddPath(const SkString& path) { - int count = paths_.size(); - for (int i = 0; i < count; ++i) { - if (path == paths_[i]) - return i; - } - paths_.emplace_back(path); - return count; -} - -void SandboxIPCHandler::HandleFontMatchRequest( - int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds) { - SkFontStyle requested_style; - std::string family; - if (!iter.ReadString(&family) || - !skia::ReadSkFontStyle(&iter, &requested_style)) - return; - - SkFontConfigInterface::FontIdentity result_identity; - SkString result_family; - SkFontStyle result_style; - SkFontConfigInterface* fc = - SkFontConfigInterface::GetSingletonDirectInterface(); - const bool r = - fc->matchFamilyName(family.c_str(), requested_style, &result_identity, - &result_family, &result_style); - - base::Pickle reply; - if (!r) { - reply.WriteBool(false); - } else { - // Stash away the returned path, so we can give it an ID (index) - // which will later be given to us in a request to open the file. - int index = FindOrAddPath(result_identity.fString); - result_identity.fID = static_cast<uint32_t>(index); - - reply.WriteBool(true); - skia::WriteSkString(&reply, result_family); - skia::WriteSkFontIdentity(&reply, result_identity); - skia::WriteSkFontStyle(&reply, result_style); - } - SendRendererReply(fds, reply, -1); -} - -void SandboxIPCHandler::HandleFontOpenRequest( - int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds) { - uint32_t index; - if (!iter.ReadUInt32(&index)) - return; - if (index >= static_cast<uint32_t>(paths_.size())) - return; - if (g_test_observer) { - g_test_observer->OnFontOpen(index); - } - const int result_fd = open(paths_[index].c_str(), O_RDONLY); - - base::Pickle reply; - reply.WriteBool(result_fd != -1); - - // The receiver will have its own access to the file, so we will close it - // after this send. - SendRendererReply(fds, reply, result_fd); - - if (result_fd >= 0) { - int err = IGNORE_EINTR(close(result_fd)); - DCHECK(!err); - } -} - -void SandboxIPCHandler::HandleGetFallbackFontForChar( - int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds) { - // The other side of this call is - // content/common/child_process_sandbox_support_impl_linux.cc - - UChar32 c; - if (!iter.ReadInt(&c)) - return; - - std::string preferred_locale; - if (!iter.ReadString(&preferred_locale)) - return; - - auto fallback_font = gfx::GetFallbackFontForChar(c, preferred_locale); - int fontconfig_interface_id = - FindOrAddPath(SkString(fallback_font.filename.data())); - - if (g_test_observer) { - g_test_observer->OnGetFallbackFontForChar(c, fallback_font.name, - fontconfig_interface_id); - } - base::Pickle reply; - reply.WriteString(fallback_font.name); - reply.WriteString(fallback_font.filename); - reply.WriteInt(fontconfig_interface_id); - reply.WriteInt(fallback_font.ttc_index); - reply.WriteBool(fallback_font.is_bold); - reply.WriteBool(fallback_font.is_italic); - SendRendererReply(fds, reply, -1); -} - -void SandboxIPCHandler::HandleGetStyleForStrike( - int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds) { - std::string family; - bool bold; - bool italic; - uint16_t pixel_size; - float device_scale_factor; - - if (!iter.ReadString(&family) || !iter.ReadBool(&bold) || - !iter.ReadBool(&italic) || !iter.ReadUInt16(&pixel_size) || - !iter.ReadFloat(&device_scale_factor)) { return; } - - gfx::FontRenderParamsQuery query; - query.families.push_back(family); - query.pixel_size = pixel_size; - query.style = italic ? gfx::Font::ITALIC : 0; - query.weight = bold ? gfx::Font::Weight::BOLD : gfx::Font::Weight::NORMAL; - query.device_scale_factor = device_scale_factor; - const gfx::FontRenderParams params = gfx::GetFontRenderParams(query, nullptr); - - // These are passed as ints since they're interpreted as tri-state chars in - // Blink. - base::Pickle reply; - reply.WriteInt(params.use_bitmaps); - reply.WriteInt(params.autohinter); - reply.WriteInt(params.hinting != gfx::FontRenderParams::HINTING_NONE); - reply.WriteInt(ConvertHinting(params.hinting)); - reply.WriteInt(params.antialiasing); - reply.WriteInt(ConvertSubpixelRendering(params.subpixel_rendering)); - reply.WriteInt(params.subpixel_positioning); - - SendRendererReply(fds, reply, -1); + NOTREACHED(); } void SandboxIPCHandler::HandleMakeSharedMemorySegment( @@ -350,34 +146,6 @@ SendRendererReply(fds, reply, shm_fd); } -void SandboxIPCHandler::HandleMatchWithFallback( - int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds) { - std::string face; - bool is_bold; - bool is_italic; - uint32_t charset; - uint32_t fallback_family; - - if (!iter.ReadString(&face) || face.empty() || !iter.ReadBool(&is_bold) || - !iter.ReadBool(&is_italic) || !iter.ReadUInt32(&charset) || - !iter.ReadUInt32(&fallback_family)) { - return; - } - - int font_fd = MatchFontFaceWithFallback(face, is_bold, is_italic, charset, - fallback_family); - - base::Pickle reply; - SendRendererReply(fds, reply, font_fd); - - if (font_fd >= 0) { - if (IGNORE_EINTR(close(font_fd)) < 0) - PLOG(ERROR) << "close"; - } -} - void SandboxIPCHandler::SendRendererReply( const std::vector<base::ScopedFD>& fds, const base::Pickle& reply,
diff --git a/content/browser/sandbox_ipc_linux.h b/content/browser/sandbox_ipc_linux.h index 06c2c604..684a6aa 100644 --- a/content/browser/sandbox_ipc_linux.h +++ b/content/browser/sandbox_ipc_linux.h
@@ -18,8 +18,6 @@ #include "content/common/content_export.h" #include "third_party/icu/source/common/unicode/uchar.h" -class SkString; - namespace content { class SandboxIPCHandler : public base::DelegateSimpleThread::Delegate { @@ -32,55 +30,19 @@ void Run() override; - class TestObserver { - public: - virtual void OnGetFallbackFontForChar(UChar32 c, - std::string name, - int id) = 0; - virtual void OnFontOpen(int id) = 0; - }; - CONTENT_EXPORT static void SetObserverForTests(TestObserver* observer); - private: - int FindOrAddPath(const SkString& path); - void HandleRequestFromChild(int fd); - void HandleFontMatchRequest(int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds); - - void HandleFontOpenRequest(int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds); - - void HandleGetFallbackFontForChar(int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds); - - void HandleGetStyleForStrike(int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds); - - void HandleLocaltime(int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds); - void HandleMakeSharedMemorySegment(int fd, base::PickleIterator iter, const std::vector<base::ScopedFD>& fds); - void HandleMatchWithFallback(int fd, - base::PickleIterator iter, - const std::vector<base::ScopedFD>& fds); - void SendRendererReply(const std::vector<base::ScopedFD>& fds, const base::Pickle& reply, int reply_fd); const int lifeline_fd_; const int browser_socket_; - std::vector<SkString> paths_; DISALLOW_COPY_AND_ASSIGN(SandboxIPCHandler); };
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc index c5be387..8b0f33aa 100644 --- a/content/browser/security_exploit_browsertest.cc +++ b/content/browser/security_exploit_browsertest.cc
@@ -184,28 +184,44 @@ static void CreateLoaderAndStartOnIOThread( scoped_refptr<ResourceMessageFilter> filter, + network::mojom::URLLoaderRequest request, int route_id, int request_id, - const network::ResourceRequest& request) { - network::mojom::URLLoaderPtr loader; - network::TestURLLoaderClient client; + const network::ResourceRequest& resource_request, + network::mojom::URLLoaderClientPtrInfo client) { filter->CreateLoaderAndStart( - mojo::MakeRequest(&loader), route_id, request_id, - network::mojom::kURLLoadOptionNone, request, - client.CreateInterfacePtr(), + std::move(request), route_id, request_id, + network::mojom::kURLLoadOptionNone, resource_request, + network::mojom::URLLoaderClientPtr(std::move(client)), net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); } - static void CreateLoaderAndStart(RenderProcessHost* process, - int route_id, - int request_id, - const network::ResourceRequest& request) { + static void CreateLoaderAndStart( + RenderProcessHost* process, + int route_id, + int request_id, + const network::ResourceRequest& resource_request) { + network::mojom::URLLoaderPtr loader; + network::TestURLLoaderClient client; + CreateLoaderAndStart(process, mojo::MakeRequest(&loader), route_id, + request_id, resource_request, + client.CreateInterfacePtr().PassInterface()); + } + + static void CreateLoaderAndStart( + RenderProcessHost* process, + network::mojom::URLLoaderRequest request, + int route_id, + int request_id, + const network::ResourceRequest& resource_request, + network::mojom::URLLoaderClientPtrInfo client) { RenderProcessHostImpl* impl = static_cast<RenderProcessHostImpl*>(process); auto filter = impl->resource_message_filter_; process->GetChannel()->ipc_task_runner()->PostTask( FROM_HERE, base::BindOnce(CreateLoaderAndStartOnIOThread, filter, - route_id, request_id, request)); + std::move(request), route_id, request_id, + resource_request, std::move(client))); } void TryCreateDuplicateRequestIds(Shell* shell, bool block_loaders) { @@ -227,11 +243,16 @@ // Use the same request id twice. RenderProcessHostKillWaiter kill_waiter(rfh->GetProcess()); + // We need to keep loader and client to keep the requests alive. + network::mojom::URLLoaderPtr loader1, loader2; + network::TestURLLoaderClient client1, client2; - CreateLoaderAndStart(rfh->GetProcess(), rfh->GetRoutingID(), - kRequestIdNotPreviouslyUsed, request); - CreateLoaderAndStart(rfh->GetProcess(), rfh->GetRoutingID(), - kRequestIdNotPreviouslyUsed, request); + CreateLoaderAndStart(rfh->GetProcess(), mojo::MakeRequest(&loader1), + rfh->GetRoutingID(), kRequestIdNotPreviouslyUsed, + request, client1.CreateInterfacePtr().PassInterface()); + CreateLoaderAndStart(rfh->GetProcess(), mojo::MakeRequest(&loader2), + rfh->GetRoutingID(), kRequestIdNotPreviouslyUsed, + request, client2.CreateInterfacePtr().PassInterface()); EXPECT_EQ(bad_message::RDH_INVALID_REQUEST_ID, kill_waiter.Wait()); }
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc index 75983185..6d3476f 100644 --- a/content/browser/service_worker/embedded_worker_instance.cc +++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -81,7 +81,8 @@ mojom::EmbeddedWorkerStartParamsPtr, std::unique_ptr<ServiceWorkerProcessManager::AllocatedProcessInfo>, std::unique_ptr<EmbeddedWorkerInstance::DevToolsProxy>, - std::unique_ptr<URLLoaderFactoryBundleInfo>)>; + std::unique_ptr<URLLoaderFactoryBundleInfo>, + blink::mojom::CacheStoragePtrInfo)>; // Allocates a renderer process for starting a worker and does setup like // registering with DevTools. Called on the UI thread. Calls |callback| on the @@ -106,7 +107,8 @@ base::BindOnce(std::move(callback), blink::ServiceWorkerStatusCode::kErrorAbort, std::move(params), std::move(process_info), - std::move(devtools_proxy), std::move(factory_bundle))); + std::move(devtools_proxy), std::move(factory_bundle), + nullptr /* cache_storage */)); return; } @@ -120,7 +122,7 @@ BrowserThread::IO, FROM_HERE, base::BindOnce(std::move(callback), status, std::move(params), std::move(process_info), std::move(devtools_proxy), - std::move(factory_bundle))); + std::move(factory_bundle), nullptr /* cache_storage */)); return; } const int process_id = process_info->process_id; @@ -130,6 +132,17 @@ // rph->IsInitializedAndNotDead(). CHECK(rph); + // Create cache storage now as an optimization, so the service worker can use + // the Cache Storage API immediately on startup. Don't do this when + // byte-to-byte check will be performed on the worker (|pause_after_download|) + // as most of those workers will have byte-to-byte equality and abort instead + // of running. + blink::mojom::CacheStoragePtr cache_storage; + if (!params->pause_after_download) { + rph->BindCacheStorage(mojo::MakeRequest(&cache_storage), + url::Origin::Create(params->script_url)); + } + // Bind |request|, which is attached to |EmbeddedWorkerInstance::client_|, to // the process. If the process dies, |client_|'s connection error callback // will be called on the IO thread. @@ -209,7 +222,7 @@ BrowserThread::IO, FROM_HERE, base::BindOnce(std::move(callback), status, std::move(params), std::move(process_info), std::move(devtools_proxy), - std::move(factory_bundle))); + std::move(factory_bundle), cache_storage.PassInterface())); } bool HasSentStartWorker(EmbeddedWorkerInstance::StartingPhase phase) { @@ -407,6 +420,7 @@ StatusCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(instance_->context_); + base::WeakPtr<ServiceWorkerContextCore> context = instance_->context_; state_ = ProcessAllocationState::ALLOCATING; start_callback_ = std::move(callback); is_installed_ = params->is_installed; @@ -415,12 +429,11 @@ started_during_browser_startup_ = true; bool can_use_existing_process = - instance_->context_->GetVersionFailureCount( - params->service_worker_version_id) < kMaxSameProcessFailureCount; + context->GetVersionFailureCount(params->service_worker_version_id) < + kMaxSameProcessFailureCount; DCHECK_EQ(params->embedded_worker_id, instance_->embedded_worker_id_); TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ServiceWorker", "ALLOCATING_PROCESS", this); - base::WeakPtr<ServiceWorkerContextCore> context = instance_->context_; base::WeakPtr<ServiceWorkerProcessManager> process_manager = context->process_manager()->AsWeakPtr(); @@ -457,7 +470,8 @@ std::unique_ptr<ServiceWorkerProcessManager::AllocatedProcessInfo> process_info, std::unique_ptr<EmbeddedWorkerInstance::DevToolsProxy> devtools_proxy, - std::unique_ptr<URLLoaderFactoryBundleInfo> factory_bundle) { + std::unique_ptr<URLLoaderFactoryBundleInfo> factory_bundle, + blink::mojom::CacheStoragePtrInfo cache_storage) { DCHECK_CURRENTLY_ON(BrowserThread::IO); std::unique_ptr<WorkerProcessHandle> process_handle; @@ -513,7 +527,8 @@ } instance_->SendStartWorker(std::move(params), - std::move(factory_for_new_scripts)); + std::move(factory_for_new_scripts), + std::move(cache_storage)); TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ServiceWorker", "INITIALIZING_ON_RENDERER", this); @@ -558,12 +573,8 @@ void EmbeddedWorkerInstance::Start(mojom::EmbeddedWorkerStartParamsPtr params, ProviderInfoGetter provider_info_getter, StatusCallback callback) { + DCHECK(context_); restart_count_++; - if (!context_) { - std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort); - // |this| may be destroyed by the callback. - return; - } DCHECK_EQ(EmbeddedWorkerStatus::STOPPED, status_); DCHECK(!params->pause_after_download || !params->is_installed); @@ -688,7 +699,8 @@ void EmbeddedWorkerInstance::SendStartWorker( mojom::EmbeddedWorkerStartParamsPtr params, - scoped_refptr<network::SharedURLLoaderFactory> factory) { + scoped_refptr<network::SharedURLLoaderFactory> factory, + blink::mojom::CacheStoragePtrInfo cache_storage) { DCHECK(context_); DCHECK(params->dispatcher_request.is_pending()); DCHECK(params->controller_request.is_pending()); @@ -704,6 +716,7 @@ inflight_start_task_->set_start_worker_sent_time(base::TimeTicks::Now()); params->provider_info = std::move(provider_info_getter_).Run(process_id(), std::move(factory)); + params->provider_info->cache_storage = std::move(cache_storage); client_->StartWorker(std::move(params)); registry_->BindWorkerToProcess(process_id(), embedded_worker_id()); @@ -776,7 +789,7 @@ } void EmbeddedWorkerInstance::OnThreadStarted(int thread_id) { - if (!context_ || !inflight_start_task_) + if (!inflight_start_task_) return; starting_phase_ = THREAD_STARTED;
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h index d8e4697be..fd99f95 100644 --- a/content/browser/service_worker/embedded_worker_instance.h +++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -31,6 +31,7 @@ #include "third_party/blink/public/common/service_worker/service_worker_status_code.h" #include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h" +#include "third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom.h" #include "url/gurl.h" namespace content { @@ -239,12 +240,16 @@ // Sends the StartWorker message to the renderer. // + // |cache_storage| is an optional optimization so the service worker can + // use the Cache Storage API immediately upon startup. + // // S13nServiceWorker: // |factory| is used for loading non-installed scripts. It can internally be a // bundle of factories instead of just the direct network factory to support // non-NetworkService schemes like chrome-extension:// URLs. void SendStartWorker(mojom::EmbeddedWorkerStartParamsPtr params, - scoped_refptr<network::SharedURLLoaderFactory> factory); + scoped_refptr<network::SharedURLLoaderFactory> factory, + blink::mojom::CacheStoragePtrInfo cache_storage); // Implements mojom::EmbeddedWorkerInstanceHost. // These functions all run on the IO thread.
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc index a4a1b058..f5d2cbe 100644 --- a/content/browser/service_worker/embedded_worker_instance_unittest.cc +++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -948,4 +948,99 @@ EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status()); } +// Records whether a CacheStoragePtr was sent as part of StartWorker. +class RecordCacheStorageHelper : public EmbeddedWorkerTestHelper { + public: + RecordCacheStorageHelper() : EmbeddedWorkerTestHelper(base::FilePath()) {} + ~RecordCacheStorageHelper() override {} + + void OnStartWorker( + int embedded_worker_id, + int64_t service_worker_version_id, + const GURL& scope, + const GURL& script_url, + bool pause_after_download, + mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, + mojom::ControllerServiceWorkerRequest controller_request, + mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, + mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, + blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) + override { + had_cache_storage_ = !!provider_info->cache_storage; + EmbeddedWorkerTestHelper::OnStartWorker( + embedded_worker_id, service_worker_version_id, scope, script_url, + pause_after_download, std::move(dispatcher_request), + std::move(controller_request), std::move(instance_host), + std::move(provider_info), std::move(installed_scripts_info)); + } + + bool had_cache_storage() const { return had_cache_storage_; } + + private: + bool had_cache_storage_ = false; +}; + +// Test that the worker is given a CacheStoragePtr during startup, when +// |pause_after_download| is false. +TEST_F(EmbeddedWorkerInstanceTest, CacheStorageOptimization) { + const GURL scope("http://example.com/"); + const GURL url("http://example.com/worker.js"); + auto helper = std::make_unique<RecordCacheStorageHelper>(); + auto* helper_rawptr = helper.get(); + helper_ = std::move(helper); + + RegistrationAndVersionPair pair = PrepareRegistrationAndVersion(scope, url); + const int64_t version_id = pair.second->version_id(); + std::unique_ptr<EmbeddedWorkerInstance> worker = + embedded_worker_registry()->CreateWorker(pair.second.get()); + + // First, test a worker without pause after download. + { + // Start the worker. + blink::ServiceWorkerStatusCode status = + blink::ServiceWorkerStatusCode::kMax; + base::RunLoop run_loop; + mojom::EmbeddedWorkerStartParamsPtr params = + CreateStartParams(version_id, scope, url); + worker->Start( + std::move(params), CreateProviderInfoGetter(), + base::BindOnce(&SaveStatusAndCall, &status, run_loop.QuitClosure())); + run_loop.Run(); + EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status); + + // Cache storage should have been sent. + EXPECT_TRUE(helper_rawptr->had_cache_storage()); + + // Stop the worker. + worker->Stop(); + base::RunLoop().RunUntilIdle(); + } + + // Second, test a worker with pause after download. + { + // Start the worker until paused. + blink::ServiceWorkerStatusCode status = + blink::ServiceWorkerStatusCode::kMax; + mojom::EmbeddedWorkerStartParamsPtr params = + CreateStartParams(version_id, scope, url); + params->pause_after_download = true; + worker->Start( + std::move(params), CreateProviderInfoGetter(), + base::BindOnce(&SaveStatusAndCall, &status, base::DoNothing::Once<>())); + base::RunLoop().RunUntilIdle(); + + // Finish starting. + worker->ResumeAfterDownload(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status); + + // Cache storage should not have been sent. + EXPECT_FALSE(helper_rawptr->had_cache_storage()); + + // Stop the worker. + worker->Stop(); + base::RunLoop().RunUntilIdle(); + } +} + } // namespace content
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc index 3f2f3ac..f2ada9ea 100644 --- a/content/browser/service_worker/service_worker_version.cc +++ b/content/browser/service_worker/service_worker_version.cc
@@ -1484,6 +1484,7 @@ } void ServiceWorkerVersion::StartWorkerInternal() { + DCHECK(context_); DCHECK_EQ(EmbeddedWorkerStatus::STOPPED, running_status()); DCHECK(inflight_requests_.IsEmpty()); DCHECK(request_timeouts_.empty());
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn index 3f781bc..8319876 100644 --- a/content/child/BUILD.gn +++ b/content/child/BUILD.gn
@@ -140,6 +140,13 @@ ] } + if (is_linux && !is_android) { + deps += [ + "//components/services/font:lib", + "//components/services/font/public/cpp", + ] + } + if (is_android) { deps += [ "//third_party/android_tools:cpu_features" ] }
diff --git a/content/child/DEPS b/content/child/DEPS index c6e3f2d..e280e35f 100644 --- a/content/child/DEPS +++ b/content/child/DEPS
@@ -4,6 +4,7 @@ "+components/tracing", "+components/variations/child_process_field_trial_syncer.h", "+components/webcrypto", + "+components/services/font", "+content/app/strings/grit", # For generated headers "+content/public/child",
diff --git a/content/child/child_process_sandbox_support_impl_linux.cc b/content/child/child_process_sandbox_support_impl_linux.cc index 046e064..32dfe1a 100644 --- a/content/child/child_process_sandbox_support_impl_linux.cc +++ b/content/child/child_process_sandbox_support_impl_linux.cc
@@ -15,6 +15,10 @@ #include "base/posix/unix_domain_socket.h" #include "base/sys_byteorder.h" #include "base/trace_event/trace_event.h" +#include "components/services/font/public/cpp/font_loader.h" +#include "components/services/font/public/interfaces/constants.mojom.h" +#include "components/services/font/public/interfaces/font_service.mojom.h" +#include "content/public/common/common_sandbox_support_linux.h" #include "services/service_manager/sandbox/linux/sandbox_linux.h" #include "services/service_manager/zygote/common/common_sandbox_support_linux.h" #include "third_party/blink/public/platform/linux/web_fallback_font.h" @@ -24,111 +28,64 @@ namespace content { -void GetFallbackFontForCharacter(int32_t character, +void GetFallbackFontForCharacter(sk_sp<font_service::FontLoader> font_loader, + int32_t character, const char* preferred_locale, - blink::WebFallbackFont* fallbackFont) { - TRACE_EVENT0("sandbox_ipc", "GetFontFamilyForCharacter"); - - base::Pickle request; - request.WriteInt( - service_manager::SandboxLinux::METHOD_GET_FALLBACK_FONT_FOR_CHAR); - request.WriteInt(character); - request.WriteString(preferred_locale); - - uint8_t buf[512]; - const ssize_t n = base::UnixDomainSocket::SendRecvMsg( - service_manager::GetSandboxFD(), buf, sizeof(buf), nullptr, request); - + blink::WebFallbackFont* fallback_font) { + DCHECK(font_loader.get()); + font_service::mojom::FontIdentityPtr font_identity; + bool is_bold = false; + bool is_italic = false; std::string family_name; - std::string filename; - int fontconfigInterfaceId = 0; - int ttcIndex = 0; - bool isBold = false; - bool isItalic = false; - if (n != -1) { - base::Pickle reply(reinterpret_cast<char*>(buf), n); - base::PickleIterator pickle_iter(reply); - if (pickle_iter.ReadString(&family_name) && - pickle_iter.ReadString(&filename) && - pickle_iter.ReadInt(&fontconfigInterfaceId) && - pickle_iter.ReadInt(&ttcIndex) && pickle_iter.ReadBool(&isBold) && - pickle_iter.ReadBool(&isItalic)) { - fallbackFont->name = blink::WebString::FromUTF8(family_name); - fallbackFont->filename = blink::WebVector<char>(filename); - fallbackFont->fontconfig_interface_id = fontconfigInterfaceId; - fallbackFont->ttc_index = ttcIndex; - fallbackFont->is_bold = isBold; - fallbackFont->is_italic = isItalic; - } + if (!font_loader->FallbackFontForCharacter(character, preferred_locale, + &font_identity, &family_name, + &is_bold, &is_italic)) { + LOG(ERROR) << "FontService fallback request does not receive a response."; + return; } + + // TODO(drott): Perhaps take WebFallbackFont out of the picture here and pass + // mojo FontIdentityPtr directly? + fallback_font->name = + blink::WebString::FromUTF8(family_name.c_str(), family_name.length()); + fallback_font->fontconfig_interface_id = font_identity->id; + fallback_font->filename.Assign(font_identity->str_representation.c_str(), + font_identity->str_representation.length()); + fallback_font->ttc_index = font_identity->ttc_index; + fallback_font->is_bold = is_bold; + fallback_font->is_italic = is_italic; + return; } -void GetRenderStyleForStrike(const char* family, +void GetRenderStyleForStrike(sk_sp<font_service::FontLoader> font_loader, + const char* family, int size, bool is_bold, bool is_italic, float device_scale_factor, blink::WebFontRenderStyle* out) { - TRACE_EVENT0("sandbox_ipc", "GetRenderStyleForStrike"); + DCHECK(font_loader.get()); + font_service::mojom::FontIdentityPtr font_identity; *out = blink::WebFontRenderStyle(); if (size < 0 || size > std::numeric_limits<uint16_t>::max()) return; - base::Pickle request; - request.WriteInt(service_manager::SandboxLinux::METHOD_GET_STYLE_FOR_STRIKE); - request.WriteString(family); - request.WriteBool(is_bold); - request.WriteBool(is_italic); - request.WriteUInt16(size); - request.WriteFloat(device_scale_factor); + font_service::mojom::FontRenderStylePtr font_render_style; + font_loader->FontRenderStyleForStrike(family, size, is_bold, is_italic, + device_scale_factor, + &font_render_style); - uint8_t buf[512]; - const ssize_t n = base::UnixDomainSocket::SendRecvMsg( - service_manager::GetSandboxFD(), buf, sizeof(buf), nullptr, request); - if (n == -1) - return; - - base::Pickle reply(reinterpret_cast<char*>(buf), n); - base::PickleIterator pickle_iter(reply); - int use_bitmaps, use_autohint, use_hinting, hint_style, use_antialias; - int use_subpixel_rendering, use_subpixel_positioning; - if (pickle_iter.ReadInt(&use_bitmaps) && pickle_iter.ReadInt(&use_autohint) && - pickle_iter.ReadInt(&use_hinting) && pickle_iter.ReadInt(&hint_style) && - pickle_iter.ReadInt(&use_antialias) && - pickle_iter.ReadInt(&use_subpixel_rendering) && - pickle_iter.ReadInt(&use_subpixel_positioning)) { - out->use_bitmaps = use_bitmaps; - out->use_auto_hint = use_autohint; - out->use_hinting = use_hinting; - out->hint_style = hint_style; - out->use_anti_alias = use_antialias; - out->use_subpixel_rendering = use_subpixel_rendering; - out->use_subpixel_positioning = use_subpixel_positioning; - } -} - -int MatchFontWithFallback(const std::string& face, - bool bold, - bool italic, - int charset, - PP_BrowserFont_Trusted_Family fallback_family) { - TRACE_EVENT0("sandbox_ipc", "MatchFontWithFallback"); - - base::Pickle request; - request.WriteInt(service_manager::SandboxLinux::METHOD_MATCH_WITH_FALLBACK); - request.WriteString(face); - request.WriteBool(bold); - request.WriteBool(italic); - request.WriteUInt32(charset); - request.WriteUInt32(fallback_family); - uint8_t reply_buf[64]; - int fd = -1; - base::UnixDomainSocket::SendRecvMsg(service_manager::GetSandboxFD(), - reply_buf, sizeof(reply_buf), &fd, - request); - return fd; + out->use_bitmaps = static_cast<char>(font_render_style->use_bitmaps); + out->use_auto_hint = static_cast<char>(font_render_style->use_autohint); + out->use_hinting = static_cast<char>(font_render_style->use_hinting); + out->hint_style = font_render_style->hint_style; + out->use_anti_alias = static_cast<char>(font_render_style->use_antialias); + out->use_subpixel_rendering = + static_cast<char>(font_render_style->use_subpixel_rendering); + out->use_subpixel_positioning = + static_cast<char>(font_render_style->use_subpixel_positioning); } } // namespace content
diff --git a/content/child/child_process_sandbox_support_impl_linux.h b/content/child/child_process_sandbox_support_impl_linux.h index 293c527..140060c 100644 --- a/content/child/child_process_sandbox_support_impl_linux.h +++ b/content/child/child_process_sandbox_support_impl_linux.h
@@ -7,7 +7,8 @@ #include <stdint.h> -#include "content/public/child/child_process_sandbox_support_linux.h" +#include "components/services/font/public/cpp/font_loader.h" +#include "third_party/skia/include/core/SkRefCnt.h" namespace blink { struct WebFallbackFont; @@ -20,7 +21,8 @@ // specified by |character|, a UTF-32 character. |preferred_locale| contains the // preferred locale identifier for |character|. The instance has an empty font // name if the request could not be satisfied. -void GetFallbackFontForCharacter(const int32_t character, +void GetFallbackFontForCharacter(sk_sp<font_service::FontLoader> font_loader, + const int32_t character, const char* preferred_locale, blink::WebFallbackFont* family); @@ -28,7 +30,8 @@ // |size_and_style| stores the bold setting in its least-significant bit, the // italic setting in its second-least-significant bit, and holds the requested // size in pixels into its remaining bits. -void GetRenderStyleForStrike(const char* family, +void GetRenderStyleForStrike(sk_sp<font_service::FontLoader> font_loader, + const char* family, int size, bool is_bold, bool is_italic,
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index bdda69a..4c3fa75e1 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn
@@ -116,8 +116,6 @@ "fileapi/file_system_messages.h", "fileapi/webblob_messages.h", "font_cache_dispatcher_win.cc", - "font_config_ipc_linux.cc", - "font_config_ipc_linux.h", "font_list.cc", "font_list.h", "font_list_android.cc",
diff --git a/content/common/common_sandbox_support_linux.cc b/content/common/common_sandbox_support_linux.cc index 1c05b650..507c3558 100644 --- a/content/common/common_sandbox_support_linux.cc +++ b/content/common/common_sandbox_support_linux.cc
@@ -15,6 +15,9 @@ namespace content { +// TODO(drott): This should be removed once we don't need to support PPAPI +// TrueType functionality anymore, and before that, it should be replaced with +// using FreeType for the purpose instead of reimplementing table parsing. bool GetFontTable(int fd, uint32_t table_tag, off_t offset,
diff --git a/content/common/font_config_ipc_linux.cc b/content/common/font_config_ipc_linux.cc deleted file mode 100644 index 2596c992..0000000 --- a/content/common/font_config_ipc_linux.cc +++ /dev/null
@@ -1,179 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/font_config_ipc_linux.h" - -#include <errno.h> -#include <fcntl.h> -#include <stdint.h> -#include <sys/mman.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/uio.h> -#include <unistd.h> - -#include <functional> -#include <memory> -#include <utility> - -#include "base/files/file_util.h" -#include "base/files/memory_mapped_file.h" -#include "base/memory/ref_counted.h" -#include "base/pickle.h" -#include "base/posix/unix_domain_socket.h" -#include "base/threading/thread_restrictions.h" -#include "base/trace_event/trace_event.h" -#include "skia/ext/skia_utils_base.h" -#include "third_party/skia/include/core/SkData.h" -#include "third_party/skia/include/core/SkRefCnt.h" -#include "third_party/skia/include/core/SkStream.h" -#include "third_party/skia/include/core/SkTypeface.h" - -namespace content { - -std::size_t SkFontConfigInterfaceFontIdentityHash::operator()( - const SkFontConfigInterface::FontIdentity& sp) const { - std::hash<std::string> stringhash; - std::hash<int> inthash; - size_t r = inthash(sp.fID); - r = r * 41 + inthash(sp.fTTCIndex); - r = r * 41 + stringhash(sp.fString.c_str()); - r = r * 41 + inthash(sp.fStyle.weight()); - r = r * 41 + inthash(sp.fStyle.slant()); - r = r * 41 + inthash(sp.fStyle.width()); - return r; -} - -// Wikpedia's main country selection page activates 21 fallback fonts, -// doubling this we should be on the generous side as an upper bound, -// but nevertheless not have the mapped typefaces cache grow excessively. -const size_t kMaxMappedTypefaces = 42; - -void CloseFD(int fd) { - int err = IGNORE_EINTR(close(fd)); - DCHECK(!err); -} - -FontConfigIPC::FontConfigIPC(int fd) - : fd_(fd) - , mapped_typefaces_(kMaxMappedTypefaces) { -} - -FontConfigIPC::~FontConfigIPC() { - CloseFD(fd_); -} - -bool FontConfigIPC::matchFamilyName(const char familyName[], - SkFontStyle requestedStyle, - FontIdentity* outFontIdentity, - SkString* outFamilyName, - SkFontStyle* outStyle) { - TRACE_EVENT0("sandbox_ipc", "FontConfigIPC::matchFamilyName"); - size_t familyNameLen = familyName ? strlen(familyName) : 0; - if (familyNameLen > kMaxFontFamilyLength) - return false; - - base::Pickle request; - request.WriteInt(METHOD_MATCH); - request.WriteData(familyName, familyNameLen); - skia::WriteSkFontStyle(&request, requestedStyle); - - uint8_t reply_buf[2048]; - const ssize_t r = base::UnixDomainSocket::SendRecvMsg( - fd_, reply_buf, sizeof(reply_buf), nullptr, request); - if (r == -1) - return false; - - base::Pickle reply(reinterpret_cast<char*>(reply_buf), r); - base::PickleIterator iter(reply); - bool result; - if (!iter.ReadBool(&result)) - return false; - if (!result) - return false; - - SkString reply_family; - FontIdentity reply_identity; - SkFontStyle reply_style; - if (!skia::ReadSkString(&iter, &reply_family) || - !skia::ReadSkFontIdentity(&iter, &reply_identity) || - !skia::ReadSkFontStyle(&iter, &reply_style)) { - return false; - } - - if (outFontIdentity) - *outFontIdentity = reply_identity; - if (outFamilyName) - *outFamilyName = reply_family; - if (outStyle) - *outStyle = reply_style; - - return true; -} - -static void DestroyMemoryMappedFile(const void*, void* context) { - base::ThreadRestrictions::ScopedAllowIO allow_munmap; - delete static_cast<base::MemoryMappedFile*>(context); -} - -SkMemoryStream* FontConfigIPC::mapFileDescriptorToStream(int fd) { - std::unique_ptr<base::MemoryMappedFile> mapped_font_file( - new base::MemoryMappedFile); - base::ThreadRestrictions::ScopedAllowIO allow_mmap; - mapped_font_file->Initialize(base::File(fd)); - DCHECK(mapped_font_file->IsValid()); - - sk_sp<SkData> data = - SkData::MakeWithProc(mapped_font_file->data(), mapped_font_file->length(), - &DestroyMemoryMappedFile, mapped_font_file.get()); - if (!data) - return nullptr; - ignore_result(mapped_font_file.release()); // Ownership transferred to SkDataB - return new SkMemoryStream(std::move(data)); -} - -SkStreamAsset* FontConfigIPC::openStream(const FontIdentity& identity) { - TRACE_EVENT0("sandbox_ipc", "FontConfigIPC::openStream"); - - base::Pickle request; - request.WriteInt(METHOD_OPEN); - request.WriteUInt32(identity.fID); - - int result_fd = -1; - uint8_t reply_buf[256]; - const ssize_t r = base::UnixDomainSocket::SendRecvMsg( - fd_, reply_buf, sizeof(reply_buf), &result_fd, request); - if (r == -1) - return nullptr; - - base::Pickle reply(reinterpret_cast<char*>(reply_buf), r); - bool result; - base::PickleIterator iter(reply); - if (!iter.ReadBool(&result) || !result) { - if (result_fd) - CloseFD(result_fd); - return nullptr; - } - - return mapFileDescriptorToStream(result_fd); -} - -sk_sp<SkTypeface> FontConfigIPC::makeTypeface( - const SkFontConfigInterface::FontIdentity& identity) { - base::AutoLock lock(lock_); - auto mapped_typefaces_it = mapped_typefaces_.Get(identity); - if (mapped_typefaces_it != mapped_typefaces_.end()) - return mapped_typefaces_it->second; - - SkStreamAsset* typeface_stream = openStream(identity); - if (!typeface_stream) - return nullptr; - sk_sp<SkTypeface> typeface_from_stream( - SkTypeface::MakeFromStream(typeface_stream, identity.fTTCIndex)); - auto mapped_typefaces_insert_it = - mapped_typefaces_.Put(identity, std::move(typeface_from_stream)); - return mapped_typefaces_insert_it->second; -} - -} // namespace content
diff --git a/content/common/font_config_ipc_linux.h b/content/common/font_config_ipc_linux.h deleted file mode 100644 index 0fc85ec5..0000000 --- a/content/common/font_config_ipc_linux.h +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_FONT_CONFIG_IPC_LINUX_H_ -#define CONTENT_COMMON_FONT_CONFIG_IPC_LINUX_H_ - -#include "base/compiler_specific.h" -#include "base/containers/mru_cache.h" -#include "base/macros.h" -#include "base/synchronization/lock.h" -#include "third_party/skia/include/core/SkRefCnt.h" -#include "third_party/skia/include/core/SkStream.h" -#include "third_party/skia/include/core/SkTypeface.h" -#include "third_party/skia/include/ports/SkFontConfigInterface.h" - -#include <stddef.h> - -#include <string> - -class SkString; - -namespace content { - -struct SkFontConfigInterfaceFontIdentityHash { - std::size_t operator()(const SkFontConfigInterface::FontIdentity& sp) const; -}; - -// FontConfig implementation for Skia that proxies out of process to get out -// of the sandbox. See https://chromium.googlesource.com/chromium/src/+/master/docs/linux_sandbox_ipc.md -class FontConfigIPC : public SkFontConfigInterface { - public: - explicit FontConfigIPC(int fd); - ~FontConfigIPC() override; - - bool matchFamilyName(const char familyName[], - SkFontStyle requested, - FontIdentity* outFontIdentifier, - SkString* outFamilyName, - SkFontStyle* outStyle) override; - - sk_sp<SkTypeface> makeTypeface(const FontIdentity& identity) override - WARN_UNUSED_RESULT; - - enum Method { - METHOD_MATCH = 0, - METHOD_OPEN = 1, - }; - - enum { - kMaxFontFamilyLength = 2048 - }; - - private: - // Marking this private in Blink's implementation of SkFontConfigInterface - // since our caching implementation's efficacy is impaired if both - // createTypeface and openStream are used in parallel. - SkStreamAsset* openStream(const FontIdentity&) override; - - SkMemoryStream* mapFileDescriptorToStream(int fd); - - const int fd_; - // Lock preventing multiple threads from creating a typeface and removing - // an element from |mapped_typefaces_| map at the same time. - base::Lock lock_; - // Practically, this hash_map definition means that we re-map the same font - // file multiple times if we receive createTypeface requests for multiple - // ttc-indices or styles but the same fontconfig interface id. Since the usage - // frequency of ttc indices is very low, and style is not used by clients of - // this API, this seems okay. - base::HashingMRUCache<FontIdentity, - sk_sp<SkTypeface>, - SkFontConfigInterfaceFontIdentityHash> - mapped_typefaces_; - - DISALLOW_COPY_AND_ASSIGN(FontConfigIPC); -}; - -} // namespace content - -#endif // CONTENT_COMMON_FONT_CONFIG_IPC_LINUX_H_
diff --git a/content/common/service_worker/service_worker_provider.mojom b/content/common/service_worker/service_worker_provider.mojom index ee663f7..c5d10570 100644 --- a/content/common/service_worker/service_worker_provider.mojom +++ b/content/common/service_worker/service_worker_provider.mojom
@@ -10,6 +10,7 @@ import "third_party/blink/public/mojom/service_worker/service_worker_object.mojom"; import "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom"; import "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom"; +import "third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom"; // The name of the InterfaceProviderSpec in service manifests used by the // frame tree to expose service-worker-specific interfaces between renderer @@ -40,6 +41,13 @@ // importScripts(). associated network.mojom.URLLoaderFactory? script_loader_factory_ptr_info; + // |cache_storage| is an optional optimization so the service worker can use + // the Cache Storage API immediately without using InterfaceProvider. May be + // null for service workers created for update checks, as the optimization + // would be wasteful because these workers usually are aborted after the + // byte-to-byte update check before running. + blink.mojom.CacheStorage? cache_storage; + service_manager.mojom.InterfaceProvider interface_provider; };
diff --git a/content/ppapi_plugin/BUILD.gn b/content/ppapi_plugin/BUILD.gn index b34b076..f356fcf 100644 --- a/content/ppapi_plugin/BUILD.gn +++ b/content/ppapi_plugin/BUILD.gn
@@ -51,6 +51,7 @@ deps = [ "//base", "//components/discardable_memory/client", + "//components/services/font/public/cpp:cpp", "//content:export", "//content/child", "//content/public/child:child_sources",
diff --git a/content/ppapi_plugin/ppapi_blink_platform_impl.cc b/content/ppapi_plugin/ppapi_blink_platform_impl.cc index 9e0474e..6813855 100644 --- a/content/ppapi_plugin/ppapi_blink_platform_impl.cc +++ b/content/ppapi_plugin/ppapi_blink_platform_impl.cc
@@ -40,11 +40,15 @@ class PpapiBlinkPlatformImpl::SandboxSupport : public WebSandboxSupport { public: +#if defined(OS_LINUX) + explicit SandboxSupport(sk_sp<font_service::FontLoader> font_loader) + : font_loader_(std::move(font_loader)) {} +#endif ~SandboxSupport() override {} #if defined(OS_MACOSX) bool LoadFont(CTFontRef srcFont, CGFontRef* out, uint32_t* fontID) override; -#elif defined(OS_POSIX) +#elif defined(OS_LINUX) SandboxSupport(); void GetFallbackFontForCharacter( WebUChar32 character, @@ -64,6 +68,7 @@ std::map<int32_t, blink::WebFallbackFont> unicode_font_families_; // For debugging crbug.com/312965 base::PlatformThreadId creation_thread_; + sk_sp<font_service::FontLoader> font_loader_; #endif }; @@ -105,8 +110,8 @@ return; } - content::GetFallbackFontForCharacter(character, preferred_locale, - fallbackFont); + content::GetFallbackFontForCharacter(font_loader_, character, + preferred_locale, fallbackFont); unicode_font_families_.insert(std::make_pair(character, *fallbackFont)); } @@ -117,8 +122,8 @@ bool is_italic, float device_scale_factor, blink::WebFontRenderStyle* out) { - GetRenderStyleForStrike(family, size, is_bold, is_italic, device_scale_factor, - out); + GetRenderStyleForStrike(font_loader_, family, size, is_bold, is_italic, + device_scale_factor, out); } #endif @@ -126,8 +131,14 @@ #endif // !defined(OS_ANDROID) && !defined(OS_WIN) PpapiBlinkPlatformImpl::PpapiBlinkPlatformImpl() { -#if !defined(OS_ANDROID) && !defined(OS_WIN) - sandbox_support_.reset(new PpapiBlinkPlatformImpl::SandboxSupport); +#if defined(OS_LINUX) && !defined(OS_ANDROID) + font_loader_ = + sk_make_sp<font_service::FontLoader>(ChildThread::Get()->GetConnector()); + SkFontConfigInterface::SetGlobal(font_loader_); + sandbox_support_.reset( + new PpapiBlinkPlatformImpl::SandboxSupport(font_loader_)); +#elif defined(OS_MACOSX) + sandbox_support_.reset(new PpapiBlinkPlatformImpl::SandboxSupport()); #endif }
diff --git a/content/ppapi_plugin/ppapi_blink_platform_impl.h b/content/ppapi_plugin/ppapi_blink_platform_impl.h index d38cfcf..aea9579 100644 --- a/content/ppapi_plugin/ppapi_blink_platform_impl.h +++ b/content/ppapi_plugin/ppapi_blink_platform_impl.h
@@ -13,6 +13,11 @@ #include "build/build_config.h" #include "content/child/blink_platform_impl.h" +#if defined(OS_LINUX) +#include "components/services/font/public/cpp/font_loader.h" +#include "third_party/skia/include/core/SkRefCnt.h" +#endif + namespace content { class PpapiBlinkPlatformImpl : public BlinkPlatformImpl { @@ -53,6 +58,10 @@ std::unique_ptr<SandboxSupport> sandbox_support_; #endif +#if defined(OS_LINUX) + sk_sp<font_service::FontLoader> font_loader_; +#endif + DISALLOW_COPY_AND_ASSIGN(PpapiBlinkPlatformImpl); };
diff --git a/content/public/app/BUILD.gn b/content/public/app/BUILD.gn index e5518f5..28d5f8d 100644 --- a/content/public/app/BUILD.gn +++ b/content/public/app/BUILD.gn
@@ -197,6 +197,10 @@ "//services/video_capture:manifest", "//services/viz:manifest", ] + + if (is_linux && !is_android) { + packaged_services += [ "//components/services/font:manifest" ] + } } service_manifest("browser_manifest") {
diff --git a/content/public/app/mojo/content_plugin_manifest.json b/content/public/app/mojo/content_plugin_manifest.json index 44c6d2ba..c00ea402 100644 --- a/content/public/app/mojo/content_plugin_manifest.json +++ b/content/public/app/mojo/content_plugin_manifest.json
@@ -25,6 +25,7 @@ "plugin" ], "device": [ "device:power_monitor" ], + "font_service": [ "font_service" ], "ui": [ "discardable_memory" ] } }
diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json index 71bc81d..70c67820 100644 --- a/content/public/app/mojo/content_renderer_manifest.json +++ b/content/public/app/mojo/content_renderer_manifest.json
@@ -33,6 +33,7 @@ "font_loader", "renderer" ], + "font_service": [ "font_service" ], "metrics": [ "url_keyed_metrics" ], "device": [ "device:power_monitor",
diff --git a/content/public/app/mojo/content_utility_manifest.json b/content/public/app/mojo/content_utility_manifest.json index 563c885..2c4dcb1 100644 --- a/content/public/app/mojo/content_utility_manifest.json +++ b/content/public/app/mojo/content_utility_manifest.json
@@ -29,7 +29,8 @@ "device": [ "device:power_monitor", "device:time_zone_monitor" - ] + ], + "font_service": [ "font_service" ] } } },
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h index a39bf379..19d9c106 100644 --- a/content/public/browser/render_process_host.h +++ b/content/public/browser/render_process_host.h
@@ -23,6 +23,7 @@ #include "ipc/ipc_sender.h" #include "media/media_buildflags.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom.h" #include "ui/gfx/native_widget_types.h" #if defined(OS_ANDROID) @@ -435,6 +436,12 @@ // https://crbug.com/846155. virtual void LockToOrigin(const GURL& lock_url) = 0; + // Binds |request| to the CacheStorageDispatcherHost instance. The binding is + // sent to the IO thread. This is for internal use only, and is only exposed + // here to support MockRenderProcessHost usage in tests. + virtual void BindCacheStorage(blink::mojom::CacheStorageRequest request, + const url::Origin& origin) = 0; + // Returns the current number of active views in this process. Excludes // any RenderViewHosts that are swapped out. size_t GetActiveViewCount();
diff --git a/content/public/child/BUILD.gn b/content/public/child/BUILD.gn index 296eca9..c7f6f6a 100644 --- a/content/public/child/BUILD.gn +++ b/content/public/child/BUILD.gn
@@ -29,7 +29,6 @@ visibility = [ "//content/*" ] sources = [ - "child_process_sandbox_support_linux.h", "child_thread.h", "dwrite_font_proxy_init_win.h", "image_decoder_utils.h",
diff --git a/content/public/child/child_process_sandbox_support_linux.h b/content/public/child/child_process_sandbox_support_linux.h deleted file mode 100644 index 5f0577c..0000000 --- a/content/public/child/child_process_sandbox_support_linux.h +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_PUBLIC_CHILD_CHILD_PROCESS_SANDBOX_SUPPORT_LINUX_H_ -#define CONTENT_PUBLIC_CHILD_CHILD_PROCESS_SANDBOX_SUPPORT_LINUX_H_ - -#include <stddef.h> -#include <stdint.h> -#include <string> - -#include "content/common/content_export.h" -#include "ppapi/c/trusted/ppb_browser_font_trusted.h" - -namespace content { - -// Return a read-only file descriptor to the font which best matches the given -// properties or -1 on failure. -// charset: specifies the language(s) that the font must cover. See -// render_sandbox_host_linux.cc for more information. -// fallback_family: If not set to PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT, font -// selection should fall back to generic Windows font names like Arial and -// Times New Roman. -CONTENT_EXPORT int MatchFontWithFallback( - const std::string& face, - bool bold, - bool italic, - int charset, - PP_BrowserFont_Trusted_Family fallback_family); - -}; // namespace content - -#endif // CONTENT_PUBLIC_CHILD_CHILD_PROCESS_SANDBOX_SUPPORT_LINUX_H_
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc index 434ec69..95d09af 100644 --- a/content/public/test/mock_render_process_host.cc +++ b/content/public/test/mock_render_process_host.cc
@@ -424,6 +424,12 @@ is_renderer_locked_to_site_ = true; } +void MockRenderProcessHost::BindCacheStorage( + blink::mojom::CacheStorageRequest request, + const url::Origin& origin) { + cache_storage_request_ = std::move(request); +} + void MockRenderProcessHost::FilterURL(bool empty_allowed, GURL* url) { RenderProcessHostImpl::FilterURL(this, empty_allowed, url); }
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h index 1088de8..acfb2cc 100644 --- a/content/public/test/mock_render_process_host.h +++ b/content/public/test/mock_render_process_host.h
@@ -144,6 +144,8 @@ bool HostHasNotBeenUsed() override; void LockToOrigin(const GURL& lock_url) override; + void BindCacheStorage(blink::mojom::CacheStorageRequest request, + const url::Origin& origin) override; // IPC::Sender via RenderProcessHost. bool Send(IPC::Message* msg) override; @@ -212,6 +214,7 @@ service_manager::Identity child_identity_; bool is_renderer_locked_to_site_ = false; network::mojom::URLLoaderFactory* url_loader_factory_; + blink::mojom::CacheStorageRequest cache_storage_request_; base::WeakPtrFactory<MockRenderProcessHost> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(MockRenderProcessHost);
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index 43e3f295..aaa4a9c 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -1018,6 +1018,16 @@ deps += [ "//sandbox:sandbox_buildflags" ] } + if (is_linux && !is_android) { + deps += [ + "//components/services/font:lib", + "//components/services/font/public/cpp", + ] + data_deps = [ + "//components/services/font:font_service", + ] + } + if (use_ozone) { deps += [ "//ui/ozone" ] }
diff --git a/content/renderer/media/webrtc/media_stream_track_metrics.cc b/content/renderer/media/webrtc/media_stream_track_metrics.cc index e84c5e83b..30ea304 100644 --- a/content/renderer/media/webrtc/media_stream_track_metrics.cc +++ b/content/renderer/media/webrtc/media_stream_track_metrics.cc
@@ -5,7 +5,6 @@ #include "content/renderer/media/webrtc/media_stream_track_metrics.h" #include <inttypes.h> -#include <set> #include <string> #include "base/md5.h" @@ -14,133 +13,44 @@ #include "content/public/common/service_names.mojom.h" #include "content/renderer/render_thread_impl.h" #include "services/service_manager/public/cpp/connector.h" -#include "third_party/webrtc/api/mediastreaminterface.h" - -using webrtc::AudioTrackVector; -using webrtc::MediaStreamInterface; -using webrtc::MediaStreamTrackInterface; -using webrtc::PeerConnectionInterface; -using webrtc::VideoTrackVector; namespace content { -namespace { -typedef std::set<std::string> IdSet; - -template <class T> -IdSet GetTrackIds(const std::vector<rtc::scoped_refptr<T>>& tracks) { - IdSet track_ids; - for (const auto& track : tracks) - track_ids.insert(track->id()); - return track_ids; -} - -// TODO(tommi): Consolidate this and TrackObserver since these implementations -// are fundamentally achieving the same thing (aside from specific logic inside -// the OnChanged callbacks). -class MediaStreamObserver - : public base::RefCountedThreadSafe<MediaStreamObserver>, - public webrtc::ObserverInterface { - public: - typedef base::Callback< - void(const IdSet& audio_track_ids, const IdSet& video_track_ids)> - OnChangedCallback; - - MediaStreamObserver( - const OnChangedCallback& callback, - const scoped_refptr<base::SingleThreadTaskRunner>& main_thread, - webrtc::MediaStreamInterface* stream) - : main_thread_(main_thread), stream_(stream), callback_(callback) { - signaling_thread_.DetachFromThread(); - stream_->RegisterObserver(this); - } - - const scoped_refptr<webrtc::MediaStreamInterface>& stream() const { - DCHECK(main_thread_->BelongsToCurrentThread()); - return stream_; - } - - void Unregister() { - DCHECK(main_thread_->BelongsToCurrentThread()); - callback_.Reset(); - stream_->UnregisterObserver(this); - stream_ = nullptr; - } - - private: - friend class base::RefCountedThreadSafe<MediaStreamObserver>; - ~MediaStreamObserver() override { - DCHECK(!stream_.get()) << "must have been unregistered before deleting"; - } - - // webrtc::ObserverInterface implementation. - void OnChanged() override { - DCHECK(signaling_thread_.CalledOnValidThread()); - main_thread_->PostTask( - FROM_HERE, base::BindOnce(&MediaStreamObserver::OnChangedOnMainThread, - this, GetTrackIds(stream_->GetAudioTracks()), - GetTrackIds(stream_->GetVideoTracks()))); - } - - void OnChangedOnMainThread(const IdSet& audio_track_ids, - const IdSet& video_track_ids) { - DCHECK(main_thread_->BelongsToCurrentThread()); - if (!callback_.is_null()) - callback_.Run(audio_track_ids, video_track_ids); - } - - const scoped_refptr<base::SingleThreadTaskRunner> main_thread_; - scoped_refptr<webrtc::MediaStreamInterface> stream_; - OnChangedCallback callback_; // Only touched on the main thread. - base::ThreadChecker signaling_thread_; -}; - -} // namespace class MediaStreamTrackMetricsObserver { public: - MediaStreamTrackMetricsObserver( - MediaStreamTrackMetrics::StreamType stream_type, - MediaStreamInterface* stream, - MediaStreamTrackMetrics* owner); + MediaStreamTrackMetricsObserver(MediaStreamTrackMetrics::Direction direction, + MediaStreamTrackMetrics::Kind kind, + std::string track_id, + MediaStreamTrackMetrics* owner); ~MediaStreamTrackMetricsObserver(); - // Sends begin/end messages for all tracks currently tracked. - void SendLifetimeMessages(MediaStreamTrackMetrics::LifetimeEvent event); + // Sends begin/end messages for the track if not already reported. + void SendLifetimeMessageForTrack( + MediaStreamTrackMetrics::LifetimeEvent event); - MediaStreamInterface* stream() { + MediaStreamTrackMetrics::Direction direction() { DCHECK(thread_checker_.CalledOnValidThread()); - return observer_->stream().get(); + return direction_; } - MediaStreamTrackMetrics::StreamType stream_type() { + MediaStreamTrackMetrics::Kind kind() { DCHECK(thread_checker_.CalledOnValidThread()); - return stream_type_; + return kind_; + } + + std::string track_id() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return track_id_; } private: - void OnChanged(const IdSet& audio_track_ids, const IdSet& video_track_ids); - - void ReportAddedAndRemovedTracks( - const IdSet& new_ids, - const IdSet& old_ids, - MediaStreamTrackMetrics::TrackType track_type); - - // Sends a lifetime message for the given tracks. OK to call with an - // empty |ids|, in which case the method has no side effects. - void ReportTracks(const IdSet& ids, - MediaStreamTrackMetrics::TrackType track_type, - MediaStreamTrackMetrics::LifetimeEvent event); - // False until start/end of lifetime messages have been sent. bool has_reported_start_; bool has_reported_end_; - // IDs of audio and video tracks in the stream being observed. - IdSet audio_track_ids_; - IdSet video_track_ids_; - - MediaStreamTrackMetrics::StreamType stream_type_; - scoped_refptr<MediaStreamObserver> observer_; + MediaStreamTrackMetrics::Direction direction_; + MediaStreamTrackMetrics::Kind kind_; + std::string track_id_; // Non-owning. MediaStreamTrackMetrics* owner_; @@ -151,47 +61,46 @@ // Used with std::find_if. struct ObserverFinder { - ObserverFinder(MediaStreamTrackMetrics::StreamType stream_type, - MediaStreamInterface* stream) - : stream_type(stream_type), stream_(stream) {} + ObserverFinder(MediaStreamTrackMetrics::Direction direction, + MediaStreamTrackMetrics::Kind kind, + const std::string& track_id) + : direction_(direction), kind_(kind), track_id_(track_id) {} bool operator()( const std::unique_ptr<MediaStreamTrackMetricsObserver>& observer) { - return stream_ == observer->stream() && - stream_type == observer->stream_type(); + return direction_ == observer->direction() && kind_ == observer->kind() && + track_id_ == observer->track_id(); } - MediaStreamTrackMetrics::StreamType stream_type; - MediaStreamInterface* stream_; + MediaStreamTrackMetrics::Direction direction_; + MediaStreamTrackMetrics::Kind kind_; + std::string track_id_; }; } // namespace MediaStreamTrackMetricsObserver::MediaStreamTrackMetricsObserver( - MediaStreamTrackMetrics::StreamType stream_type, - MediaStreamInterface* stream, + MediaStreamTrackMetrics::Direction direction, + MediaStreamTrackMetrics::Kind kind, + std::string track_id, MediaStreamTrackMetrics* owner) : has_reported_start_(false), has_reported_end_(false), - audio_track_ids_(GetTrackIds(stream->GetAudioTracks())), - video_track_ids_(GetTrackIds(stream->GetVideoTracks())), - stream_type_(stream_type), - observer_(new MediaStreamObserver( - base::Bind(&MediaStreamTrackMetricsObserver::OnChanged, - base::Unretained(this)), - base::ThreadTaskRunnerHandle::Get(), - stream)), + direction_(direction), + kind_(kind), + track_id_(std::move(track_id)), owner_(owner) { + DCHECK(owner); } MediaStreamTrackMetricsObserver::~MediaStreamTrackMetricsObserver() { DCHECK(thread_checker_.CalledOnValidThread()); - observer_->Unregister(); - SendLifetimeMessages(MediaStreamTrackMetrics::DISCONNECTED); + SendLifetimeMessageForTrack( + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected); } -void MediaStreamTrackMetricsObserver::SendLifetimeMessages( +void MediaStreamTrackMetricsObserver::SendLifetimeMessageForTrack( MediaStreamTrackMetrics::LifetimeEvent event) { DCHECK(thread_checker_.CalledOnValidThread()); - if (event == MediaStreamTrackMetrics::CONNECTED) { + if (event == MediaStreamTrackMetrics::LifetimeEvent::kConnected) { // Both ICE CONNECTED and COMPLETED can trigger the first // start-of-life event, so we only report the first. if (has_reported_start_) @@ -199,7 +108,7 @@ DCHECK(!has_reported_start_ && !has_reported_end_); has_reported_start_ = true; } else { - DCHECK(event == MediaStreamTrackMetrics::DISCONNECTED); + DCHECK(event == MediaStreamTrackMetrics::LifetimeEvent::kDisconnected); // We only report the first end-of-life event, since there are // several cases where end-of-life can be reached. We also don't @@ -209,10 +118,9 @@ has_reported_end_ = true; } - ReportTracks(audio_track_ids_, MediaStreamTrackMetrics::AUDIO_TRACK, event); - ReportTracks(video_track_ids_, MediaStreamTrackMetrics::VIDEO_TRACK, event); + owner_->SendLifetimeMessage(track_id_, kind_, event, direction_); - if (event == MediaStreamTrackMetrics::DISCONNECTED) { + if (event == MediaStreamTrackMetrics::LifetimeEvent::kDisconnected) { // After disconnection, we can get reconnected, so we need to // forget that we've sent lifetime events, while retaining all // other state. @@ -222,76 +130,33 @@ } } -void MediaStreamTrackMetricsObserver::OnChanged( - const IdSet& audio_track_ids, const IdSet& video_track_ids) { - DCHECK(thread_checker_.CalledOnValidThread()); - - // We only report changes after our initial report, and never after - // our last report. - if (has_reported_start_ && !has_reported_end_) { - ReportAddedAndRemovedTracks(audio_track_ids, - audio_track_ids_, - MediaStreamTrackMetrics::AUDIO_TRACK); - ReportAddedAndRemovedTracks(video_track_ids, - video_track_ids_, - MediaStreamTrackMetrics::VIDEO_TRACK); - } - - // We always update our sets of tracks. - audio_track_ids_ = audio_track_ids; - video_track_ids_ = video_track_ids; -} - -void MediaStreamTrackMetricsObserver::ReportAddedAndRemovedTracks( - const IdSet& new_ids, - const IdSet& old_ids, - MediaStreamTrackMetrics::TrackType track_type) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(has_reported_start_ && !has_reported_end_); - - IdSet added_tracks = base::STLSetDifference<IdSet>(new_ids, old_ids); - IdSet removed_tracks = base::STLSetDifference<IdSet>(old_ids, new_ids); - - ReportTracks(added_tracks, track_type, MediaStreamTrackMetrics::CONNECTED); - ReportTracks( - removed_tracks, track_type, MediaStreamTrackMetrics::DISCONNECTED); -} - -void MediaStreamTrackMetricsObserver::ReportTracks( - const IdSet& ids, - MediaStreamTrackMetrics::TrackType track_type, - MediaStreamTrackMetrics::LifetimeEvent event) { - DCHECK(thread_checker_.CalledOnValidThread()); - for (IdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) { - owner_->SendLifetimeMessage(*it, track_type, event, stream_type_); - } -} - MediaStreamTrackMetrics::MediaStreamTrackMetrics() : ice_state_(webrtc::PeerConnectionInterface::kIceConnectionNew) {} MediaStreamTrackMetrics::~MediaStreamTrackMetrics() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); for (const auto& observer : observers_) { - observer->SendLifetimeMessages(DISCONNECTED); + observer->SendLifetimeMessageForTrack(LifetimeEvent::kDisconnected); } } -void MediaStreamTrackMetrics::AddStream(StreamType type, - MediaStreamInterface* stream) { +void MediaStreamTrackMetrics::AddTrack(Direction direction, + Kind kind, + const std::string& track_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - observers_.push_back( - std::make_unique<MediaStreamTrackMetricsObserver>(type, stream, this)); + observers_.push_back(std::make_unique<MediaStreamTrackMetricsObserver>( + direction, kind, std::move(track_id), this)); SendLifeTimeMessageDependingOnIceState(observers_.back().get()); } -void MediaStreamTrackMetrics::RemoveStream(StreamType type, - MediaStreamInterface* stream) { +void MediaStreamTrackMetrics::RemoveTrack(Direction direction, + Kind kind, + const std::string& track_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto it = std::find_if(observers_.begin(), observers_.end(), - ObserverFinder(type, stream)); + ObserverFinder(direction, kind, track_id)); if (it == observers_.end()) { - // Since external apps could call removeStream with a stream they + // Since external apps could call removeTrack() with a stream they // never added, this can happen without it being an error. return; } @@ -300,40 +165,41 @@ } void MediaStreamTrackMetrics::IceConnectionChange( - PeerConnectionInterface::IceConnectionState new_state) { + webrtc::PeerConnectionInterface::IceConnectionState new_state) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ice_state_ = new_state; for (const auto& observer : observers_) { SendLifeTimeMessageDependingOnIceState(observer.get()); } } + void MediaStreamTrackMetrics::SendLifeTimeMessageDependingOnIceState( MediaStreamTrackMetricsObserver* observer) { // There is a state transition diagram for these states at // http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCIceConnectionState switch (ice_state_) { - case PeerConnectionInterface::kIceConnectionConnected: - case PeerConnectionInterface::kIceConnectionCompleted: - observer->SendLifetimeMessages(CONNECTED); + case webrtc::PeerConnectionInterface::kIceConnectionConnected: + case webrtc::PeerConnectionInterface::kIceConnectionCompleted: + observer->SendLifetimeMessageForTrack(LifetimeEvent::kConnected); break; - case PeerConnectionInterface::kIceConnectionFailed: - // We don't really need to handle FAILED (it is only supposed - // to be preceded by CHECKING so we wouldn't yet have sent a - // lifetime message) but we might as well use belt and - // suspenders and handle it the same as the other "end call" - // states. It will be ignored anyway if the call is not - // already connected. - case PeerConnectionInterface::kIceConnectionNew: - // It's a bit weird to count NEW as an end-lifetime event, but - // it's possible to transition directly from a connected state - // (CONNECTED or COMPLETED) to NEW, which can then be followed - // by a new connection. The observer will ignore the end - // lifetime event if it was not preceded by a begin-lifetime - // event. - case PeerConnectionInterface::kIceConnectionDisconnected: - case PeerConnectionInterface::kIceConnectionClosed: - observer->SendLifetimeMessages(DISCONNECTED); + case webrtc::PeerConnectionInterface::kIceConnectionFailed: + // We don't really need to handle FAILED (it is only supposed + // to be preceded by CHECKING so we wouldn't yet have sent a + // lifetime message) but we might as well use belt and + // suspenders and handle it the same as the other "end call" + // states. It will be ignored anyway if the call is not + // already connected. + case webrtc::PeerConnectionInterface::kIceConnectionNew: + // It's a bit weird to count NEW as an end-lifetime event, but + // it's possible to transition directly from a connected state + // (CONNECTED or COMPLETED) to NEW, which can then be followed + // by a new connection. The observer will ignore the end + // lifetime event if it was not preceded by a begin-lifetime + // event. + case webrtc::PeerConnectionInterface::kIceConnectionDisconnected: + case webrtc::PeerConnectionInterface::kIceConnectionClosed: + observer->SendLifetimeMessageForTrack(LifetimeEvent::kDisconnected); break; default: @@ -345,28 +211,28 @@ } void MediaStreamTrackMetrics::SendLifetimeMessage(const std::string& track_id, - TrackType track_type, + Kind kind, LifetimeEvent event, - StreamType stream_type) { + Direction direction) { RenderThreadImpl* render_thread = RenderThreadImpl::current(); // |render_thread| can be NULL in certain cases when running as part // |of a unit test. if (render_thread) { - if (event == CONNECTED) { + if (event == LifetimeEvent::kConnected) { GetMediaStreamTrackMetricsHost()->AddTrack( - MakeUniqueId(track_id, stream_type), track_type == AUDIO_TRACK, - stream_type == RECEIVED_STREAM); + MakeUniqueId(track_id, direction), kind == Kind::kAudio, + direction == Direction::kReceive); } else { - DCHECK_EQ(DISCONNECTED, event); + DCHECK_EQ(LifetimeEvent::kDisconnected, event); GetMediaStreamTrackMetricsHost()->RemoveTrack( - MakeUniqueId(track_id, stream_type)); + MakeUniqueId(track_id, direction)); } } } uint64_t MediaStreamTrackMetrics::MakeUniqueIdImpl(uint64_t pc_id, const std::string& track_id, - StreamType stream_type) { + Direction direction) { // We use a hash over the |track| pointer and the PeerConnection ID, // plus a boolean flag indicating whether the track is remote (since // you might conceivably have a remote track added back as a sent @@ -376,10 +242,8 @@ // no longer be considered), just one with virtually zero chance of // collisions when faced with non-malicious data. std::string unique_id_string = - base::StringPrintf("%" PRIu64 " %s %d", - pc_id, - track_id.c_str(), - stream_type == RECEIVED_STREAM ? 1 : 0); + base::StringPrintf("%" PRIu64 " %s %d", pc_id, track_id.c_str(), + direction == Direction::kReceive ? 1 : 0); base::MD5Context ctx; base::MD5Init(&ctx); @@ -392,10 +256,10 @@ } uint64_t MediaStreamTrackMetrics::MakeUniqueId(const std::string& track_id, - StreamType stream_type) { + Direction direction) { return MakeUniqueIdImpl( reinterpret_cast<uint64_t>(reinterpret_cast<void*>(this)), track_id, - stream_type); + direction); } mojom::MediaStreamTrackMetricsHostPtr&
diff --git a/content/renderer/media/webrtc/media_stream_track_metrics.h b/content/renderer/media/webrtc/media_stream_track_metrics.h index 7793186..addd76a 100644 --- a/content/renderer/media/webrtc/media_stream_track_metrics.h +++ b/content/renderer/media/webrtc/media_stream_track_metrics.h
@@ -15,10 +15,6 @@ #include "content/common/media/media_stream.mojom.h" #include "third_party/webrtc/api/peerconnectioninterface.h" -namespace webrtc { -class MediaStreamInterface; -} - namespace content { class MediaStreamTrackMetricsObserver; @@ -36,19 +32,16 @@ explicit MediaStreamTrackMetrics(); ~MediaStreamTrackMetrics(); - enum StreamType { SENT_STREAM, RECEIVED_STREAM }; + enum class Direction { kSend, kReceive }; + enum class Kind { kAudio, kVideo }; + enum class LifetimeEvent { kConnected, kDisconnected }; - enum TrackType { AUDIO_TRACK, VIDEO_TRACK }; + // Starts tracking the lifetime of the track until |RemoveTrack| is called + // or this object's lifetime ends. + void AddTrack(Direction direction, Kind kind, const std::string& track_id); - enum LifetimeEvent { CONNECTED, DISCONNECTED }; - - // Starts tracking lifetimes of all the tracks in |stream| and any - // tracks added or removed to/from the stream until |RemoveStream| - // is called or this object's lifetime ends. - void AddStream(StreamType type, webrtc::MediaStreamInterface* stream); - - // Stops tracking lifetimes of tracks in |stream|. - void RemoveStream(StreamType type, webrtc::MediaStreamInterface* stream); + // Stops tracking the lifetime of the track. + void RemoveTrack(Direction direction, Kind kind, const std::string& track_id); // Called to indicate changes in the ICE connection state for the // PeerConnection this object is associated with. Used to generate @@ -71,9 +64,9 @@ // PeerConnection), false for local streams (sent over a // PeerConnection). virtual void SendLifetimeMessage(const std::string& track_id, - TrackType track_type, + Kind kind, LifetimeEvent lifetime_event, - StreamType stream_type); + Direction direction); protected: // Calls SendLifetimeMessage for |observer| depending on |ice_state_|. @@ -86,12 +79,12 @@ // is a one-to-one relationship). uint64_t MakeUniqueIdImpl(uint64_t pc_id, const std::string& track, - StreamType stream_type); + Direction direction); private: // Make a unique ID for the given track, that is valid while the // track object and the PeerConnection it is attached to both exist. - uint64_t MakeUniqueId(const std::string& track, StreamType stream_type); + uint64_t MakeUniqueId(const std::string& track_id, Direction direction); mojom::MediaStreamTrackMetricsHostPtr& GetMediaStreamTrackMetricsHost();
diff --git a/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc b/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc index d8819aa..4fb4ff3 100644 --- a/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc +++ b/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc
@@ -78,8 +78,7 @@ virtual ~MockMediaStreamTrackMetrics() {} MOCK_METHOD4(SendLifetimeMessage, - void(const std::string&, TrackType, LifetimeEvent, StreamType)); - + void(const std::string&, Kind, LifetimeEvent, Direction)); using MediaStreamTrackMetrics::MakeUniqueIdImpl; }; @@ -170,88 +169,82 @@ // Lower 32 bits the same, upper 32 differ. EXPECT_NE( - metrics_->MakeUniqueIdImpl( - 0x1000000000000001, "x", MediaStreamTrackMetrics::RECEIVED_STREAM), - metrics_->MakeUniqueIdImpl( - 0x2000000000000001, "x", MediaStreamTrackMetrics::RECEIVED_STREAM)); + metrics_->MakeUniqueIdImpl(0x1000000000000001, "x", + MediaStreamTrackMetrics::Direction::kReceive), + metrics_->MakeUniqueIdImpl(0x2000000000000001, "x", + MediaStreamTrackMetrics::Direction::kReceive)); // Track ID differs. EXPECT_NE(metrics_->MakeUniqueIdImpl( - 42, "x", MediaStreamTrackMetrics::RECEIVED_STREAM), + 42, "x", MediaStreamTrackMetrics::Direction::kReceive), metrics_->MakeUniqueIdImpl( - 42, "y", MediaStreamTrackMetrics::RECEIVED_STREAM)); + 42, "y", MediaStreamTrackMetrics::Direction::kReceive)); // Remove vs. local track differs. EXPECT_NE(metrics_->MakeUniqueIdImpl( - 42, "x", MediaStreamTrackMetrics::RECEIVED_STREAM), + 42, "x", MediaStreamTrackMetrics::Direction::kReceive), metrics_->MakeUniqueIdImpl( - 42, "x", MediaStreamTrackMetrics::SENT_STREAM)); + 42, "x", MediaStreamTrackMetrics::Direction::kSend)); } TEST_F(MediaStreamTrackMetricsTest, BasicRemoteStreams) { - scoped_refptr<MockAudioTrackInterface> audio(MakeAudioTrack("audio")); - scoped_refptr<MockVideoTrackInterface> video(MakeVideoTrack("video")); - stream_->AddTrack(audio.get()); - stream_->AddTrack(video.get()); - metrics_->AddStream(MediaStreamTrackMetrics::RECEIVED_STREAM, stream_.get()); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetrics::Kind::kVideo, "video"); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kReceive)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kReceive)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kReceive)); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kReceive)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionDisconnected); } TEST_F(MediaStreamTrackMetricsTest, BasicLocalStreams) { - scoped_refptr<MockAudioTrackInterface> audio(MakeAudioTrack("audio")); - scoped_refptr<MockVideoTrackInterface> video(MakeVideoTrack("video")); - stream_->AddTrack(audio.get()); - stream_->AddTrack(video.get()); - metrics_->AddStream(MediaStreamTrackMetrics::SENT_STREAM, stream_.get()); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kVideo, "video"); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); metrics_->IceConnectionChange(PeerConnectionInterface::kIceConnectionFailed); } @@ -259,341 +252,226 @@ metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); - scoped_refptr<MockAudioTrackInterface> audio(MakeAudioTrack("audio")); - scoped_refptr<MockVideoTrackInterface> video(MakeVideoTrack("video")); - stream_->AddTrack(audio.get()); - stream_->AddTrack(video.get()); - metrics_->AddStream(MediaStreamTrackMetrics::SENT_STREAM, stream_.get()); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kVideo, "video"); } TEST_F(MediaStreamTrackMetricsTest, RemoteStreamAddedAferIceConnect) { metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kReceive)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kReceive)); - scoped_refptr<MockAudioTrackInterface> audio(MakeAudioTrack("audio")); - scoped_refptr<MockVideoTrackInterface> video(MakeVideoTrack("video")); - stream_->AddTrack(audio.get()); - stream_->AddTrack(video.get()); - metrics_->AddStream(MediaStreamTrackMetrics::RECEIVED_STREAM, stream_.get()); -} - -TEST_F(MediaStreamTrackMetricsTest, RemoteStreamTrackAdded) { - scoped_refptr<MockAudioTrackInterface> initial(MakeAudioTrack("initial")); - scoped_refptr<MockAudioTrackInterface> added(MakeAudioTrack("added")); - stream_->AddTrack(initial.get()); - metrics_->AddStream(MediaStreamTrackMetrics::RECEIVED_STREAM, stream_.get()); - - EXPECT_CALL(*metrics_, - SendLifetimeMessage("initial", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); - metrics_->IceConnectionChange( - PeerConnectionInterface::kIceConnectionConnected); - - EXPECT_CALL(*metrics_, - SendLifetimeMessage("added", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); - AddAudioTrack(added.get()); - - EXPECT_CALL(*metrics_, - SendLifetimeMessage("initial", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("added", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); - metrics_->IceConnectionChange(PeerConnectionInterface::kIceConnectionFailed); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetrics::Kind::kVideo, "video"); } TEST_F(MediaStreamTrackMetricsTest, LocalStreamTrackRemoved) { - scoped_refptr<MockAudioTrackInterface> first(MakeAudioTrack("first")); - scoped_refptr<MockAudioTrackInterface> second(MakeAudioTrack("second")); - stream_->AddTrack(first.get()); - stream_->AddTrack(second.get()); - metrics_->AddStream(MediaStreamTrackMetrics::SENT_STREAM, stream_.get()); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "first"); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "second"); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("first", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("second", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "first", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "second", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("first", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - stream_->RemoveTrack(first.get()); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("first", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); + metrics_->RemoveTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "first"); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("second", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("second", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); metrics_->IceConnectionChange(PeerConnectionInterface::kIceConnectionFailed); } -TEST_F(MediaStreamTrackMetricsTest, LocalStreamModificationsBeforeAndAfter) { - scoped_refptr<MockAudioTrackInterface> first(MakeAudioTrack("first")); - scoped_refptr<MockAudioTrackInterface> second(MakeAudioTrack("second")); - stream_->AddTrack(first.get()); - metrics_->AddStream(MediaStreamTrackMetrics::SENT_STREAM, stream_.get()); +TEST_F(MediaStreamTrackMetricsTest, RemoveAfterDisconnect) { + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); - // This gets added after we start observing, but no lifetime message - // should be sent at this point since the call is not connected. It - // should get sent only once it gets connected. - AddAudioTrack(second.get()); - - EXPECT_CALL(*metrics_, - SendLifetimeMessage("first", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("second", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("first", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("second", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); metrics_->IceConnectionChange(PeerConnectionInterface::kIceConnectionFailed); // This happens after the call is disconnected so no lifetime // message should be sent. - RemoveAudioTrack(first.get()); + metrics_->RemoveTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); } TEST_F(MediaStreamTrackMetricsTest, RemoteStreamMultipleDisconnects) { - scoped_refptr<MockAudioTrackInterface> audio(MakeAudioTrack("audio")); - stream_->AddTrack(audio.get()); - metrics_->AddStream(MediaStreamTrackMetrics::RECEIVED_STREAM, stream_.get()); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kReceive)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kReceive)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionDisconnected); metrics_->IceConnectionChange(PeerConnectionInterface::kIceConnectionFailed); - RemoveAudioTrack(audio.get()); + metrics_->RemoveTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); } TEST_F(MediaStreamTrackMetricsTest, RemoteStreamConnectDisconnectTwice) { - scoped_refptr<MockAudioTrackInterface> audio(MakeAudioTrack("audio")); - stream_->AddTrack(audio.get()); - metrics_->AddStream(MediaStreamTrackMetrics::RECEIVED_STREAM, stream_.get()); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); for (size_t i = 0; i < 2; ++i) { - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kReceive)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::RECEIVED_STREAM)); + SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kReceive)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionDisconnected); } - RemoveAudioTrack(audio.get()); + metrics_->RemoveTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); } TEST_F(MediaStreamTrackMetricsTest, LocalStreamRemovedNoDisconnect) { - scoped_refptr<MockAudioTrackInterface> audio(MakeAudioTrack("audio")); - scoped_refptr<MockVideoTrackInterface> video(MakeVideoTrack("video")); - stream_->AddTrack(audio.get()); - stream_->AddTrack(video.get()); - metrics_->AddStream(MediaStreamTrackMetrics::SENT_STREAM, stream_.get()); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kVideo, "video"); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - metrics_->RemoveStream(MediaStreamTrackMetrics::SENT_STREAM, stream_.get()); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); + metrics_->RemoveTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); + metrics_->RemoveTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kVideo, "video"); } TEST_F(MediaStreamTrackMetricsTest, LocalStreamLargerTest) { - scoped_refptr<MockAudioTrackInterface> audio1(MakeAudioTrack("audio1")); - scoped_refptr<MockAudioTrackInterface> audio2(MakeAudioTrack("audio2")); - scoped_refptr<MockAudioTrackInterface> audio3(MakeAudioTrack("audio3")); - scoped_refptr<MockVideoTrackInterface> video1(MakeVideoTrack("video1")); - scoped_refptr<MockVideoTrackInterface> video2(MakeVideoTrack("video2")); - scoped_refptr<MockVideoTrackInterface> video3(MakeVideoTrack("video3")); - stream_->AddTrack(audio1.get()); - stream_->AddTrack(video1.get()); - metrics_->AddStream(MediaStreamTrackMetrics::SENT_STREAM, stream_.get()); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kVideo, "video"); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio1", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video1", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); metrics_->IceConnectionChange( PeerConnectionInterface::kIceConnectionConnected); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio2", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - AddAudioTrack(audio2.get()); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video2", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - AddVideoTrack(video2.get()); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); + metrics_->RemoveTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio1", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - RemoveAudioTrack(audio1.get()); + // Add back audio + EXPECT_CALL(*metrics_, SendLifetimeMessage( + "audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kConnected, + MediaStreamTrackMetrics::Direction::kSend)); + metrics_->AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio3", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - AddAudioTrack(audio3.get()); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video3", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - AddVideoTrack(video3.get()); - - // Add back audio1 - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio1", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::CONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - AddAudioTrack(audio1.get()); - - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio2", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - RemoveAudioTrack(audio2.get()); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video2", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - RemoveVideoTrack(video2.get()); - - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio1", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - RemoveAudioTrack(audio1.get()); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video1", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - RemoveVideoTrack(video1.get()); - - EXPECT_CALL(*metrics_, - SendLifetimeMessage("audio3", - MediaStreamTrackMetrics::AUDIO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - EXPECT_CALL(*metrics_, - SendLifetimeMessage("video3", - MediaStreamTrackMetrics::VIDEO_TRACK, - MediaStreamTrackMetrics::DISCONNECTED, - MediaStreamTrackMetrics::SENT_STREAM)); - metrics_->RemoveStream(MediaStreamTrackMetrics::SENT_STREAM, stream_.get()); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("audio", MediaStreamTrackMetrics::Kind::kAudio, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); + metrics_->RemoveTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kAudio, "audio"); + EXPECT_CALL( + *metrics_, + SendLifetimeMessage("video", MediaStreamTrackMetrics::Kind::kVideo, + MediaStreamTrackMetrics::LifetimeEvent::kDisconnected, + MediaStreamTrackMetrics::Direction::kSend)); + metrics_->RemoveTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetrics::Kind::kVideo, "video"); } } // namespace content
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc index fcf13d5..bd092e8 100644 --- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc +++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -893,6 +893,13 @@ return kSdpSemanticRequestedDefault; } +MediaStreamTrackMetrics::Kind MediaStreamTrackMetricsKind( + const blink::WebMediaStreamTrack& track) { + return track.Source().GetType() == blink::WebMediaStreamSource::kTypeAudio + ? MediaStreamTrackMetrics::Kind::kAudio + : MediaStreamTrackMetrics::Kind::kVideo; +} + } // namespace // Implementation of LocalRTCStatsRequest. @@ -1826,6 +1833,9 @@ webrtc_streams); if (!webrtc_sender) return nullptr; + track_metrics_.AddTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetricsKind(track), + track.Id().Utf8()); DCHECK(FindSender(RTCRtpSender::getId(webrtc_sender)) == rtp_senders_.end()); rtp_senders_.push_back(std::make_unique<RTCRtpSender>( native_peer_connection_, task_runner_, signaling_thread(), @@ -1840,8 +1850,6 @@ if (GetLocalStreamUsageCount(rtp_senders_, stream_ref->adapter().web_stream()) == 1u) { PerSessionWebRTCAPIMetrics::GetInstance()->IncrementStreamCounter(); - track_metrics_.AddStream(MediaStreamTrackMetrics::SENT_STREAM, - stream_ref->adapter().webrtc_stream().get()); } } return rtp_senders_.back()->ShallowCopy(); @@ -1850,11 +1858,15 @@ bool RTCPeerConnectionHandler::RemoveTrack(blink::WebRTCRtpSender* web_sender) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::RemoveTrack"); + auto web_track = web_sender->Track(); auto it = FindSender(web_sender->Id()); if (it == rtp_senders_.end()) return false; if (!(*it)->RemoveFromPeerConnection(native_peer_connection_.get())) return false; + track_metrics_.RemoveTrack(MediaStreamTrackMetrics::Direction::kSend, + MediaStreamTrackMetricsKind(web_track), + web_track.Id().Utf8()); auto stream_refs = (*it)->stream_refs(); if (peer_connection_tracker_) { peer_connection_tracker_->TrackRemoveTransceiver( @@ -1869,8 +1881,6 @@ if (GetLocalStreamUsageCount(rtp_senders_, stream_ref->adapter().web_stream()) == 0u) { PerSessionWebRTCAPIMetrics::GetInstance()->DecrementStreamCounter(); - track_metrics_.RemoveStream(MediaStreamTrackMetrics::SENT_STREAM, - stream_ref->adapter().webrtc_stream().get()); } } return true; @@ -2062,7 +2072,10 @@ remote_stream_adapter_refs) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::OnAddRemoteTrack"); - + auto web_track = remote_track_adapter_ref->web_track(); + track_metrics_.AddTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetricsKind(web_track), + web_track.Id().Utf8()); for (const auto& remote_stream_adapter_ref : remote_stream_adapter_refs) { // New remote stream? if (GetRemoteStreamUsageCount( @@ -2070,9 +2083,6 @@ remote_stream_adapter_ref->adapter().webrtc_stream().get()) == 0) { // Update metrics. PerSessionWebRTCAPIMetrics::GetInstance()->IncrementStreamCounter(); - track_metrics_.AddStream( - MediaStreamTrackMetrics::RECEIVED_STREAM, - remote_stream_adapter_ref->adapter().webrtc_stream().get()); } } @@ -2116,8 +2126,12 @@ nullptr /* sender */, it->second->ShallowCopy() /* receiver */); } remote_stream_adapter_refs = it->second->StreamAdapterRefs(); + auto receiver = it->second->ShallowCopy(); + track_metrics_.RemoveTrack(MediaStreamTrackMetrics::Direction::kReceive, + MediaStreamTrackMetricsKind(receiver->Track()), + receiver->Track().Id().Utf8()); if (!is_closed_) - client_->DidRemoveRemoteTrack(it->second->ShallowCopy()); + client_->DidRemoveRemoteTrack(std::move(receiver)); rtp_receivers_.erase(it); } @@ -2127,9 +2141,6 @@ rtp_receivers_, remote_stream_adapter_ref->adapter().webrtc_stream().get()) == 0) { // Update metrics. - track_metrics_.RemoveStream( - MediaStreamTrackMetrics::RECEIVED_STREAM, - remote_stream_adapter_ref->adapter().webrtc_stream().get()); PerSessionWebRTCAPIMetrics::GetInstance()->DecrementStreamCounter(); } }
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc index e03d171..a20adaaa 100644 --- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc +++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc
@@ -63,22 +63,24 @@ remote_track_can_complete_initialization_( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED), - is_initialized_(false) { + is_initialized_(false), + is_disposed_(false) { DCHECK(factory_); DCHECK(main_thread_); } WebRtcMediaStreamTrackAdapter::~WebRtcMediaStreamTrackAdapter() { DCHECK(!remote_track_can_complete_initialization_.IsSignaled()); - DCHECK(!is_initialized_); + DCHECK(is_disposed_); } void WebRtcMediaStreamTrackAdapter::Dispose() { DCHECK(main_thread_->BelongsToCurrentThread()); - if (!is_initialized_) + DCHECK(is_initialized_); + if (is_disposed_) return; remote_track_can_complete_initialization_.Reset(); - is_initialized_ = false; + is_disposed_ = true; if (web_track_.Source().GetType() == blink::WebMediaStreamSource::kTypeAudio) { if (local_track_audio_sink_) @@ -99,10 +101,19 @@ return is_initialized_; } +void WebRtcMediaStreamTrackAdapter::InitializeOnMainThread() { + DCHECK(main_thread_->BelongsToCurrentThread()); + if (is_initialized_) + return; + // TODO(hbos): Only ever initialize explicitly, + // remove EnsureTrackIsInitialized(). https://crbug.com/857458 + EnsureTrackIsInitialized(); +} + const blink::WebMediaStreamTrack& WebRtcMediaStreamTrackAdapter::web_track() { DCHECK(main_thread_->BelongsToCurrentThread()); - DCHECK(!web_track_.IsNull()); EnsureTrackIsInitialized(); + DCHECK(!web_track_.IsNull()); return web_track_; } @@ -231,6 +242,7 @@ } void WebRtcMediaStreamTrackAdapter::EnsureTrackIsInitialized() { + DCHECK(main_thread_->BelongsToCurrentThread()); if (is_initialized_) return; @@ -242,7 +254,6 @@ void WebRtcMediaStreamTrackAdapter::DisposeLocalAudioTrack() { DCHECK(main_thread_->BelongsToCurrentThread()); - DCHECK(!is_initialized_); DCHECK(local_track_audio_sink_); DCHECK_EQ(web_track_.Source().GetType(), blink::WebMediaStreamSource::kTypeAudio); @@ -256,7 +267,6 @@ void WebRtcMediaStreamTrackAdapter::DisposeLocalVideoTrack() { DCHECK(main_thread_->BelongsToCurrentThread()); - DCHECK(!is_initialized_); DCHECK(local_track_video_sink_); DCHECK_EQ(web_track_.Source().GetType(), blink::WebMediaStreamSource::kTypeVideo); @@ -267,7 +277,6 @@ void WebRtcMediaStreamTrackAdapter::DisposeRemoteAudioTrack() { DCHECK(main_thread_->BelongsToCurrentThread()); - DCHECK(!is_initialized_); DCHECK(remote_audio_track_adapter_); DCHECK_EQ(web_track_.Source().GetType(), blink::WebMediaStreamSource::kTypeAudio); @@ -280,7 +289,6 @@ void WebRtcMediaStreamTrackAdapter::DisposeRemoteVideoTrack() { DCHECK(main_thread_->BelongsToCurrentThread()); - DCHECK(!is_initialized_); DCHECK(remote_video_track_adapter_); DCHECK_EQ(web_track_.Source().GetType(), blink::WebMediaStreamSource::kTypeVideo); @@ -300,7 +308,7 @@ void WebRtcMediaStreamTrackAdapter::FinalizeRemoteTrackDisposingOnMainThread() { DCHECK(main_thread_->BelongsToCurrentThread()); - DCHECK(!is_initialized_); + DCHECK(is_disposed_); remote_audio_track_adapter_ = nullptr; remote_video_track_adapter_ = nullptr; webrtc_track_ = nullptr;
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h index 722738c3..8be75b5 100644 --- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h +++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h
@@ -53,6 +53,7 @@ void Dispose(); bool is_initialized() const; + void InitializeOnMainThread(); // These methods must be called on the main thread. // TODO(hbos): Allow these methods to be called on any thread and make them // const. https://crbug.com/756436 @@ -116,6 +117,7 @@ // completed on the main thread. base::WaitableEvent remote_track_can_complete_initialization_; bool is_initialized_; + bool is_disposed_; blink::WebMediaStreamTrack web_track_; scoped_refptr<webrtc::MediaStreamTrackInterface> webrtc_track_; // If the track is local, a sink is added to the local webrtc track that is
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.cc b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.cc index 28d6c17..de3f036b 100644 --- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.cc +++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.cc
@@ -59,6 +59,17 @@ return base::WrapUnique(new AdapterRef(map_, type_, adapter_)); } +void WebRtcMediaStreamTrackAdapterMap::AdapterRef::InitializeOnMainThread() { + adapter_->InitializeOnMainThread(); + if (type_ == WebRtcMediaStreamTrackAdapterMap::AdapterRef::Type::kRemote) { + base::AutoLock scoped_lock(map_->lock_); + if (!map_->remote_track_adapters_.FindBySecondary(web_track().UniqueId())) { + map_->remote_track_adapters_.SetSecondaryKey(webrtc_track(), + web_track().UniqueId()); + } + } +} + WebRtcMediaStreamTrackAdapterMap::WebRtcMediaStreamTrackAdapterMap( PeerConnectionDependencyFactory* const factory, scoped_refptr<base::SingleThreadTaskRunner> main_thread) @@ -182,13 +193,13 @@ // entry as its secondary key. This ensures that there is at least one // |AdapterRef| alive until after the adapter is initialized and its secondary // key is set. + auto adapter_ref = base::WrapUnique( + new AdapterRef(this, AdapterRef::Type::kRemote, new_adapter)); main_thread_->PostTask( FROM_HERE, base::BindOnce( - &WebRtcMediaStreamTrackAdapterMap::OnRemoteTrackAdapterInitialized, - this, - base::WrapUnique( - new AdapterRef(this, AdapterRef::Type::kRemote, new_adapter)))); + &WebRtcMediaStreamTrackAdapterMap::AdapterRef::InitializeOnMainThread, + std::move(adapter_ref))); return base::WrapUnique( new AdapterRef(this, AdapterRef::Type::kRemote, new_adapter)); } @@ -198,14 +209,4 @@ return remote_track_adapters_.PrimarySize(); } -void WebRtcMediaStreamTrackAdapterMap::OnRemoteTrackAdapterInitialized( - std::unique_ptr<AdapterRef> adapter_ref) { - DCHECK(adapter_ref->is_initialized()); - { - base::AutoLock scoped_lock(lock_); - remote_track_adapters_.SetSecondaryKey(adapter_ref->webrtc_track(), - adapter_ref->web_track().UniqueId()); - } -} - } // namespace content
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h index c7ef9ed..8c9cbf1 100644 --- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h +++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h
@@ -38,6 +38,7 @@ std::unique_ptr<AdapterRef> Copy() const; bool is_initialized() const { return adapter_->is_initialized(); } + void InitializeOnMainThread(); const blink::WebMediaStreamTrack& web_track() const { return adapter_->web_track(); } @@ -137,8 +138,6 @@ // Invoke on the main thread. virtual ~WebRtcMediaStreamTrackAdapterMap(); - void OnRemoteTrackAdapterInitialized(std::unique_ptr<AdapterRef> adapter_ref); - // Pointer to a |PeerConnectionDependencyFactory| owned by the |RenderThread|. // It's valid for the lifetime of |RenderThread|. PeerConnectionDependencyFactory* const factory_;
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc index 8c5fd3e..a141ce50 100644 --- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc +++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc
@@ -53,8 +53,8 @@ } std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> - GetOrCreateRemoteTrackAdapter( - webrtc::MediaStreamTrackInterface* webrtc_track) { + GetOrCreateRemoteTrackAdapter(webrtc::MediaStreamTrackInterface* webrtc_track, + bool wait_for_initialization = true) { DCHECK(main_thread_->BelongsToCurrentThread()); std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> adapter; signaling_thread()->PostTask( @@ -63,7 +63,13 @@ GetOrCreateRemoteTrackAdapterOnSignalingThread, base::Unretained(this), base::Unretained(webrtc_track), &adapter)); - RunMessageLoopsUntilIdle(); + RunMessageLoopsUntilIdle(wait_for_initialization); + DCHECK(adapter); + if (wait_for_initialization) { + DCHECK(adapter->is_initialized()); + } else { + DCHECK(!adapter->is_initialized()); + } return adapter; } @@ -76,7 +82,7 @@ // Runs message loops on the webrtc signaling thread and the main thread until // idle. - void RunMessageLoopsUntilIdle() { + void RunMessageLoopsUntilIdle(bool run_loop_on_main_thread = true) { DCHECK(main_thread_->BelongsToCurrentThread()); base::WaitableEvent waitable_event( base::WaitableEvent::ResetPolicy::MANUAL, @@ -86,7 +92,8 @@ RunMessageLoopUntilIdleOnSignalingThread, base::Unretained(this), &waitable_event)); waitable_event.Wait(); - base::RunLoop().RunUntilIdle(); + if (run_loop_on_main_thread) + base::RunLoop().RunUntilIdle(); } void RunMessageLoopUntilIdleOnSignalingThread( @@ -168,6 +175,29 @@ } TEST_F(WebRtcMediaStreamTrackAdapterMapTest, + InitializeRemoteTrackAdapterExplicitly) { + scoped_refptr<MockWebRtcAudioTrack> webrtc_track = + MockWebRtcAudioTrack::Create("remote_track"); + std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> adapter_ref = + GetOrCreateRemoteTrackAdapter(webrtc_track.get(), false); + EXPECT_FALSE(adapter_ref->is_initialized()); + adapter_ref->InitializeOnMainThread(); + EXPECT_TRUE(adapter_ref->is_initialized()); + + EXPECT_EQ(1u, map_->GetRemoteTrackCount()); + // Ensure the implicit initialization's posted task is run after it is already + // initialized. + RunMessageLoopsUntilIdle(); + // Destroying all references to the adapter should remove it from the map and + // dispose it. + adapter_ref.reset(); + EXPECT_EQ(0u, map_->GetRemoteTrackCount()); + EXPECT_EQ(nullptr, map_->GetRemoteTrackAdapter(webrtc_track.get())); + // Allow the disposing of track to occur. + RunMessageLoopsUntilIdle(); +} + +TEST_F(WebRtcMediaStreamTrackAdapterMapTest, LocalAndRemoteTrackAdaptersWithSameID) { // Local and remote tracks should be able to use the same id without conflict. const char* id = "id";
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc index 7ca513b..74f586d4 100644 --- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc +++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc
@@ -37,7 +37,6 @@ if (track_adapter_) { EXPECT_TRUE(track_adapter_->is_initialized()); track_adapter_->Dispose(); - EXPECT_FALSE(track_adapter_->is_initialized()); track_adapter_ = nullptr; RunMessageLoopsUntilIdle(); } @@ -80,9 +79,9 @@ dependency_factory_.get(), main_thread_, webrtc_track); } - // Runs message loops on the webrtc signaling thread and the main thread until - // idle. - void RunMessageLoopsUntilIdle() { + // Runs message loops on the webrtc signaling thread and optionally the main + // thread until idle. + void RunMessageLoopsUntilIdle(bool run_loop_on_main_thread = true) { base::WaitableEvent waitable_event( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); @@ -91,7 +90,8 @@ RunMessageLoopUntilIdleOnSignalingThread, base::Unretained(this), &waitable_event)); waitable_event.Wait(); - base::RunLoop().RunUntilIdle(); + if (run_loop_on_main_thread) + base::RunLoop().RunUntilIdle(); } void RunMessageLoopUntilIdleOnSignalingThread( @@ -157,6 +157,7 @@ base::BindOnce( &WebRtcMediaStreamTrackAdapterTest::CreateRemoteTrackAdapter, base::Unretained(this), base::Unretained(webrtc_track.get()))); + // The adapter is initialized implicitly in a PostTask, allow it to run. RunMessageLoopsUntilIdle(); DCHECK(track_adapter_); EXPECT_TRUE(track_adapter_->is_initialized()); @@ -181,6 +182,7 @@ base::BindOnce( &WebRtcMediaStreamTrackAdapterTest::CreateRemoteTrackAdapter, base::Unretained(this), base::Unretained(webrtc_track.get()))); + // The adapter is initialized implicitly in a PostTask, allow it to run. RunMessageLoopsUntilIdle(); DCHECK(track_adapter_); EXPECT_TRUE(track_adapter_->is_initialized()); @@ -197,4 +199,33 @@ track_adapter_->GetRemoteVideoTrackAdapterForTesting()->initialized()); } +TEST_F(WebRtcMediaStreamTrackAdapterTest, RemoteTrackExplicitlyInitialized) { + scoped_refptr<MockWebRtcAudioTrack> webrtc_track = + MockWebRtcAudioTrack::Create("remote_audio_track"); + dependency_factory_->GetWebRtcSignalingThread()->PostTask( + FROM_HERE, + base::BindOnce( + &WebRtcMediaStreamTrackAdapterTest::CreateRemoteTrackAdapter, + base::Unretained(this), base::Unretained(webrtc_track.get()))); + // Wait for the CreateRemoteTrackAdapter() call, but don't run the main thread + // loop that would have implicitly initialized the adapter. + RunMessageLoopsUntilIdle(false); + DCHECK(track_adapter_); + EXPECT_FALSE(track_adapter_->is_initialized()); + // Explicitly initialize before the main thread loop has a chance to run. + track_adapter_->InitializeOnMainThread(); + EXPECT_TRUE(track_adapter_->is_initialized()); + EXPECT_TRUE(!track_adapter_->web_track().IsNull()); + EXPECT_EQ(track_adapter_->web_track().Source().GetType(), + blink::WebMediaStreamSource::kTypeAudio); + EXPECT_TRUE(track_adapter_->webrtc_track()); + EXPECT_EQ(track_adapter_->webrtc_track()->kind(), + webrtc::MediaStreamTrackInterface::kAudioKind); + EXPECT_EQ(track_adapter_->webrtc_track()->id().c_str(), + track_adapter_->web_track().Id()); + EXPECT_TRUE(track_adapter_->GetRemoteAudioTrackAdapterForTesting()); + EXPECT_TRUE( + track_adapter_->GetRemoteAudioTrackAdapterForTesting()->initialized()); +} + } // namespace content
diff --git a/content/renderer/mus/BUILD.gn b/content/renderer/mus/BUILD.gn index 297195a..3fbd631 100644 --- a/content/renderer/mus/BUILD.gn +++ b/content/renderer/mus/BUILD.gn
@@ -18,6 +18,10 @@ configs += [ "//content:content_implementation" ] + public_deps = [ + "//content/public/common:common_sources", + ] + deps = [ "//base", "//cc", @@ -25,7 +29,6 @@ "//components/viz/client", "//content/common", "//content/public/child:child_sources", - "//content/public/common:common_sources", "//media/mojo/interfaces:remoting", "//services/service_manager/public/cpp", "//services/ui/public/cpp",
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc index 0bc1692..59cafc6 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -205,13 +205,17 @@ class RendererBlinkPlatformImpl::SandboxSupport : public blink::WebSandboxSupport { public: +#if defined(OS_LINUX) + explicit SandboxSupport(sk_sp<font_service::FontLoader> font_loader) + : font_loader_(std::move(font_loader)) {} +#endif ~SandboxSupport() override {} #if defined(OS_MACOSX) bool LoadFont(CTFontRef src_font, CGFontRef* container, uint32_t* font_id) override; -#elif defined(OS_POSIX) +#elif defined(OS_LINUX) void GetFallbackFontForCharacter( blink::WebUChar32 character, const char* preferred_locale, @@ -229,6 +233,7 @@ // here. base::Lock unicode_font_families_mutex_; std::map<int32_t, blink::WebFallbackFont> unicode_font_families_; + sk_sp<font_service::FontLoader> font_loader_; #endif }; #endif // !defined(OS_ANDROID) && !defined(OS_WIN) @@ -247,13 +252,6 @@ is_locked_to_site_(false), default_task_runner_(main_thread_scheduler->DefaultTaskRunner()), main_thread_scheduler_(main_thread_scheduler) { -#if !defined(OS_ANDROID) && !defined(OS_WIN) && !defined(OS_FUCHSIA) - if (g_sandbox_enabled && sandboxEnabled()) { - sandbox_support_.reset(new RendererBlinkPlatformImpl::SandboxSupport); - } else { - DVLOG(1) << "Disabling sandbox support for testing."; - } -#endif // RenderThread may not exist in some tests. if (RenderThreadImpl::current()) { @@ -267,11 +265,28 @@ web_idb_factory_.reset(new WebIDBFactoryImpl( sync_message_filter_, RenderThreadImpl::current()->GetIOTaskRunner().get())); +#if defined(OS_LINUX) + font_loader_ = sk_make_sp<font_service::FontLoader>(connector_.get()); + SkFontConfigInterface::SetGlobal(font_loader_); +#endif } else { service_manager::mojom::ConnectorRequest request; connector_ = service_manager::Connector::Create(&request); } +#if !defined(OS_ANDROID) && !defined(OS_WIN) && !defined(OS_FUCHSIA) + if (g_sandbox_enabled && sandboxEnabled()) { +#if defined(OS_MACOSX) + sandbox_support_.reset(new RendererBlinkPlatformImpl::SandboxSupport()); +#else + sandbox_support_.reset( + new RendererBlinkPlatformImpl::SandboxSupport(font_loader_)); +#endif + } else { + DVLOG(1) << "Disabling sandbox support for testing."; + } +#endif + blink_interface_provider_.reset( new BlinkInterfaceProviderImpl(connector_.get())); top_level_blame_context_.Initialize(); @@ -592,8 +607,8 @@ return; } - content::GetFallbackFontForCharacter(character, preferred_locale, - fallbackFont); + content::GetFallbackFontForCharacter(font_loader_, character, + preferred_locale, fallbackFont); unicode_font_families_.insert(std::make_pair(character, *fallbackFont)); } @@ -604,8 +619,8 @@ bool is_italic, float device_scale_factor, blink::WebFontRenderStyle* out) { - GetRenderStyleForStrike(family, size, is_bold, is_italic, device_scale_factor, - out); + GetRenderStyleForStrike(font_loader_, family, size, is_bold, is_italic, + device_scale_factor, out); } #endif
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h index 94d5e7b2..a068ebb9 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h
@@ -29,6 +29,11 @@ #include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h" #include "third_party/blink/public/platform/modules/webdatabase/web_database.mojom.h" +#if defined(OS_LINUX) +#include "components/services/font/public/cpp/font_loader.h" // nogncheck +#include "third_party/skia/include/core/SkRefCnt.h" // nogncheck +#endif + namespace IPC { class SyncMessageFilter; } @@ -319,6 +324,10 @@ blink::mojom::WebDatabaseHostPtrInfo web_database_host_info_; scoped_refptr<blink::mojom::ThreadSafeWebDatabaseHostPtr> web_database_host_; +#if defined(OS_LINUX) + sk_sp<font_service::FontLoader> font_loader_; +#endif + THREAD_CHECKER(main_thread_checker_); DISALLOW_COPY_AND_ASSIGN(RendererBlinkPlatformImpl);
diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc index 30e9f57..0b722923 100644 --- a/content/renderer/renderer_main.cc +++ b/content/renderer/renderer_main.cc
@@ -43,14 +43,6 @@ #include "base/android/library_loader/library_loader_hooks.h" #endif // OS_ANDROID -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && \ - !defined(OS_FUCHSIA) -#include "content/common/font_config_ipc_linux.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" -#include "services/service_manager/zygote/common/common_sandbox_support_linux.h" -#include "third_party/skia/include/ports/SkFontConfigInterface.h" -#endif - #if defined(OS_MACOSX) #include <Carbon/Carbon.h> #include <signal.h> @@ -121,16 +113,6 @@ } #endif -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && \ - !defined(OS_FUCHSIA) - // This call could already have been made from zygote_main_linux.cc. However - // we need to do it here if Zygote is disabled. - if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote)) { - SkFontConfigInterface::SetGlobal( - sk_make_sp<FontConfigIPC>(service_manager::GetSandboxFD())); - } -#endif - InitializeSkia(); // This function allows pausing execution using the --renderer-startup-dialog
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc index b10fed5..5ff5de0 100644 --- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc +++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -55,8 +55,10 @@ "EmbeddedWorkerInstanceClientImpl::StartWorker"); auto start_timing = mojom::EmbeddedWorkerStartTiming::New(); start_timing->start_worker_received_time = base::TimeTicks::Now(); - service_manager::mojom::InterfaceProviderPtr interface_provider( - std::move(params->provider_info->interface_provider)); + blink::mojom::CacheStoragePtrInfo cache_storage = + std::move(params->provider_info->cache_storage); + service_manager::mojom::InterfaceProviderPtrInfo interface_provider = + std::move(params->provider_info->interface_provider); auto client = std::make_unique<ServiceWorkerContextClient>( params->embedded_worker_id, params->service_worker_version_id, @@ -77,6 +79,7 @@ "ServiceWorker.EmbeddedWorkerInstanceClient.StartWorker", metric, StartWorkerHistogramEnum::NUM_TYPES); wrapper_ = StartWorkerContext(std::move(params), std::move(client), + std::move(cache_storage), std::move(interface_provider)); } @@ -133,7 +136,8 @@ EmbeddedWorkerInstanceClientImpl::StartWorkerContext( mojom::EmbeddedWorkerStartParamsPtr params, std::unique_ptr<ServiceWorkerContextClient> context_client, - service_manager::mojom::InterfaceProviderPtr interface_provider) { + blink::mojom::CacheStoragePtrInfo cache_storage, + service_manager::mojom::InterfaceProviderPtrInfo interface_provider) { std::unique_ptr<blink::WebServiceWorkerInstalledScriptsManager> manager; // |installed_scripts_info| is null if scripts should be served by net layer, // when the worker is not installed, or the worker is launched for checking @@ -147,7 +151,7 @@ std::make_unique<WorkerWrapper>(blink::WebEmbeddedWorker::Create( std::move(context_client), std::move(manager), params->content_settings_proxy.PassHandle(), - interface_provider.PassInterface().PassHandle())); + cache_storage.PassHandle(), interface_provider.PassHandle())); blink::WebEmbeddedWorkerStartData start_data; start_data.script_url = params->script_url;
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.h b/content/renderer/service_worker/embedded_worker_instance_client_impl.h index 8d24349..6c942b7 100644 --- a/content/renderer/service_worker/embedded_worker_instance_client_impl.h +++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
@@ -101,7 +101,8 @@ std::unique_ptr<WorkerWrapper> StartWorkerContext( mojom::EmbeddedWorkerStartParamsPtr params, std::unique_ptr<ServiceWorkerContextClient> context_client, - service_manager::mojom::InterfaceProviderPtr interface_provider); + blink::mojom::CacheStoragePtrInfo cache_storage, + service_manager::mojom::InterfaceProviderPtrInfo interface_provider); mojo::Binding<mojom::EmbeddedWorkerInstanceClient> binding_;
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index 4ca446be..ab42e81 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn
@@ -352,6 +352,10 @@ ] } + if (is_linux && !is_android) { + deps += [ "//components/services/font:lib" ] + } + if (use_x11) { # Some tests rely on this tool at runtime. Note: it might be better if # the tests that needed it had this as a dep instead of adding it here. @@ -892,6 +896,10 @@ service_manifest("content_shell_packaged_services_manifest_overlay") { source = "//content/shell/browser/content_shell_packaged_services_manifest_overlay.json" packaged_services = [ "//services/test/echo:manifest" ] + + if (is_linux && !is_android) { + packaged_services += [ "//components/services/font:manifest" ] + } } group("content_shell_crash_test") {
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc index 2a7e3b5..904da49b 100644 --- a/content/shell/browser/shell_content_browser_client.cc +++ b/content/shell/browser/shell_content_browser_client.cc
@@ -65,6 +65,11 @@ #include "content/public/common/content_descriptors.h" #endif +#if defined(OS_LINUX) +#include "components/services/font/font_service_app.h" // nogncheck +#include "components/services/font/public/interfaces/constants.mojom.h" // nogncheck +#endif + #if defined(OS_WIN) #include "sandbox/win/src/sandbox.h" #include "services/service_manager/sandbox/win/sandbox_win.h" @@ -227,6 +232,10 @@ base::BindRepeating(&base::ASCIIToUTF16, "Test Service"); (*services)[echo::mojom::kServiceName] = base::BindRepeating(&base::ASCIIToUTF16, "Echo Service"); +#if defined(OS_LINUX) + (*services)[font_service::mojom::kServiceName] = + base::BindRepeating(&base::ASCIIToUTF16, "Font Service"); +#endif } bool ShellContentBrowserClient::ShouldTerminateOnServiceQuit(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 5648249a..10e7cf3 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1058,10 +1058,7 @@ } if (is_linux) { - sources += [ - "../browser/linux_ipc_browsertest.cc", - "../zygote/zygote_browsertest.cc", - ] + sources += [ "../zygote/zygote_browsertest.cc" ] deps += [ "//ui/gfx:test_support" ] }
diff --git a/content/utility/BUILD.gn b/content/utility/BUILD.gn index 610106e..117ae90 100644 --- a/content/utility/BUILD.gn +++ b/content/utility/BUILD.gn
@@ -68,6 +68,10 @@ "//media/mojo/services", ] } + + if (is_linux && !is_android) { + deps += [ "//components/services/font:lib" ] + } } # See comment at the top of //content/BUILD.gn for how this works.
diff --git a/content/utility/utility_blink_platform_with_sandbox_support_impl.cc b/content/utility/utility_blink_platform_with_sandbox_support_impl.cc index a3987953..13ab692 100644 --- a/content/utility/utility_blink_platform_with_sandbox_support_impl.cc +++ b/content/utility/utility_blink_platform_with_sandbox_support_impl.cc
@@ -13,6 +13,8 @@ #elif defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) #include "base/synchronization/lock.h" #include "content/child/child_process_sandbox_support_impl_linux.h" +#include "content/child/child_thread_impl.h" +#include "services/service_manager/public/cpp/connector.h" #include "third_party/blink/public/platform/linux/web_fallback_font.h" #include "third_party/blink/public/platform/linux/web_sandbox_support.h" #endif @@ -30,6 +32,10 @@ class UtilityBlinkPlatformWithSandboxSupportImpl::SandboxSupport : public blink::WebSandboxSupport { public: +#if defined(OS_LINUX) + explicit SandboxSupport(sk_sp<font_service::FontLoader> font_loader) + : font_loader_(std::move(font_loader)) {} +#endif ~SandboxSupport() override {} #if defined(OS_MACOSX) @@ -53,14 +59,20 @@ base::Lock unicode_font_families_mutex_; // Maps unicode chars to their fallback fonts. std::map<int32_t, blink::WebFallbackFont> unicode_font_families_; + sk_sp<font_service::FontLoader> font_loader_; #endif // defined(OS_MACOSX) }; #endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) UtilityBlinkPlatformWithSandboxSupportImpl:: - UtilityBlinkPlatformWithSandboxSupportImpl() { -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) + UtilityBlinkPlatformWithSandboxSupportImpl( + service_manager::Connector* connector) { +#if defined(OS_LINUX) + font_loader_ = sk_make_sp<font_service::FontLoader>(connector); + SkFontConfigInterface::SetGlobal(font_loader_); + sandbox_support_ = std::make_unique<SandboxSupport>(font_loader_); +#elif defined(OS_MACOSX) sandbox_support_ = std::make_unique<SandboxSupport>(); #endif } @@ -105,9 +117,8 @@ fallback_font->is_italic = iter->second.is_italic; return; } - - content::GetFallbackFontForCharacter(character, preferred_locale, - fallback_font); + content::GetFallbackFontForCharacter(font_loader_, character, + preferred_locale, fallback_font); unicode_font_families_.emplace(character, *fallback_font); } @@ -118,8 +129,8 @@ bool is_italic, float device_scale_factor, blink::WebFontRenderStyle* out) { - GetRenderStyleForStrike(family, size, is_bold, is_italic, device_scale_factor, - out); + GetRenderStyleForStrike(font_loader_, family, size, is_bold, is_italic, + device_scale_factor, out); } #endif
diff --git a/content/utility/utility_blink_platform_with_sandbox_support_impl.h b/content/utility/utility_blink_platform_with_sandbox_support_impl.h index db4236a..bdb6e9e 100644 --- a/content/utility/utility_blink_platform_with_sandbox_support_impl.h +++ b/content/utility/utility_blink_platform_with_sandbox_support_impl.h
@@ -11,10 +11,19 @@ #include "build/build_config.h" #include "content/utility/utility_blink_platform_impl.h" +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) +#include "components/services/font/public/cpp/font_loader.h" // nogncheck +#include "third_party/skia/include/core/SkRefCnt.h" // nogncheck +#endif + namespace blink { class WebSandboxSupport; } +namespace service_manager { +class Connector; +} + namespace content { // This class extends from UtilityBlinkPlatformImpl with added blink web @@ -22,7 +31,9 @@ class UtilityBlinkPlatformWithSandboxSupportImpl : public UtilityBlinkPlatformImpl { public: - UtilityBlinkPlatformWithSandboxSupportImpl(); + UtilityBlinkPlatformWithSandboxSupportImpl() = delete; + explicit UtilityBlinkPlatformWithSandboxSupportImpl( + service_manager::Connector*); ~UtilityBlinkPlatformWithSandboxSupportImpl() override; // BlinkPlatformImpl @@ -33,6 +44,9 @@ class SandboxSupport; std::unique_ptr<SandboxSupport> sandbox_support_; #endif +#if defined(OS_LINUX) + sk_sp<font_service::FontLoader> font_loader_; +#endif DISALLOW_COPY_AND_ASSIGN(UtilityBlinkPlatformWithSandboxSupportImpl); };
diff --git a/content/utility/utility_service_factory.cc b/content/utility/utility_service_factory.cc index 4d2eb95..2d0a82de 100644 --- a/content/utility/utility_service_factory.cc +++ b/content/utility/utility_service_factory.cc
@@ -45,6 +45,11 @@ #endif // BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION) #endif +#if defined(OS_LINUX) +#include "components/services/font/font_service_app.h" // nogncheck +#include "components/services/font/public/interfaces/constants.mojom.h" // nogncheck +#endif + #if defined(OS_WIN) #include "sandbox/win/src/sandbox.h" @@ -170,6 +175,14 @@ services->insert( std::make_pair(content::mojom::kNetworkServiceName, network_info)); } + +#if defined(OS_LINUX) + service_manager::EmbeddedServiceInfo font_service_info; + font_service_info.factory = + base::BindRepeating(&font_service::FontServiceApp::CreateService); + services->insert( + std::make_pair(font_service::mojom::kServiceName, font_service_info)); +#endif } void UtilityServiceFactory::OnServiceQuit() {
diff --git a/content/utility/utility_thread_impl.cc b/content/utility/utility_thread_impl.cc index 767ee872..65b164f 100644 --- a/content/utility/utility_thread_impl.cc +++ b/content/utility/utility_thread_impl.cc
@@ -118,7 +118,8 @@ blink_platform_impl_ = sandbox_support - ? std::make_unique<UtilityBlinkPlatformWithSandboxSupportImpl>() + ? std::make_unique<UtilityBlinkPlatformWithSandboxSupportImpl>( + GetConnector()) : std::make_unique<UtilityBlinkPlatformImpl>(); blink::Platform::Initialize(blink_platform_impl_.get()); }
diff --git a/docs/linux_sandbox_ipc.md b/docs/linux_sandbox_ipc.md index 5ff70906..f0555b8 100644 --- a/docs/linux_sandbox_ipc.md +++ b/docs/linux_sandbox_ipc.md
@@ -4,11 +4,13 @@ is a lower level system which deals with cases where we need to route requests from the bottom of the call stack up into the browser. -The motivating example is Skia, which uses fontconfig to load fonts. In a -chrooted renderer we cannot access the user's fontcache, nor the font files -themselves. However, font loading happens when we have called through WebKit, -through Skia and into the SkFontHost. At this point, we cannot loop back around -to use the main IPC system. +The motivating example used to be Skia, which uses fontconfig to load +fonts. Howvever, the OOP IPC for FontConfig was moved to using Font Service and +the `components/services/font/public/cpp/font_loader.h` interface. + +These days, only the out-of-process localtime implementation as well as +an OOP call for making a shared memory segment are using the Sandbox IPC +file-descriptor based system. See `sandbox/linux/services/libc_interceptor.cc`. Thus we define a small IPC system which doesn't depend on anything but `base` and which can make synchronous requests to the browser process. @@ -36,22 +38,12 @@ Here is a (possibly incomplete) list of endpoints in the renderer: -### fontconfig +### localtime -As mentioned above, the motivating example of this is dealing with fontconfig -from a chrooted renderer. We implement our own Skia FontHost, outside of the -Skia tree, in `skia/ext/SkFontHost_fontconfig**`. +`content/browser/sandbox_ipc_linux.h` defines HandleLocalTime which is +implemented in `sandbox/linux/services/libc_interceptor.cc`. -There are two methods used. One for performing a match against the fontconfig -data and one to return a file descriptor to a font file resulting from one of -those matches. The only wrinkle is that fontconfig is a single-threaded library -and it's already used in the browser by GTK itself. +### Creating a shared memory segment -Thus, we have a couple of options: - -1. Handle the requests on the UI thread in the browser. -1. Handle the requests in a separate address space. - -The original implementation did the former (handle on UI thread). This turned -out to be a terrible idea, performance wise, so we now handle the requests on a -dedicated process. +`content/browser/sandbox_ipc_linux.h` defines HandleMakeSharedMemorySegment +which is implemented in `content/browser/sandbox_ipc_linux.cc`.
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn index b46aaa83..8debf0c 100644 --- a/extensions/shell/BUILD.gn +++ b/extensions/shell/BUILD.gn
@@ -222,6 +222,10 @@ ] } + if (is_linux) { + deps += [ "//components/services/font/public/cpp" ] + } + if (is_chromeos) { sources += [ "browser/api/vpn_provider/vpn_service_factory.cc",
diff --git a/extensions/shell/browser/DEPS b/extensions/shell/browser/DEPS index ac34117..2f55513 100644 --- a/extensions/shell/browser/DEPS +++ b/extensions/shell/browser/DEPS
@@ -8,6 +8,7 @@ "+components/network_session_configurator/common", "+components/pref_registry", "+components/sessions", + "+components/services/font", "+components/update_client", "+components/user_prefs", "+components/web_modal",
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc index c018d0c..fd77420 100644 --- a/extensions/shell/browser/shell_content_browser_client.cc +++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -8,8 +8,10 @@ #include <utility> +#include "base/bind.h" #include "base/command_line.h" #include "base/macros.h" +#include "build/build_config.h" #include "components/guest_view/browser/guest_view_message_filter.h" #include "components/nacl/common/buildflags.h" #include "content/public/browser/browser_main_runner.h" @@ -56,6 +58,12 @@ #include "content/public/browser/child_process_data.h" #endif +#if defined(OS_LINUX) +#include "base/strings/utf_string_conversions.h" +#include "components/services/font/font_service_app.h" // nogncheck +#include "components/services/font/public/interfaces/constants.mojom.h" // nogncheck +#endif + using base::CommandLine; using content::BrowserContext; using content::BrowserThread; @@ -257,6 +265,14 @@ return throttles; } +void ShellContentBrowserClient::RegisterOutOfProcessServices( + OutOfProcessServiceMap* services) { +#if defined(OS_LINUX) + (*services)[font_service::mojom::kServiceName] = + base::BindRepeating(&base::ASCIIToUTF16, "Font Service"); +#endif +} + std::unique_ptr<content::NavigationUIData> ShellContentBrowserClient::GetNavigationUIData( content::NavigationHandle* navigation_handle) {
diff --git a/extensions/shell/browser/shell_content_browser_client.h b/extensions/shell/browser/shell_content_browser_client.h index 3be19c9..3bb45208 100644 --- a/extensions/shell/browser/shell_content_browser_client.h +++ b/extensions/shell/browser/shell_content_browser_client.h
@@ -64,6 +64,7 @@ std::vector<std::unique_ptr<content::NavigationThrottle>> CreateThrottlesForNavigation( content::NavigationHandle* navigation_handle) override; + void RegisterOutOfProcessServices(OutOfProcessServiceMap* services) override; std::unique_ptr<content::NavigationUIData> GetNavigationUIData( content::NavigationHandle* navigation_handle) override; void RegisterNonNetworkNavigationURLLoaderFactories(
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn index e926a3e..204456cc 100644 --- a/gpu/BUILD.gn +++ b/gpu/BUILD.gn
@@ -663,4 +663,50 @@ libfuzzer_options = [ "max_len=16384" ] } + + fuzzer_test("gpu_raster_swiftshader_fuzzer") { + sources = [ + "command_buffer/tests/fuzzer_main.cc", + ] + + defines = [ + "GPU_FUZZER_USE_RASTER_DECODER", + "GPU_FUZZER_USE_SWIFTSHADER", + ] + + deps = [ + ":gles2", + ":gpu", + "//base", + "//base/third_party/dynamic_annotations", + "//ui/gfx/geometry", + "//ui/gl", + "//ui/gl:test_support", + ] + + libfuzzer_options = [ "max_len=16384" ] + } + + fuzzer_test("gpu_raster_angle_fuzzer") { + sources = [ + "command_buffer/tests/fuzzer_main.cc", + ] + + defines = [ + "GPU_FUZZER_USE_RASTER_DECODER", + "GPU_FUZZER_USE_ANGLE", + ] + + deps = [ + ":gles2", + ":gpu", + "//base", + "//base/third_party/dynamic_annotations", + "//ui/gfx/geometry", + "//ui/gl", + "//ui/gl:test_support", + ] + + libfuzzer_options = [ "max_len=16384" ] + } }
diff --git a/headless/BUILD.gn b/headless/BUILD.gn index a339e25f..7e22f62 100644 --- a/headless/BUILD.gn +++ b/headless/BUILD.gn
@@ -449,6 +449,10 @@ deps += [ "//components/os_crypt" ] } + if (is_linux) { + deps += [ "//components/services/font/public/cpp" ] + } + if (is_component_build) { sources += [ "lib/browser/headless_content_browser_client.cc", @@ -744,6 +748,7 @@ "public/util/testing/test_in_memory_protocol_handler.h", "test/headless_browser_test.cc", "test/headless_browser_test.h", + "test/headless_client_browsertest.cc", "test/headless_protocol_browsertest.cc", "test/headless_test_launcher.cc", "test/test_protocol_handler.cc",
diff --git a/headless/lib/browser/DEPS b/headless/lib/browser/DEPS index f20dffe..6552fb8 100644 --- a/headless/lib/browser/DEPS +++ b/headless/lib/browser/DEPS
@@ -3,6 +3,7 @@ "+components/printing/browser", "+components/printing/common", "+components/security_state", + "+components/services/font", "+components/viz", "+printing", "+storage/browser/quota",
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc index b764fc0..715270a 100644 --- a/headless/lib/browser/headless_content_browser_client.cc +++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -13,6 +13,7 @@ #include "base/json/json_reader.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" +#include "build/build_config.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/client_certificate_delegate.h" @@ -50,6 +51,11 @@ #include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h" #endif +#if defined(OS_LINUX) +#include "components/services/font/font_service_app.h" // nogncheck +#include "components/services/font/public/interfaces/constants.mojom.h" // nogncheck +#endif + namespace headless { namespace { @@ -165,6 +171,10 @@ (*services)[printing::mojom::kServiceName] = base::BindRepeating(&base::ASCIIToUTF16, "PDF Compositor Service"); #endif +#if defined(OS_LINUX) + (*services)[font_service::mojom::kServiceName] = + base::BindRepeating(&base::ASCIIToUTF16, "Font Service"); +#endif } std::unique_ptr<base::Value>
diff --git a/headless/lib/browser/headless_devtools_client_impl.cc b/headless/lib/browser/headless_devtools_client_impl.cc index 05da6bf7..313c464b 100644 --- a/headless/lib/browser/headless_devtools_client_impl.cc +++ b/headless/lib/browser/headless_devtools_client_impl.cc
@@ -16,6 +16,11 @@ namespace headless { +namespace { +int g_next_message_id = 0; +int g_next_raw_message_id = 1; +} // namespace + // static std::unique_ptr<HeadlessDevToolsClient> HeadlessDevToolsClient::Create() { auto result = std::make_unique<HeadlessDevToolsClientImpl>(); @@ -67,7 +72,10 @@ tracing_domain_(this), weak_ptr_factory_(this) {} -HeadlessDevToolsClientImpl::~HeadlessDevToolsClientImpl() = default; +HeadlessDevToolsClientImpl::~HeadlessDevToolsClientImpl() { + if (parent_client_) + parent_client_->sessions_.erase(session_id_); +}; void HeadlessDevToolsClientImpl::AttachToExternalHost( ExternalHost* external_host) { @@ -102,35 +110,34 @@ raw_protocol_listener_ = raw_protocol_listener; } +std::unique_ptr<HeadlessDevToolsClient> +HeadlessDevToolsClientImpl::CreateSession(const std::string& session_id) { + std::unique_ptr<HeadlessDevToolsClientImpl> client = + std::make_unique<HeadlessDevToolsClientImpl>(); + client->parent_client_ = this; + client->session_id_ = session_id; + sessions_[session_id] = client.get(); + return client; +} + int HeadlessDevToolsClientImpl::GetNextRawDevToolsMessageId() { - int id = next_raw_message_id_; - next_raw_message_id_ += 2; + int id = g_next_raw_message_id; + g_next_raw_message_id += 2; return id; } void HeadlessDevToolsClientImpl::SendRawDevToolsMessage( const std::string& json_message) { -#ifndef NDEBUG - std::unique_ptr<base::Value> message = - base::JSONReader::Read(json_message, base::JSON_PARSE_RFC); - const base::Value* id_value = message->FindKey("id"); - if (!id_value) { - NOTREACHED() << "Badly formed message " << json_message; + std::unique_ptr<base::Value> message = base::JSONReader::Read(json_message); + if (!message->is_dict()) { + LOG(ERROR) << "Malformed raw message"; return; } -#endif - DCHECK(channel_ || external_host_); - if (channel_) - channel_->SendProtocolMessage(json_message); - else - external_host_->SendProtocolMessage(json_message); -} - -void HeadlessDevToolsClientImpl::SendRawDevToolsMessage( - const base::DictionaryValue& message) { - std::string json_message; - base::JSONWriter::Write(message, &json_message); - SendRawDevToolsMessage(json_message); + std::unique_ptr<base::DictionaryValue> dict = + base::DictionaryValue::From(std::move(message)); + if (!session_id_.empty()) + dict->SetString("sessionId", session_id_); + SendProtocolMessage(dict.get()); } void HeadlessDevToolsClientImpl::DispatchMessageFromExternalHost( @@ -141,8 +148,30 @@ void HeadlessDevToolsClientImpl::ReceiveProtocolMessage( const std::string& json_message) { + // LOG(ERROR) << "[RECV] " << json_message; std::unique_ptr<base::Value> message = base::JSONReader::Read(json_message, base::JSON_PARSE_RFC); + if (!message || !message->is_dict()) { + NOTREACHED() << "Badly formed reply " << json_message; + return; + } + std::unique_ptr<base::DictionaryValue> message_dict = + base::DictionaryValue::From(std::move(message)); + + std::string session_id; + if (message_dict->GetString("sessionId", &session_id)) { + auto it = sessions_.find(session_id); + if (it != sessions_.end()) { + it->second->ReceiveProtocolMessage(json_message, std::move(message_dict)); + return; + } + } + ReceiveProtocolMessage(json_message, std::move(message_dict)); +} + +void HeadlessDevToolsClientImpl::ReceiveProtocolMessage( + const std::string& json_message, + std::unique_ptr<base::DictionaryValue> message) { const base::DictionaryValue* message_dict; if (!message || !message->GetAsDictionary(&message_dict)) { NOTREACHED() << "Badly formed reply " << json_message; @@ -415,13 +444,25 @@ CallbackType callback) { if (renderer_crashed_) return; - DCHECK(channel_ || external_host_); - int id = next_message_id_; - next_message_id_ += 2; // We only send even numbered messages. + int id = g_next_message_id; + g_next_message_id += 2; // We only send even numbered messages. message->SetInteger("id", id); + if (!session_id_.empty()) + message->SetString("sessionId", session_id_); + pending_messages_[id] = Callback(std::move(callback)); + SendProtocolMessage(message); +} + +void HeadlessDevToolsClientImpl::SendProtocolMessage( + const base::DictionaryValue* message) { + if (parent_client_) { + parent_client_->SendProtocolMessage(message); + return; + } + std::string json_message; base::JSONWriter::Write(*message, &json_message); - pending_messages_[id] = Callback(std::move(callback)); + // LOG(ERROR) << "[SEND] " << json_message; if (channel_) channel_->SendProtocolMessage(json_message); else
diff --git a/headless/lib/headless_devtools_client_browsertest.cc b/headless/lib/headless_devtools_client_browsertest.cc index fa62bbce..02cc620 100644 --- a/headless/lib/headless_devtools_client_browsertest.cc +++ b/headless/lib/headless_devtools_client_browsertest.cc
@@ -1170,7 +1170,9 @@ std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue()); params->SetString("expression", "1+1"); message.Set("params", std::move(params)); - devtools_client_->SendRawDevToolsMessage(message); + std::string json_message; + base::JSONWriter::Write(message, &json_message); + devtools_client_->SendRawDevToolsMessage(json_message); } bool OnProtocolMessage(const std::string& json_message,
diff --git a/headless/public/headless_devtools_client.h b/headless/public/headless_devtools_client.h index 055f8d51..350ffd5 100644 --- a/headless/public/headless_devtools_client.h +++ b/headless/public/headless_devtools_client.h
@@ -198,12 +198,14 @@ virtual void SetRawProtocolListener( RawProtocolListener* raw_protocol_listener) = 0; + virtual std::unique_ptr<HeadlessDevToolsClient> CreateSession( + const std::string& session_id) = 0; + // Generates an odd numbered ID. virtual int GetNextRawDevToolsMessageId() = 0; // The id within the message must be odd to prevent collisions. virtual void SendRawDevToolsMessage(const std::string& json_message) = 0; - virtual void SendRawDevToolsMessage(const base::DictionaryValue& message) = 0; // TODO(dgozman): remove this method together with ExternalHost. virtual void DispatchMessageFromExternalHost(
diff --git a/headless/public/internal/headless_devtools_client_impl.h b/headless/public/internal/headless_devtools_client_impl.h index 3708182b..cba1a53 100644 --- a/headless/public/internal/headless_devtools_client_impl.h +++ b/headless/public/internal/headless_devtools_client_impl.h
@@ -7,6 +7,7 @@ #include <unordered_map> +#include "base/containers/flat_map.h" #include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner.h" #include "headless/public/devtools/domains/accessibility.h" @@ -96,9 +97,10 @@ tracing::Domain* GetTracing() override; void SetRawProtocolListener( RawProtocolListener* raw_protocol_listener) override; + std::unique_ptr<HeadlessDevToolsClient> CreateSession( + const std::string& session_id) override; int GetNextRawDevToolsMessageId() override; void SendRawDevToolsMessage(const std::string& json_message) override; - void SendRawDevToolsMessage(const base::DictionaryValue& message) override; void DispatchMessageFromExternalHost( const std::string& json_message) override; void AttachToChannel( @@ -170,15 +172,19 @@ const EventHandler* event_handler, const base::DictionaryValue* result_dict); + void ReceiveProtocolMessage(const std::string& json_message, + std::unique_ptr<base::DictionaryValue> message); + void SendProtocolMessage(const base::DictionaryValue* message); + std::unique_ptr<HeadlessDevToolsChannel> channel_; ExternalHost* external_host_ = nullptr; RawProtocolListener* raw_protocol_listener_ = nullptr; - int next_message_id_ = 0; - int next_raw_message_id_ = 1; std::unordered_map<int, Callback> pending_messages_; EventHandlerMap event_handlers_; - + std::string session_id_; + HeadlessDevToolsClientImpl* parent_client_ = nullptr; + base::flat_map<std::string, HeadlessDevToolsClientImpl*> sessions_; bool renderer_crashed_ = false; accessibility::ExperimentalDomain accessibility_domain_;
diff --git a/headless/test/headless_client_browsertest.cc b/headless/test/headless_client_browsertest.cc new file mode 100644 index 0000000..4f8fd0b --- /dev/null +++ b/headless/test/headless_client_browsertest.cc
@@ -0,0 +1,68 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> + +#include "build/build_config.h" +#include "content/public/test/browser_test.h" +#include "headless/public/devtools/domains/runtime.h" +#include "headless/public/devtools/domains/target.h" +#include "headless/public/headless_devtools_client.h" +#include "headless/test/headless_browser_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace headless { + +class HeadlessClientBrowserTest : public HeadlessAsyncDevTooledBrowserTest, + public target::ExperimentalObserver { + public: + HeadlessClientBrowserTest() = default; + + private: + // HeadlessWebContentsObserver implementation. + void RunDevTooledTest() override { + browser_devtools_client_->GetTarget()->GetExperimental()->AddObserver(this); + browser_devtools_client_->GetTarget()->CreateTarget( + target::CreateTargetParams::Builder().SetUrl("about:blank").Build(), + base::BindOnce(&HeadlessClientBrowserTest::AttachToTarget, + base::Unretained(this))); + } + + void AttachToTarget(std::unique_ptr<target::CreateTargetResult> result) { + browser_devtools_client_->GetTarget()->AttachToTarget( + target::AttachToTargetParams::Builder() + .SetTargetId(result->GetTargetId()) + .SetFlatten(true) + .Build(), + base::BindOnce(&HeadlessClientBrowserTest::CreateSession, + base::Unretained(this))); + } + + void CreateSession(std::unique_ptr<target::AttachToTargetResult> result) { + session_client_ = + browser_devtools_client_->CreateSession(result->GetSessionId()); + session_client_->GetRuntime()->Evaluate( + "window.location.href", + base::BindOnce(&HeadlessClientBrowserTest::FinishTest, + base::Unretained(this))); + } + + void FinishTest(std::unique_ptr<runtime::EvaluateResult> result) { + const base::Value* value = result->GetResult()->GetValue(); + std::string str; + EXPECT_TRUE(value->GetAsString(&str)); + EXPECT_EQ("about:blank", str); + session_client_.reset(); + FinishAsynchronousTest(); + } + + private: + std::unique_ptr<HeadlessDevToolsClient> session_client_; +}; + +IN_PROC_BROWSER_TEST_F(HeadlessClientBrowserTest, FlatProtocolAccess) { + RunTest(); +} + +} // namespace headless
diff --git a/ios/chrome/browser/consent_auditor/consent_auditor_factory.cc b/ios/chrome/browser/consent_auditor/consent_auditor_factory.cc index e9dc3ea..3a5cdcf 100644 --- a/ios/chrome/browser/consent_auditor/consent_auditor_factory.cc +++ b/ios/chrome/browser/consent_auditor/consent_auditor_factory.cc
@@ -57,12 +57,9 @@ web::BrowserState* browser_state) const { ios::ChromeBrowserState* ios_browser_state = ios::ChromeBrowserState::FromBrowserState(browser_state); - // TODO(crbug.com/851438): Don't create user event service at all if it is not - // needed. - syncer::UserEventService* const user_event_service = - IOSUserEventServiceFactory::GetForBrowserState(ios_browser_state); - std::unique_ptr<syncer::ConsentSyncBridge> bridge; + std::unique_ptr<syncer::ConsentSyncBridge> consent_sync_bridge; + syncer::UserEventService* user_event_service = nullptr; if (base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType)) { syncer::OnceModelTypeStoreFactory store_factory = browser_sync::ProfileSyncService::GetModelTypeStoreFactory( @@ -72,12 +69,16 @@ syncer::USER_CONSENTS, base::BindRepeating(&syncer::ReportUnrecoverableError, ::GetChannel())); - bridge = std::make_unique<syncer::ConsentSyncBridgeImpl>( + consent_sync_bridge = std::make_unique<syncer::ConsentSyncBridgeImpl>( std::move(store_factory), std::move(change_processor)); + } else { + user_event_service = + IOSUserEventServiceFactory::GetForBrowserState(ios_browser_state); } return std::make_unique<consent_auditor::ConsentAuditor>( - ios_browser_state->GetPrefs(), std::move(bridge), user_event_service, + ios_browser_state->GetPrefs(), std::move(consent_sync_bridge), + user_event_service, // The browser version and locale do not change runtime, so we can pass // them directly. version_info::GetVersionNumber(),
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn index e39f6bc..1213a1c 100644 --- a/ios/chrome/browser/ui/autofill/BUILD.gn +++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -11,8 +11,6 @@ "card_unmask_prompt_view_bridge.mm", "chrome_autofill_client_ios.h", "chrome_autofill_client_ios.mm", - "storage_switch_tooltip.h", - "storage_switch_tooltip.mm", ] deps = [ ":autofill_ui",
diff --git a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm index 5a0420e..180ab8e 100644 --- a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm +++ b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
@@ -14,16 +14,16 @@ #include "components/strings/grit/components_strings.h" #import "ios/chrome/browser/ui/autofill/cells/cvc_item.h" #import "ios/chrome/browser/ui/autofill/cells/status_item.h" -#import "ios/chrome/browser/ui/autofill/cells/storage_switch_item.h" -#import "ios/chrome/browser/ui/autofill/storage_switch_tooltip.h" #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h" #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h" +#import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h" #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h" #import "ios/chrome/browser/ui/collection_view/collection_view_model.h" #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" #import "ios/chrome/browser/ui/rtl_geometry.h" #import "ios/third_party/material_components_ios/src/components/AppBar/src/MaterialAppBar.h" #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h" +#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" #include "ui/base/l10n/l10n_util.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -126,12 +126,7 @@ UIBarButtonItem* _verifyButton; CVCItem* _CVCItem; StatusItem* _statusItem; - StorageSwitchItem* _storageSwitchItem; - - // The tooltip is added as a child of the collection view rather than the - // StorageSwitchContentView to allow it to overflow the bounds of the switch - // view. - StorageSwitchTooltip* _storageSwitchTooltip; + CollectionViewSwitchItem* _storageSwitchItem; // Owns |self|. autofill::CardUnmaskPromptViewBridge* _bridge; // weak @@ -222,14 +217,12 @@ if (controller->CanStoreLocally()) { _storageSwitchItem = - [[StorageSwitchItem alloc] initWithType:ItemTypeStorageSwitch]; + [[CollectionViewSwitchItem alloc] initWithType:ItemTypeStorageSwitch]; + _storageSwitchItem.text = l10n_util::GetNSString( + IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX); _storageSwitchItem.on = controller->GetStoreLocallyStartState(); [model addItem:_storageSwitchItem toSectionWithIdentifier:SectionIdentifierMain]; - - _storageSwitchTooltip = [[StorageSwitchTooltip alloc] init]; - [_storageSwitchTooltip setHidden:YES]; - [self.collectionView addSubview:_storageSwitchTooltip]; } else { _storageSwitchItem = nil; } @@ -268,7 +261,6 @@ - (void)showSpinner { [_verifyButton setEnabled:NO]; - [_storageSwitchTooltip setHidden:YES]; [self updateWithStatus:StatusItemState::VERIFYING @@ -335,31 +327,6 @@ preferredHeightForStatus); } -- (void)layoutTooltipFromButton:(UIButton*)button { - const CGRect buttonFrameInCollectionView = - [self.collectionView convertRect:button.bounds fromView:button]; - CGRect tooltipFrame = _storageSwitchTooltip.frame; - - // First, set the width and use sizeToFit to have the label flow the text and - // set the height appropriately. - const CGFloat kTooltipMargin = 8; - CGFloat availableWidth = - CGRectGetMinX(buttonFrameInCollectionView) - 2 * kTooltipMargin; - const CGFloat kMaxTooltipWidth = 210; - tooltipFrame.size.width = MIN(availableWidth, kMaxTooltipWidth); - _storageSwitchTooltip.frame = tooltipFrame; - [_storageSwitchTooltip sizeToFit]; - - // Then use the size to position the tooltip appropriately, based on the - // button position. - tooltipFrame = _storageSwitchTooltip.frame; - tooltipFrame.origin.x = CGRectGetMinX(buttonFrameInCollectionView) - - kTooltipMargin - CGRectGetWidth(tooltipFrame); - tooltipFrame.origin.y = CGRectGetMaxY(buttonFrameInCollectionView) - - CGRectGetHeight(tooltipFrame); - _storageSwitchTooltip.frame = tooltipFrame; -} - - (BOOL)inputCVCIsValid:(CVCItem*)item { return _bridge->GetController()->InputCvcIsValid( base::SysNSStringToUTF16(item.CVCText)); @@ -453,19 +420,6 @@ _bridge->PerformClose(); } -- (void)onTooltipButtonTapped:(UIButton*)button { - BOOL shouldShowTooltip = !button.selected; - button.highlighted = shouldShowTooltip; - if (shouldShowTooltip) { - button.selected = YES; - [self layoutTooltipFromButton:button]; - [_storageSwitchTooltip setHidden:NO]; - } else { - button.selected = NO; - [_storageSwitchTooltip setHidden:YES]; - } -} - - (void)onStorageSwitchChanged:(UISwitch*)switchView { // Update the item. _storageSwitchItem.on = switchView.on; @@ -565,12 +519,11 @@ break; } case ItemTypeStorageSwitch: { - StorageSwitchCell* storageSwitchCell = - base::mac::ObjCCastStrict<StorageSwitchCell>(cell); - [storageSwitchCell.tooltipButton - addTarget:self - action:@selector(onTooltipButtonTapped:) - forControlEvents:UIControlEventTouchUpInside]; + CollectionViewSwitchCell* storageSwitchCell = + base::mac::ObjCCastStrict<CollectionViewSwitchCell>(cell); + storageSwitchCell.textLabel.font = [MDCTypography body2Font]; + storageSwitchCell.textLabel.textColor = + [[MDCPalette greyPalette] tint500]; [storageSwitchCell.switchView addTarget:self action:@selector(onStorageSwitchChanged:) forControlEvents:UIControlEventValueChanged];
diff --git a/ios/chrome/browser/ui/autofill/cells/BUILD.gn b/ios/chrome/browser/ui/autofill/cells/BUILD.gn index 3a01976..5f725dc9d 100644 --- a/ios/chrome/browser/ui/autofill/cells/BUILD.gn +++ b/ios/chrome/browser/ui/autofill/cells/BUILD.gn
@@ -10,8 +10,6 @@ "cvc_item.mm", "status_item.h", "status_item.mm", - "storage_switch_item.h", - "storage_switch_item.mm", ] deps = [ @@ -39,7 +37,6 @@ "autofill_edit_item_unittest.mm", "cvc_item_unittest.mm", "status_item_unittest.mm", - "storage_switch_item_unittest.mm", ] deps = [
diff --git a/ios/chrome/browser/ui/autofill/cells/storage_switch_item.h b/ios/chrome/browser/ui/autofill/cells/storage_switch_item.h deleted file mode 100644 index d7a488f..0000000 --- a/ios/chrome/browser/ui/autofill/cells/storage_switch_item.h +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_CELLS_STORAGE_SWITCH_ITEM_H_ -#define IOS_CHROME_BROWSER_UI_AUTOFILL_CELLS_STORAGE_SWITCH_ITEM_H_ - -#import <UIKit/UIKit.h> - -#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h" -#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h" - -// StorageSwitchItem is the model class corresponding to StorageSwitchCell. -@interface StorageSwitchItem : CollectionViewItem - -// The current state of the switch. -@property(nonatomic, assign, getter=isOn) BOOL on; - -@end - -// StorageSwitchCell implements a UICollectionViewCell subclass containing a -// text label, a switch and a tooltip button. -@interface StorageSwitchCell : MDCCollectionViewCell - -// Label displaying a standard text. -@property(nonatomic, readonly, strong) UILabel* textLabel; - -// The tooltip button. Clients own and manage the tooltip they present and can -// use the button frame as the anchor point. Otherwise, this button is a no-op. -@property(nonatomic, readonly, strong) UIButton* tooltipButton; - -// The switch view. -@property(nonatomic, readonly, strong) UISwitch* switchView; - -@end - -#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_CELLS_STORAGE_SWITCH_ITEM_H_
diff --git a/ios/chrome/browser/ui/autofill/cells/storage_switch_item.mm b/ios/chrome/browser/ui/autofill/cells/storage_switch_item.mm deleted file mode 100644 index eb34c2e..0000000 --- a/ios/chrome/browser/ui/autofill/cells/storage_switch_item.mm +++ /dev/null
@@ -1,145 +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. - -#import "ios/chrome/browser/ui/autofill/cells/storage_switch_item.h" - -#include "components/grit/components_scaled_resources.h" -#include "components/strings/grit/components_strings.h" -#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" -#import "ios/chrome/browser/ui/uikit_ui_util.h" -#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h" -#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" -#include "ui/base/l10n/l10n_util_mac.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -namespace { -// Padding used on the leading and trailing edges of the cell. -const CGFloat kHorizontalPadding = 16; -// Padding used on the top and bottom edges of the cell. -const CGFloat kVerticalPadding = 16; -// Space at the leading side of the tooltip button. -const CGFloat kTooltipButtonLeadingSpacing = 8; -// Space at the trailing side of the tooltip button. -const CGFloat kTooltipButtonTrailingSpacing = 30; -} - -@implementation StorageSwitchItem - -@synthesize on = _on; - -- (instancetype)initWithType:(NSInteger)type { - self = [super initWithType:type]; - if (self) { - self.cellClass = [StorageSwitchCell class]; - } - return self; -} - -#pragma mark CollectionViewItem - -- (void)configureCell:(StorageSwitchCell*)cell { - [super configureCell:cell]; - cell.switchView.on = self.on; -} - -@end - -@implementation StorageSwitchCell - -@synthesize textLabel = _textLabel; -@synthesize tooltipButton = _tooltipButton; -@synthesize switchView = _switchView; - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - UIView* contentView = self.contentView; - - _textLabel = [[UILabel alloc] init]; - _textLabel.translatesAutoresizingMaskIntoConstraints = NO; - [contentView addSubview:_textLabel]; - - _switchView = [[UISwitch alloc] init]; - _switchView.translatesAutoresizingMaskIntoConstraints = NO; - [contentView addSubview:_switchView]; - - _tooltipButton = [UIButton buttonWithType:UIButtonTypeCustom]; - _tooltipButton.translatesAutoresizingMaskIntoConstraints = NO; - [contentView addSubview:_tooltipButton]; - - _textLabel.text = l10n_util::GetNSString( - IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX); - _textLabel.font = [[MDCTypography fontLoader] mediumFontOfSize:14]; - _textLabel.textColor = [[MDCPalette greyPalette] tint500]; - _textLabel.numberOfLines = 0; - [_textLabel - setContentCompressionResistancePriority:UILayoutPriorityDefaultLow - forAxis: - UILayoutConstraintAxisHorizontal]; - - _switchView.onTintColor = [[MDCPalette cr_bluePalette] tint500]; - - [_tooltipButton setImage:NativeImage(IDR_AUTOFILL_TOOLTIP_ICON_H) - forState:UIControlStateSelected]; - [_tooltipButton setImage:NativeImage(IDR_AUTOFILL_TOOLTIP_ICON) - forState:UIControlStateNormal]; - - // Set up the constraints. - [NSLayoutConstraint activateConstraints:@[ - [_textLabel.topAnchor constraintEqualToAnchor:contentView.topAnchor - constant:kVerticalPadding], - [_textLabel.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor - constant:-kVerticalPadding], - [_textLabel.leadingAnchor - constraintEqualToAnchor:contentView.leadingAnchor - constant:kHorizontalPadding], - [_textLabel.trailingAnchor - constraintLessThanOrEqualToAnchor:_tooltipButton.leadingAnchor - constant:-kTooltipButtonLeadingSpacing], - [_tooltipButton.centerYAnchor - constraintEqualToAnchor:contentView.centerYAnchor], - [_tooltipButton.trailingAnchor - constraintEqualToAnchor:_switchView.leadingAnchor - constant:-kTooltipButtonTrailingSpacing], - [_switchView.centerYAnchor - constraintEqualToAnchor:contentView.centerYAnchor], - [_switchView.trailingAnchor - constraintEqualToAnchor:contentView.trailingAnchor - constant:-kHorizontalPadding], - ]]; - } - return self; -} - -// Implement -layoutSubviews as per instructions in documentation for -// +[MDCCollectionViewCell cr_preferredHeightForWidth:forItem:]. -- (void)layoutSubviews { - [super layoutSubviews]; - - // Adjust the text label preferredMaxLayoutWidth when the parent's width - // changes, for instance on screen rotation. - self.textLabel.preferredMaxLayoutWidth = - CGRectGetWidth(self.contentView.frame) - 2 * kHorizontalPadding - - kTooltipButtonLeadingSpacing - CGRectGetWidth(self.tooltipButton.frame) - - kTooltipButtonTrailingSpacing - CGRectGetWidth(self.switchView.frame); - - // Re-layout with the new preferred width to allow the label to adjust its - // height. - [super layoutSubviews]; -} - -- (void)prepareForReuse { - [super prepareForReuse]; - [self.tooltipButton removeTarget:nil - action:nil - forControlEvents:self.tooltipButton.allControlEvents]; - [self.switchView removeTarget:nil - action:nil - forControlEvents:self.switchView.allControlEvents]; -} - -@end
diff --git a/ios/chrome/browser/ui/autofill/cells/storage_switch_item_unittest.mm b/ios/chrome/browser/ui/autofill/cells/storage_switch_item_unittest.mm deleted file mode 100644 index d311112..0000000 --- a/ios/chrome/browser/ui/autofill/cells/storage_switch_item_unittest.mm +++ /dev/null
@@ -1,59 +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. - -#import "ios/chrome/browser/ui/autofill/cells/storage_switch_item.h" - -#include "base/mac/foundation_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -namespace { - -using StorageSwitchItemTest = PlatformTest; - -// Tests that the label and switch values are set properly after a call to -// |configureCell:|. -TEST_F(StorageSwitchItemTest, ConfigureCell) { - StorageSwitchItem* item = [[StorageSwitchItem alloc] initWithType:0]; - item.on = YES; - - id cell = [[[item cellClass] alloc] init]; - ASSERT_TRUE([cell isMemberOfClass:[StorageSwitchCell class]]); - - StorageSwitchCell* switchCell = - base::mac::ObjCCastStrict<StorageSwitchCell>(cell); - EXPECT_TRUE(switchCell.textLabel.text); - EXPECT_FALSE(switchCell.switchView.on); - - [item configureCell:cell]; - EXPECT_TRUE(switchCell.switchView.on); -} - -TEST_F(StorageSwitchItemTest, PrepareForReuseClearsActions) { - StorageSwitchCell* cell = [[StorageSwitchCell alloc] init]; - UIButton* tooltipButton = cell.tooltipButton; - UISwitch* switchView = cell.switchView; - NSArray* target = [NSArray array]; - - EXPECT_EQ(0U, [[tooltipButton allTargets] count]); - EXPECT_EQ(0U, [[switchView allTargets] count]); - [tooltipButton addTarget:target - action:@selector(count) - forControlEvents:UIControlEventTouchUpInside]; - [switchView addTarget:target - action:@selector(count) - forControlEvents:UIControlEventValueChanged]; - EXPECT_EQ(1U, [[tooltipButton allTargets] count]); - EXPECT_EQ(1U, [[switchView allTargets] count]); - - [cell prepareForReuse]; - EXPECT_EQ(0U, [[tooltipButton allTargets] count]); - EXPECT_EQ(0U, [[switchView allTargets] count]); -} - -} // namespace
diff --git a/ios/chrome/browser/ui/autofill/storage_switch_tooltip.h b/ios/chrome/browser/ui/autofill/storage_switch_tooltip.h deleted file mode 100644 index 652cf35c..0000000 --- a/ios/chrome/browser/ui/autofill/storage_switch_tooltip.h +++ /dev/null
@@ -1,22 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_STORAGE_SWITCH_TOOLTIP_H_ -#define IOS_CHROME_BROWSER_UI_AUTOFILL_STORAGE_SWITCH_TOOLTIP_H_ - -#import <UIKit/UIKit.h> - -// A label that displays the storage setting tooltip text with appropriate -// layout and styling. -@interface StorageSwitchTooltip : UILabel - -- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER; - -- (instancetype)init; - -- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE; - -@end - -#endif // IOS_CHROME_BROWSER_UI_AUTOFILL_STORAGE_SWITCH_TOOLTIP_H_
diff --git a/ios/chrome/browser/ui/autofill/storage_switch_tooltip.mm b/ios/chrome/browser/ui/autofill/storage_switch_tooltip.mm deleted file mode 100644 index 216034f4..0000000 --- a/ios/chrome/browser/ui/autofill/storage_switch_tooltip.mm +++ /dev/null
@@ -1,73 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "ios/chrome/browser/ui/autofill/storage_switch_tooltip.h" - -#include "base/logging.h" -#include "components/strings/grit/components_strings.h" -#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" -#include "ui/base/l10n/l10n_util.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -namespace { - -const CGFloat kCornerRadius = 2.0f; -const CGFloat kFontSize = 12.0f; -const CGFloat kInset = 8.0f; - -} // namespace - -@implementation StorageSwitchTooltip - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - NSString* tooltipText = - l10n_util::GetNSString(IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_TOOLTIP); - [self setText:tooltipText]; - [self setTextColor:[UIColor whiteColor]]; - [self setBackgroundColor:[UIColor colorWithWhite:0.0 alpha:0.9]]; - [[self layer] setCornerRadius:kCornerRadius]; - [[self layer] setMasksToBounds:YES]; - [self setFont:[[MDCTypography fontLoader] regularFontOfSize:kFontSize]]; - [self setNumberOfLines:0]; // Allows multi-line layout. - } - return self; -} - -- (instancetype)init { - return [self initWithFrame:CGRectZero]; -} - -- (instancetype)initWithCoder:(NSCoder*)aDecoder { - NOTREACHED(); - return nil; -} - -// The logic in textRectForBounds:limitedToNumberOfLines: and drawTextInRect: -// adds an inset. Based on -// http://stackoverflow.com/questions/21167226/resizing-a-uilabel-to-accomodate-insets/21267507#21267507 -- (CGRect)textRectForBounds:(CGRect)bounds - limitedToNumberOfLines:(NSInteger)numberOfLines { - UIEdgeInsets insets = {kInset, kInset, kInset, kInset}; - CGRect rect = [super textRectForBounds:UIEdgeInsetsInsetRect(bounds, insets) - limitedToNumberOfLines:numberOfLines]; - - rect.origin.x -= insets.left; - rect.origin.y -= insets.top; - rect.size.width += (insets.left + insets.right); - rect.size.height += (insets.top + insets.bottom); - - return rect; -} - -- (void)drawTextInRect:(CGRect)rect { - UIEdgeInsets insets = {kInset, kInset, kInset, kInset}; - [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)]; -} - -@end
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm index 61594f9f..e55887a 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator_unittest.mm
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ios/chrome/browser/ui/location_bar/location_bar_legacy_coordinator.h" +#import "ios/chrome/browser/ui/location_bar/location_bar_coordinator.h" #include <memory> #include <string> @@ -60,10 +60,9 @@ namespace { -class LocationBarLegacyCoordinatorTest : public PlatformTest { +class LocationBarCoordinatorTest : public PlatformTest { protected: - LocationBarLegacyCoordinatorTest() - : web_state_list_(&web_state_list_delegate_) {} + LocationBarCoordinatorTest() : web_state_list_(&web_state_list_delegate_) {} void SetUp() override { PlatformTest::SetUp(); @@ -87,7 +86,7 @@ delegate_ = [[TestToolbarCoordinatorDelegate alloc] init]; url_loader_ = [[FakeURLLoader alloc] init]; - coordinator_ = [[LocationBarLegacyCoordinator alloc] init]; + coordinator_ = [[LocationBarCoordinator alloc] init]; coordinator_.browserState = browser_state_.get(); coordinator_.webStateList = &web_state_list_; coordinator_.delegate = delegate_; @@ -105,7 +104,7 @@ } web::TestWebThreadBundle web_thread_bundle_; - LocationBarLegacyCoordinator* coordinator_; + LocationBarCoordinator* coordinator_; std::unique_ptr<TestChromeBrowserState> browser_state_; FakeWebStateListDelegate web_state_list_delegate_; WebStateList web_state_list_; @@ -113,7 +112,7 @@ FakeURLLoader* url_loader_; }; -TEST_F(LocationBarLegacyCoordinatorTest, Stops) { +TEST_F(LocationBarCoordinatorTest, Stops) { EXPECT_TRUE(coordinator_.view == nil); [coordinator_ start]; EXPECT_TRUE(coordinator_.view != nil); @@ -124,7 +123,7 @@ // Calls -loadGURLFromLocationBar:transition: with https://www.google.com/ URL. // Verifies that URLLoader receives correct load request, which also includes // variations header. -TEST_F(LocationBarLegacyCoordinatorTest, LoadGoogleUrl) { +TEST_F(LocationBarCoordinatorTest, LoadGoogleUrl) { ASSERT_EQ(VariationsHttpHeaderProvider::ForceIdsResult::SUCCESS, VariationsHttpHeaderProvider::GetInstance()->ForceVariationIds( /*variation_ids=*/{"100"}, /*command_line_variation_ids=*/"")); @@ -146,7 +145,7 @@ // Calls -loadGURLFromLocationBar:transition: with https://www.nongoogle.com/ // URL. Verifies that URLLoader receives correct load request without variations // header. -TEST_F(LocationBarLegacyCoordinatorTest, LoadNonGoogleUrl) { +TEST_F(LocationBarCoordinatorTest, LoadNonGoogleUrl) { ASSERT_EQ(VariationsHttpHeaderProvider::ForceIdsResult::SUCCESS, VariationsHttpHeaderProvider::GetInstance()->ForceVariationIds( /*variation_ids=*/{"100"}, /*command_line_variation_ids=*/""));
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm index 2b6b53e..c5880686 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
@@ -286,7 +286,7 @@ [self updateAccessibility]; } -#pragma mark - UIAccessibilityContainer] +#pragma mark - UIAccessibilityContainer - (NSArray*)accessibilityElements { return self.accessibleElements;
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm index 1e9baf5..e7fdcae 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -209,8 +209,10 @@ // Display a fake "placeholder". NSString* placeholderString = l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT); - UIColor* placeholderColor = - [LocationBarSteadyViewColorScheme incognitoScheme].placeholderColor; + LocationBarSteadyViewColorScheme* scheme = + self.incognito ? [LocationBarSteadyViewColorScheme incognitoScheme] + : [LocationBarSteadyViewColorScheme standardScheme]; + UIColor* placeholderColor = scheme.placeholderColor; self.locationBarSteadyView.locationLabel.attributedText = [ [NSAttributedString alloc] initWithString:placeholderString
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_util.cc b/ios/chrome/browser/ui/omnibox/omnibox_util.cc index e46b140..42ca1d9 100644 --- a/ios/chrome/browser/ui/omnibox/omnibox_util.cc +++ b/ios/chrome/browser/ui/omnibox/omnibox_util.cc
@@ -122,9 +122,7 @@ return base::ASCIIToUTF16("location_bar_insecure"); case security_state::EV_SECURE: case security_state::SECURE: - return base::ASCIIToUTF16("location_bar_secure"); case security_state::SECURE_WITH_POLICY_INSTALLED_CERT: - // TODO(crbug.com/848732): update this icon. return base::ASCIIToUTF16("location_bar_secure"); case security_state::DANGEROUS: return base::ASCIIToUTF16("location_bar_dangerous");
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm index 6ee8b069..5f78eab 100644 --- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm +++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
@@ -983,6 +983,9 @@ } void OmniboxViewIOS::OnSelectedMatchForAppending(const base::string16& str) { + // Exit preedit state and append the match. Refocus if necessary. + if ([field_ isPreEditing]) + [field_ exitPreEditState]; this->SetUserText(str); this->FocusOmnibox(); }
diff --git a/ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.mm index c9aeb3d..58be286 100644 --- a/ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.mm +++ b/ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.mm
@@ -18,7 +18,6 @@ #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h" #import "ios/chrome/browser/ui/autofill/cells/cvc_item.h" #import "ios/chrome/browser/ui/autofill/cells/status_item.h" -#import "ios/chrome/browser/ui/autofill/cells/storage_switch_item.h" #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h" #import "ios/chrome/browser/ui/collection_view/cells/collection_view_account_item.h" #import "ios/chrome/browser/ui/collection_view/cells/collection_view_detail_item.h" @@ -90,7 +89,6 @@ ItemTypeAutofillDynamicHeight, ItemTypeAutofillCVC, ItemTypeAutofillStatus, - ItemTypeAutofillStorageSwitch, ItemTypeAccountControlDynamicHeight, ItemTypeFooter, ItemTypeContentSuggestions, @@ -245,8 +243,6 @@ toSectionWithIdentifier:SectionIdentifierAutofill]; [model addItem:[self statusItemError] toSectionWithIdentifier:SectionIdentifierAutofill]; - [model addItem:[self storageSwitchItem] - toSectionWithIdentifier:SectionIdentifierAutofill]; // Payments cells. [model addSectionWithIdentifier:SectionIdentifierPayments]; @@ -367,7 +363,6 @@ case ItemTypeTextError: case ItemTypeAutofillCVC: case ItemTypeAutofillStatus: - case ItemTypeAutofillStorageSwitch: case ItemTypePaymentsDynamicHeight: case ItemTypeAutofillDynamicHeight: case ItemTypeColdStateSigninPromo: @@ -427,7 +422,6 @@ [self.collectionViewModel itemAtIndexPath:indexPath]; switch (item.type) { case ItemTypeApp: - case ItemTypeAutofillStorageSwitch: case ItemTypeColdStateSigninPromo: case ItemTypeSwitchBasic: case ItemTypeSwitchDynamicHeight: @@ -695,13 +689,6 @@ return item; } -- (CollectionViewItem*)storageSwitchItem { - StorageSwitchItem* item = - [[StorageSwitchItem alloc] initWithType:ItemTypeAutofillStorageSwitch]; - item.on = YES; - return item; -} - - (CollectionViewFooterItem*)shortFooterItem { CollectionViewFooterItem* footerItem = [[CollectionViewFooterItem alloc] initWithType:ItemTypeFooter];
diff --git a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm index 3edb1063..b2967cb 100644 --- a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm +++ b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm
@@ -99,8 +99,7 @@ // Tests signing in with one account, switching sync account to a second and // choosing to keep the browsing data separate during the switch. -// TODO(crbug.com/854446): Enable after fixing. -- (void)DISABLED_testSignInSwitchAccountsAndKeepDataSeparate { +- (void)testSignInSwitchAccountsAndKeepDataSeparate { // Set up the fake identities. ios::FakeChromeIdentityService* identity_service = ios::FakeChromeIdentityService::GetInstanceFromChromeProvider(); @@ -137,8 +136,7 @@ // Tests signing in with one account, switching sync account to a second and // choosing to import the browsing data during the switch. -// TODO(crbug.com/854446): Enable after fixing. -- (void)DISABLED_testSignInSwitchAccountsAndImportData { +- (void)testSignInSwitchAccountsAndImportData { // Set up the fake identities. ios::FakeChromeIdentityService* identity_service = ios::FakeChromeIdentityService::GetInstanceFromChromeProvider(); @@ -176,8 +174,7 @@ // Tests that switching from a managed account to a non-managed account works // correctly and displays the expected warnings. -// TODO(crbug.com/854446): Enable after fixing. -- (void)DISABLED_testSignInSwitchManagedAccount { +- (void)testSignInSwitchManagedAccount { // Set up the fake identities. ios::FakeChromeIdentityService* identity_service = ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.h b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.h index 02208e72..ba89ecb4 100644 --- a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.h +++ b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.h
@@ -30,6 +30,8 @@ @property(nonatomic, weak) UIImage* icon; @property(nonatomic, weak) UIImage* snapshot; @property(nonatomic, copy) NSString* title; +// Fixed (immutable) UI elements that may be referenced in animations. +@property(nonatomic, readonly, weak) UIView* topBar; // Returns a cell with the same theme, icon, snapshot, and title as the reciever // (but no delegate or identifier) for use in animated transitions.
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm index 9f734cd..791be92 100644 --- a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm +++ b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
@@ -16,8 +16,9 @@ #endif @interface GridCell () +// Redeclare TopBar readwrite internally. +@property(nonatomic, readwrite, weak) UIView* topBar; // Visual components of the cell. -@property(nonatomic, weak) UIView* topBar; @property(nonatomic, weak) UIImageView* iconView; @property(nonatomic, weak) TopAlignedImageView* snapshotView; @property(nonatomic, weak) UILabel* titleLabel; @@ -36,8 +37,8 @@ @synthesize icon = _icon; @synthesize snapshot = _snapshot; @synthesize title = _title; -// Private properties. @synthesize topBar = _topBar; +// Private properties. @synthesize iconView = _iconView; @synthesize snapshotView = _snapshotView; @synthesize titleLabel = _titleLabel; @@ -145,6 +146,8 @@ UIColorFromRGB(kGridCellSnapshotBackgroundColor); switch (theme) { case GridThemeLight: + self.contentView.backgroundColor = + UIColorFromRGB(kGridLightThemeCellHeaderColor); self.topBar.backgroundColor = UIColorFromRGB(kGridLightThemeCellHeaderColor); self.titleLabel.textColor = UIColorFromRGB(kGridLightThemeCellTitleColor); @@ -154,6 +157,8 @@ UIColorFromRGB(kGridLightThemeCellSelectionColor).CGColor; break; case GridThemeDark: + self.contentView.backgroundColor = + UIColorFromRGB(kGridDarkThemeCellHeaderColor); self.topBar.backgroundColor = UIColorFromRGB(kGridDarkThemeCellHeaderColor); self.titleLabel.textColor = UIColorFromRGB(kGridDarkThemeCellTitleColor);
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm index 0e59d2f..76b1656c 100644 --- a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm +++ b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
@@ -190,7 +190,8 @@ [self.collectionView layoutIfNeeded]; NSMutableArray<GridTransitionLayoutItem*>* items = [[NSMutableArray alloc] init]; - GridTransitionLayoutItem* selectedItem; + GridTransitionLayoutItem* activeItem; + GridTransitionLayoutItem* selectionItem; for (NSIndexPath* path in self.collectionView.indexPathsForVisibleItems) { GridCell* cell = base::mac::ObjCCastStrict<GridCell>( [self.collectionView cellForItemAtIndexPath:path]); @@ -200,15 +201,24 @@ // change to the other properties such as center, bounds, etc. attributes.frame = [self.collectionView convertRect:attributes.frame toView:nil]; + GridCell* proxyCell = [cell proxyForTransitions]; GridTransitionLayoutItem* item = - [GridTransitionLayoutItem itemWithCell:[cell proxyForTransitions] + [GridTransitionLayoutItem itemWithCell:proxyCell + auxillaryView:proxyCell.topBar attributes:attributes]; [items addObject:item]; if ([cell.itemIdentifier isEqualToString:self.selectedItemID]) { - selectedItem = item; + activeItem = item; + + selectionItem = + [GridTransitionLayoutItem itemWithCell:[cell proxyForTransitions] + auxillaryView:nil + attributes:attributes]; } } - return [GridTransitionLayout layoutWithItems:items selectedItem:selectedItem]; + return [GridTransitionLayout layoutWithItems:items + activeItem:activeItem + selectionItem:selectionItem]; } #pragma mark - UICollectionViewDataSource
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm index 12c46b2a7..1fdc2ff5 100644 --- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm +++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -681,36 +681,39 @@ // interact with the layout system at all. - (void)animateToolbarsForAppearance { DCHECK(self.transitionCoordinator); - // TODO(crbug.com/820410): Tune the timing of these animations. + // Set toolbar alphas to 0 prior to animation. + self.topToolbar.alpha = 0; + self.bottomToolbar.alpha = 0; - // Capture the current toolbar transforms. - CGAffineTransform topToolbarBaseTransform = self.topToolbar.transform; - CGAffineTransform bottomToolbarBaseTransform = self.bottomToolbar.transform; - // Translate the top toolbar up offscreen by shifting it up by its height. - self.topToolbar.transform = CGAffineTransformTranslate( - self.topToolbar.transform, /*tx=*/0, - /*ty=*/-(self.topToolbar.bounds.size.height * 0.5)); - // Translate the bottom toolbar down offscreen by shifting it down by its - // height. - self.bottomToolbar.transform = CGAffineTransformTranslate( - self.bottomToolbar.transform, /*tx=*/0, - /*ty=*/(self.topToolbar.bounds.size.height * 0.5)); - - // Block that restores the toolbar transforms, suitable for using with the - // transition coordinator. + // Fade the toolbars in for the last 60% of the transition. + auto keyframe = ^{ + [UIView addKeyframeWithRelativeStartTime:0.4 + relativeDuration:0.6 + animations:^{ + self.topToolbar.alpha = 1.0; + self.bottomToolbar.alpha = 1.0; + }]; + }; + // Animation block that does the keyframe animation. auto animation = ^(id<UIViewControllerTransitionCoordinatorContext> context) { - self.topToolbar.transform = topToolbarBaseTransform; - self.bottomToolbar.transform = bottomToolbarBaseTransform; + [UIView animateKeyframesWithDuration:context.transitionDuration + delay:0 + options:UIViewAnimationOptionLayoutSubviews + animations:keyframe + completion:nil]; }; // Also hide the scroll view (and thus the tab grids) until the transition - // completes. + // completes. Set the toolbar alpha to 1.0 as part of the completion handler + // as well, in case the animation didn't complete. self.scrollView.hidden = YES; auto cleanup = ^(id<UIViewControllerTransitionCoordinatorContext> context) { self.scrollView.hidden = NO; + self.topToolbar.alpha = 1.0; + self.bottomToolbar.alpha = 1.0; }; - // Animate the toolbars into place alongside the current transition. + // Animate the toolbar alphas alongside the current transition. [self.transitionCoordinator animateAlongsideTransition:animation completion:cleanup]; } @@ -718,38 +721,36 @@ // Translates the toolbar views offscreen using the transition coordinator. - (void)animateToolbarsForDisappearance { DCHECK(self.transitionCoordinator); - // TODO(crbug.com/820410): Tune the timing of these animations. - // Capture the current toolbar transforms. - CGAffineTransform topToolbarBaseTransform = self.topToolbar.transform; - CGAffineTransform bottomToolbarBaseTransform = self.bottomToolbar.transform; - // Translate the top toolbar up offscreen by shifting it up by its height. - CGAffineTransform topToolbarOffsetTransform = CGAffineTransformTranslate( - self.topToolbar.transform, /*tx=*/0, - /*ty=*/-(self.topToolbar.bounds.size.height * 0.5)); - // Translate the bottom toolbar down offscreen by shifting it down by its - // height. - CGAffineTransform bottomToolbarOffsetTransform = CGAffineTransformTranslate( - self.bottomToolbar.transform, /*tx=*/0, - /*ty=*/(self.topToolbar.bounds.size.height * 0.5)); + // Fade the toolbars out in the first 66% of the transition. + auto keyframe = ^{ + [UIView addKeyframeWithRelativeStartTime:0 + relativeDuration:0.66 + animations:^{ + self.topToolbar.alpha = 0.0; + self.bottomToolbar.alpha = 0.0; + }]; + }; - // Block that animates the toolbar transforms, suitable for using with the - // transition coordinator. + // Animation block that does the keyframe animation. auto animation = ^(id<UIViewControllerTransitionCoordinatorContext> context) { - self.topToolbar.transform = topToolbarOffsetTransform; - self.bottomToolbar.transform = bottomToolbarOffsetTransform; + [UIView animateKeyframesWithDuration:context.transitionDuration + delay:0 + options:UIViewAnimationOptionLayoutSubviews + animations:keyframe + completion:nil]; }; // Hide the scroll view (and thus the tab grids) until the transition - // completes. + // completes. Restore the toolbar opacity when the transition completes. self.scrollView.hidden = YES; auto cleanup = ^(id<UIViewControllerTransitionCoordinatorContext> context) { self.scrollView.hidden = NO; - self.topToolbar.transform = topToolbarBaseTransform; - self.bottomToolbar.transform = bottomToolbarBaseTransform; + self.topToolbar.alpha = 1.0; + self.bottomToolbar.alpha = 1.0; }; - // Animate the toolbars into place alongside the current transition. + // Animate the toolbar alphas alongside the current transition. [self.transitionCoordinator animateAlongsideTransition:animation completion:cleanup]; }
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn b/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn index 7337c0f..cdbf4eb 100644 --- a/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn +++ b/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn
@@ -23,5 +23,6 @@ deps = [ "//base", + "//ios/chrome/browser/ui/util", ] }
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm b/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm index e566b8f..630e6460 100644 --- a/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm +++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm
@@ -5,13 +5,17 @@ #import "ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.h" #import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h" +#import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h" #import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_state_providing.h" +#import "ios/chrome/browser/ui/util/layout_guide_names.h" +#import "ios/chrome/browser/ui/util/named_guide.h" +#import "ios/chrome/browser/ui/util/property_animator_group.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif -@interface GridToVisibleTabAnimator ()<GridTransitionAnimationDelegate> +@interface GridToVisibleTabAnimator () @property(nonatomic, weak) id<GridTransitionStateProviding> stateProvider; // Animation object for this transition. @property(nonatomic, strong) GridTransitionAnimation* animation; @@ -35,7 +39,7 @@ - (NSTimeInterval)transitionDuration: (id<UIViewControllerContextTransitioning>)transitionContext { - return 0.4; + return 0.3; } - (void)animateTransition: @@ -63,33 +67,45 @@ GridTransitionLayout* layout = [self.stateProvider layoutForTransitionContext:transitionContext]; + // Ask the state provider for the views to use when inserting the animation. + UIView* proxyContainer = + [self.stateProvider proxyContainerForTransitionContext:transitionContext]; + + // Find the rect for the animating tab by getting the content area layout + // guide. + // Conceptually this transition is presenting a tab (a BVC). However, + // currently the BVC instances are themselves contanted within a BVCContainer + // view controller. This means that the |presentedView| is not the BVC's + // view; rather it's the view of the view controller that contains the BVC. + // Unfortunatley, the layout guide needed here is attached to the BVC's view, + // which is the first (and only) subview of the BVCContainerViewController's + // view. + // TODO(crbug.com/860234) Clean up this arrangement. + UIView* viewWithNamedGuides = presentedView.subviews[0]; + CGRect finalRect = + [NamedGuide guideWithName:kContentAreaGuide view:viewWithNamedGuides] + .layoutFrame; + layout.expandedRect = + [proxyContainer convertRect:finalRect fromView:viewWithNamedGuides]; + + NSTimeInterval duration = [self transitionDuration:transitionContext]; // Create the animation view and insert it. self.animation = [[GridTransitionAnimation alloc] initWithLayout:layout - delegate:self + duration:duration direction:GridAnimationDirectionExpanding]; - // Ask the state provider for the views to use when inserting the animation. - UIView* proxyContainer = - [self.stateProvider proxyContainerForTransitionContext:transitionContext]; UIView* viewBehindProxies = [self.stateProvider proxyPositionForTransitionContext:transitionContext]; - [proxyContainer insertSubview:self.animation aboveSubview:viewBehindProxies]; - NSTimeInterval duration = [self transitionDuration:transitionContext]; - - // Fade in the presented view. - [UIView animateWithDuration:duration * 0.3 - delay:duration * 0.7 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - presentedView.alpha = 1.0; - } - completion:nil]; + [self.animation.animator addCompletion:^(UIViewAnimatingPosition position) { + BOOL finished = (position == UIViewAnimatingPositionEnd); + [self gridTransitionAnimationDidFinish:finished]; + }]; // Run the main animation. - [self.animation animateWithDuration:duration]; + [self.animation.animator startAnimation]; } - (void)gridTransitionAnimationDidFinish:(BOOL)finished { @@ -104,6 +120,9 @@ if (self.transitionContext.transitionWasCancelled) { [presentedView removeFromSuperview]; } else { + // TODO(crbug.com/850507): Have the tab view animate itself in alongside + // this transition instead of just setting the alpha here. + presentedView.alpha = 1; [gridView removeFromSuperview]; } // Mark the transition as completed.
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h index 501f683..043b1d6 100644 --- a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h +++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h
@@ -11,44 +11,36 @@ // The directions the animation can take. typedef NS_ENUM(NSUInteger, GridAnimationDirection) { - // Moving from the expanded grid down into the regular grid. + // Moving from an expanded single tab down into the grid. GridAnimationDirectionContracting = 0, - // Moving from the regular grid out to the expanded grid. + // Moving from the grid out to an expanded single tab. GridAnimationDirectionExpanding = 1, }; -// Delegate for this animation, to be informed about animation events. -@protocol GridTransitionAnimationDelegate -// Tell the delegate thet the animation completed. If |finished| is YES, then -// the animation was able to run its full duration. -- (void)gridTransitionAnimationDidFinish:(BOOL)finished; -@end - // A view that encapsulates an animation used to transition into a grid. // A transition animator should place this view at the appropriate place in the -// view hierarchy and then call -animateWithDuration: to trigger the animations. -// TODO(crbug.com/804539): Update this class to be an Orchestrator object -// that the present and dismiss animations can both use. +// view hierarchy and then call |-beginAnimations| on its |animator| property. @interface GridTransitionAnimation : UIView +// The animator object this animation uses; it will have the same duration +// that this object is initialized with. +// This property is |nil| until this object is added to a view hierarchy. Any +// animations or callbacks added to |animator| must be added *after* this object +// is added as a subview of another view. +@property(nonatomic, readonly) id<UIViewImplicitlyAnimating> animator; + // Designated initializer. |layout| is a GridTransitionLayout object defining // the layout the animation should animate to. |delegate| is an object that will -// be informed about events in this object's animation. -// If |startsExpanded| is YES, the animation will start with the grid cells in -// the expanded position and zoom down to the regular grid position. Otherwise -// they will start in the grid position and zoom out to the expanded positions. +// be informed about events in this object's animation. |direction| is the +// direction that the transition will animate. - (instancetype)initWithLayout:(GridTransitionLayout*)layout - delegate:(id<GridTransitionAnimationDelegate>)delegate + duration:(NSTimeInterval)duration direction:(GridAnimationDirection)direction NS_DESIGNATED_INITIALIZER; - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE; - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; -// Runs the animation for this object with the passed duration. -// It's an error to call this more than once on any instance of this object. -- (void)animateWithDuration:(NSTimeInterval)duration; - @end #endif // IOS_CHROME_BROWSER_UI_TAB_GRID_TRANSITIONS_GRID_TRANSITION_ANIMATION_H_
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm index 1cd2332..ee535bf2 100644 --- a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm +++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm
@@ -4,53 +4,63 @@ #import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h" +#import "base/logging.h" #import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h" +#import "ios/chrome/browser/ui/util/property_animator_group.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif +namespace { + +// Scale factor for inactive items when a tab is expanded. +const CGFloat kInactiveItemScale = 0.95; +} + @interface GridTransitionAnimation () +// The property animator group backing the public |animator| property. +@property(nonatomic, readonly) PropertyAnimatorGroup* animations; // The layout of the grid for this animation. @property(nonatomic, strong) GridTransitionLayout* layout; -// The delegate for this animation. -@property(nonatomic, weak) id<GridTransitionAnimationDelegate> delegate; +// The duration of the animation. +@property(nonatomic, readonly, assign) NSTimeInterval duration; // The direction this animation is in. @property(nonatomic, readonly, assign) GridAnimationDirection direction; -// The x and y scales of the enlarged grid relative to the selected cell in the -// regular grid. -@property(nonatomic, assign) CGFloat xScale; -@property(nonatomic, assign) CGFloat yScale; -// Convenience properties for getting the size and center of the selected cell +// Convenience properties for getting the size and center of the active cell // in the grid. -@property(nonatomic, readonly) CGSize selectedSize; -@property(nonatomic, readonly) CGPoint selectedCenter; -// Corner radius that the selected cell will have when it is animated into the +@property(nonatomic, readonly) CGSize activeSize; +@property(nonatomic, readonly) CGPoint activeCenter; +// Corner radius that the active cell will have when it is animated into the // regulat grid. -@property(nonatomic, assign) CGFloat finalSelectedCellCornerRadius; +@property(nonatomic, assign) CGFloat finalActiveCellCornerRadius; @end @implementation GridTransitionAnimation +@synthesize animations = _animations; @synthesize layout = _layout; -@synthesize delegate = _delegate; +@synthesize duration = _duration; @synthesize direction = _direction; -@synthesize xScale = _xScale; -@synthesize yScale = _yScale; -@synthesize finalSelectedCellCornerRadius = _finalSelectedCellCornerRadius; +@synthesize finalActiveCellCornerRadius = _finalActiveCellCornerRadius; - (instancetype)initWithLayout:(GridTransitionLayout*)layout - delegate:(id<GridTransitionAnimationDelegate>)delegate + duration:(NSTimeInterval)duration direction:(GridAnimationDirection)direction { if (self = [super initWithFrame:CGRectZero]) { + _animations = [[PropertyAnimatorGroup alloc] init]; _layout = layout; - _delegate = delegate; + _duration = duration; _direction = direction; - _finalSelectedCellCornerRadius = - _layout.selectedItem.cell.contentView.layer.cornerRadius; + _finalActiveCellCornerRadius = + _layout.activeItem.cell.contentView.layer.cornerRadius; } return self; } +- (id<UIViewImplicitlyAnimating>)animator { + return self.animations; +} + #pragma mark - UIView - (void)willMoveToSuperview:(UIView*)newSuperview { @@ -61,293 +71,362 @@ } - (void)didMoveToSuperview { + if (!self.superview) + return; // Positioning the animating items depends on converting points to this // view's coordinate system, so wait until it's in a view hierarchy. switch (self.direction) { case GridAnimationDirectionContracting: - [self positionSelectedItemInExpandedGrid]; - [self positionUnselectedItemsInExpandedGrid]; + [self positionExpandedActiveItem]; + [self prepareInactiveItemsForAppearance]; + [self buildContractingAnimations]; break; case GridAnimationDirectionExpanding: - [self positionSelectedItemInRegularGrid]; - [self positionUnselectedItemsInRegularGrid]; + [self prepareAllItemsForExpansion]; + [self buildExpandingAnimations]; break; } + // Make sure all of the layout after the view setup is complete before any + // animations are run. + [self layoutIfNeeded]; } #pragma mark - Private Properties -- (CGSize)selectedSize { - return self.layout.selectedItem.attributes.size; +- (CGSize)activeSize { + return self.layout.activeItem.attributes.size; } -- (CGPoint)selectedCenter { - return self.layout.selectedItem.attributes.center; -} - -#pragma mark - Public methods - -- (void)animateWithDuration:(NSTimeInterval)duration { - switch (self.direction) { - case GridAnimationDirectionContracting: - [self animateToRegularGridWithDuration:duration]; - break; - case GridAnimationDirectionExpanding: - [self animateToExpandedGridWithDuration:duration]; - break; - } +- (CGPoint)activeCenter { + return self.layout.activeItem.attributes.center; } #pragma mark - Private methods -- (void)animateToRegularGridWithDuration:(NSTimeInterval)duration { - // The transition is structured as two or three separate animations. They are - // timed based on |staggeredDuration|, which is a configurable fraction - // of the overall animation duration. - CGFloat staggeredDuration = duration * 0.7; +- (void)buildContractingAnimations { + // The transition is structured as two or four separate animations. They are + // timed based on various sub-durations and delays which are expressed as + // fractions of the overall animation duration. + CGFloat partialDuration = 0.6; + CGFloat briefDuration = partialDuration * 0.5; + CGFloat shortDelay = 0.12; + CGFloat longDelay = 0.4; - // If there's only one cell, the animation has two parts: - // (A) Fading in the selected cell highlight indicator. - // (B) Zooming the selected cell into position. - // These parts are timed over |duration| like this: + // If there's only one cell, the animation has two parts. + // (A) Zooming the active cell into position. + // (B) Fading in the active cell's auxilliary view. + // (C) Rounding the corners of the active cell. // - // |#|----------[A]--------------------{100%} - // {0%}------------------[B]--------------------{100%} + // {0%}------------------[A]----------{60%} + // {40%}--[B]---------------{70%} + // {0%}---[C]---{30%} + + // If there's more than once cell, the animation adds two more parts: + // (D) Scaling up the inactive cells. + // (E) Fading the inactive cells to 100% opacity. + // The overall timing is as follows: // - // (|#| is |duration| - |staggeredDuration|). - // Animation B will call the completion handler in this case. - - // If there's more than once cell, the animation has three parts: - // (A) Fading in the selected cell highlight indicator. - // (B) Zooming the selected cell into position. - // (C) Zooming the unselected cells into position. - // The timing is as follows: + // {0%}------------------[A]----------{60%} + // {40%}--[B]---------------{70%} + // {0%}---[C]---{30%} + // {0%}------------------[D]--------------------{100%} + // {12%}--------------[E]-------------- {72%} // - // |#|----------[A]--------------------{100%} - // {0%}------------------[B]------|*| - // |#|----------[C]--------------------{100%} - // (|*| is |staggeredDuration|). - // (|#| is |duration| - |staggeredDuration|). - // Animation C will call the completion handler in this case. + // All animations are timed ease-in (so more motion happens later), except + // for C which is relatively small in space and short in duration; it has + // linear timing so it doesn't seem instantaneous. + // (Changing the timing constants above will change the timing % values) - // TODO(crbug.com/820410): Tune the timing, relative pacing, and curves of - // these animations. - - UICollectionViewCell* selectedCell = self.layout.selectedItem.cell; - - // Run animation (A) starting at |1 - staggeredDuration|. - [UIView animateWithDuration:staggeredDuration - delay:duration - staggeredDuration - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - selectedCell.selected = YES; - } - completion:nil]; - - // Completion block to be run when the transition completes. - auto completion = ^(BOOL finished) { - // Tell the delegate the animation has completed. - [self.delegate gridTransitionAnimationDidFinish:finished]; + // A: Zoom the active cell into position. + auto zoomActiveCellAnimation = ^{ + [self positionAndScaleActiveItemInGrid]; }; + auto zoomActiveCellKeyframeAnimation = + [self keyframeAnimationWithRelativeStart:0 + relativeDuration:partialDuration + animations:zoomActiveCellAnimation]; + UIViewPropertyAnimator* zoomActiveCell = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveEaseIn + animations:zoomActiveCellKeyframeAnimation]; + [self.animations addAnimator:zoomActiveCell]; + + // B: Fade in the active cell's auxillary view + UIView* auxillaryView = self.layout.activeItem.auxillaryView; + auto fadeInAuxillaryKeyframeAnimation = + [self keyframeAnimationWithRelativeStart:longDelay + relativeDuration:briefDuration + animations:^{ + auxillaryView.alpha = 1.0; + }]; + UIViewPropertyAnimator* fadeInAuxillary = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveEaseIn + animations:fadeInAuxillaryKeyframeAnimation]; + [self.animations addAnimator:fadeInAuxillary]; + + // C: Round the corners of the active cell. + UICollectionViewCell* cell = self.layout.activeItem.cell; + cell.contentView.layer.cornerRadius = 0.0; + auto roundCornersAnimation = ^{ + cell.contentView.layer.cornerRadius = self.finalActiveCellCornerRadius; + }; + auto roundCornersKeyframeAnimation = + [self keyframeAnimationWithRelativeStart:0 + relativeDuration:briefDuration + animations:roundCornersAnimation]; + UIViewPropertyAnimator* roundCorners = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveLinear + animations:roundCornersKeyframeAnimation]; + [self.animations addAnimator:roundCorners]; if (self.layout.items.count == 1) { // Single cell case. - // Run animation (B) for the whole duration without delay. - [UIView animateWithDuration:duration - delay:0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - [self positionSelectedItemInRegularGrid]; - } - completion:completion]; - } else { - // Multiple cell case. - // Run animation (B) up to |staggeredDuration|. - [UIView animateWithDuration:staggeredDuration - delay:0.0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - [self positionSelectedItemInRegularGrid]; - } - completion:nil]; - - // Run animation (C) for |staggeredDuration| up to the end of the - // transition. - [UIView animateWithDuration:staggeredDuration - delay:duration - staggeredDuration - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - [self positionUnselectedItemsInRegularGrid]; - } - completion:completion]; + return; } + + // Additional animations for multiple cells. + // D: Scale up inactive cells. + auto scaleUpCellsAnimation = ^{ + for (GridTransitionLayoutItem* item in self.layout.items) { + if (item == self.layout.activeItem) + continue; + item.cell.transform = CGAffineTransformIdentity; + } + }; + UIViewPropertyAnimator* scaleUpCells = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveEaseOut + animations:scaleUpCellsAnimation]; + [self.animations addAnimator:scaleUpCells]; + + // E: Fade in inactive cells. + auto fadeInCellsAnimation = ^{ + for (GridTransitionLayoutItem* item in self.layout.items) { + if (item == self.layout.activeItem) + continue; + item.cell.alpha = 1.0; + } + }; + auto fadeInCellsKeyframeAnimation = + [self keyframeAnimationWithRelativeStart:shortDelay + relativeDuration:partialDuration + animations:fadeInCellsAnimation]; + UIViewPropertyAnimator* fadeInCells = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveEaseOut + animations:fadeInCellsKeyframeAnimation]; + [self.animations addAnimator:fadeInCells]; } -- (void)animateToExpandedGridWithDuration:(NSTimeInterval)duration { - // The transition is structured as two or three separate animations. They are - // timed based on |staggeredDuration|, which is a configurable fraction - // of the overall animation duration. - CGFloat staggeredDuration = duration * 0.9; +- (void)buildExpandingAnimations { + // The transition is structured as two or four separate animations. They are + // timed based on two sub-durations which are expressed as fractions of the + // overall animation duration. + CGFloat partialDuration = 0.66; + CGFloat briefDuration = 0.23; - // If there's only one cell, the animation has two parts: - // (A) Fading out the selected cell highlight indicator. - // (B) Zooming the selected cell out into position. + // If there's only one cell, the animation has three parts: + // (A) Zooming the active cell out into the expanded position. + // (B) Fading out the active cell's auxilliary view. + // (C) Squaring the corners of the active cell. // These parts are timed over |duration| like this: // - // {0%}-----------[A]-------------|*| - // {0%}------------------[B]--------------------{100%} + // {0%}--[A]-----------------------------------{100%} + // {0%}--[B]--{23%} + // {77%}---[C]---{100%} + + // If there's more than once cell, the animation adds: + // (C) Scaling the inactive cells to 95% + // (D) Fading out the inactive cells. + // The overall timing is as follows: // - // (|#| is |duration| - |staggeredDuration|). - // Animation B will call the completion handler in this case. - - // If there's more than once cell, the animation has three parts: - // (A) Fading out the selected cell highlight indicator. - // (B) Zooming the selected cell into position. - // (C) Zooming the unselected cells into position. - // The timing is as follows: + // {0%}--[A]-----------------------------------{100%} + // {0%}--[B]--{23%} + // {77%}---[C]---{100%} + // {0%}--[D]-----------------------------------{100%} + // {0%}--[E]-----------------{66%} // - // {0%}-----------[A]-------------|*| - // {0%}---------- [C]-------------|*| - // |#|-------------[B]-----------{100%} - // (|*| is |staggeredDuration|) - // (|#| is |duration| - |staggeredDuration|). - // Animation C will call the completion handler in this case. + // All animations are timed ease-out (so more motion happens sooner), except + // for C which is relatively small in space and short in duration; it has + // linear timing so it doesn't seem instantaneous. - // TODO(crbug.com/820410): Tune the timing, relative pacing, and curves of - // these animations. + // A: Zoom the active cell into position. + UIViewPropertyAnimator* zoomActiveCell = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveEaseOut + animations:^{ + [self positionExpandedActiveItem]; + }]; + [self.animations addAnimator:zoomActiveCell]; - UICollectionViewCell* selectedCell = self.layout.selectedItem.cell; + // B: Fade out the active cell's auxillary view. + UIView* auxillaryView = self.layout.activeItem.auxillaryView; + auto fadeOutAuxilliaryAnimation = + [self keyframeAnimationWithRelativeStart:0 + relativeDuration:briefDuration + animations:^{ + auxillaryView.alpha = 0.0; + }]; + UIViewPropertyAnimator* fadeOutAuxilliary = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveEaseOut + animations:fadeOutAuxilliaryAnimation]; + [self.animations addAnimator:fadeOutAuxilliary]; - // Run animation (A) for |staggeredDuration|. - [UIView animateWithDuration:staggeredDuration - delay:0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - selectedCell.selected = NO; - } - completion:nil]; - - // Completion block to be run when the transition completes. - auto completion = ^(BOOL finished) { - // Tell the delegate the animation has completed. - [self.delegate gridTransitionAnimationDidFinish:finished]; + // C: Square the active cell's corners. + UICollectionViewCell* cell = self.layout.activeItem.cell; + auto squareCornersAnimation = ^{ + cell.contentView.layer.cornerRadius = 0.0; }; + auto squareCornersKeyframeAnimation = + [self keyframeAnimationWithRelativeStart:1.0 - briefDuration + relativeDuration:briefDuration + animations:squareCornersAnimation]; + UIViewPropertyAnimator* squareCorners = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveLinear + animations:squareCornersKeyframeAnimation]; + [self.animations addAnimator:squareCorners]; - if (self.layout.items.count == 1) { - // Single cell case. - // Run animation (B) for the whole duration without delay. - [UIView animateWithDuration:duration - delay:0 - options:UIViewAnimationOptionCurveEaseIn - animations:^{ - [self positionSelectedItemInExpandedGrid]; - } - completion:completion]; - } else { - // Multiple cell case. - // Run animation (C) for |staggeredDuration|. - [UIView animateWithDuration:staggeredDuration - delay:0.0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - [self positionUnselectedItemsInExpandedGrid]; - } - completion:completion]; + // If there's only a single cell, that's all. + if (self.layout.items.count == 1) + return; - // Run animation (B) for |staggeredDuration| up to the end of the - // transition. - [UIView animateWithDuration:staggeredDuration - delay:duration - staggeredDuration - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - [self positionSelectedItemInExpandedGrid]; - } - completion:nil]; - } + // Additional animations for multiple cells. + // D: Scale down inactive cells. + auto scaleDownCellsAnimation = ^{ + for (GridTransitionLayoutItem* item in self.layout.items) { + if (item == self.layout.activeItem) + continue; + item.cell.transform = CGAffineTransformScale( + item.cell.transform, kInactiveItemScale, kInactiveItemScale); + } + }; + UIViewPropertyAnimator* scaleDownCells = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveEaseOut + animations:scaleDownCellsAnimation]; + [self.animations addAnimator:scaleDownCells]; + + // E: Fade out inactive cells. + auto fadeOutCellsAnimation = ^{ + for (GridTransitionLayoutItem* item in self.layout.items) { + if (item == self.layout.activeItem) + continue; + item.cell.alpha = 0.0; + } + }; + auto fadeOutCellsKeyframeAnimation = + [self keyframeAnimationWithRelativeStart:0 + relativeDuration:partialDuration + animations:fadeOutCellsAnimation]; + UIViewPropertyAnimator* fadeOutCells = [[UIViewPropertyAnimator alloc] + initWithDuration:self.duration + curve:UIViewAnimationCurveEaseOut + animations:fadeOutCellsKeyframeAnimation]; + [self.animations addAnimator:fadeOutCells]; } -// Perfrom the initial setup for the animation, computing scale based on the +// Perfroms the initial setup for the animation, computing scale based on the // superview size and adding the transition cells to the view hierarchy. - (void)prepareForAnimationInSuperview:(UIView*)newSuperview { - // Extract some useful metrics from the animation superview. - CGSize animationSize = newSuperview.bounds.size; + // Add the selection item first, so it's under ther other views. + [self addSubview:self.layout.selectionItem.cell]; - // Compute the scale of the transition grid (which is at the proportional size - // of the superview). - self.xScale = animationSize.width / self.selectedSize.width; - self.yScale = animationSize.height / self.selectedSize.height; + // Only show the selection part. + self.layout.selectionItem.cell.contentView.hidden = YES; + self.layout.selectionItem.cell.selected = YES; + // Add the active item last so it's always the top subview. for (GridTransitionLayoutItem* item in self.layout.items) { + if (item == self.layout.activeItem) + continue; [self addSubview:item.cell]; } + [self addSubview:self.layout.activeItem.cell]; } -// Positions the selected item in the expanded grid position with a zero corner -// radius. -- (void)positionSelectedItemInExpandedGrid { - [self positionAndScaleItemInExpandedGrid:self.layout.selectedItem]; - UICollectionViewCell* cell = self.layout.selectedItem.cell; - cell.contentView.layer.cornerRadius = 0.0; +// Positions the active item in the expanded grid position with a zero corner +// radius and a 0% opacity auxilliary view. +- (void)positionExpandedActiveItem { + GridTransitionLayoutItem* activeItem = self.layout.activeItem; + UICollectionViewCell* cell = activeItem.cell; + // Ensure that the cell's subviews are correctly positioned. + [cell layoutIfNeeded]; + // Position the cell frame so so that the area below the aux view matches the + // expanded rect. + // Easiest way to do this is to set the frame to the expanded rect and then + // add height to it to include the aux view height. + CGFloat auxHeight = activeItem.auxillaryView.frame.size.height; + CGRect cellFrame = self.layout.expandedRect; + cellFrame.size.height += auxHeight; + cellFrame.origin.y -= auxHeight; + cell.frame = cellFrame; + activeItem.auxillaryView.alpha = 0.0; } -// Positions all of the non-selected items in their expanded grid positions. -- (void)positionUnselectedItemsInExpandedGrid { - // Lay out the transition grid add it as subviews. +// Positions all of the inactive items in their grid positions. +// Fades and scales each of those items. +- (void)prepareInactiveItemsForAppearance { for (GridTransitionLayoutItem* item in self.layout.items) { - if (item == self.layout.selectedItem) + if (item == self.layout.activeItem) continue; - [self positionAndScaleItemInExpandedGrid:item]; + [self positionItemInGrid:item]; + item.cell.alpha = 0.0; + item.cell.transform = CGAffineTransformScale( + item.cell.transform, kInactiveItemScale, kInactiveItemScale); } + [self positionItemInGrid:self.layout.selectionItem]; } -// Positions the selected item in the regular grid position with its final +// Positions the active item in the regular grid position with its final // corner radius. -- (void)positionSelectedItemInRegularGrid { - [self positionAndScaleItemInRegularGrid:self.layout.selectedItem]; - UICollectionViewCell* cell = self.layout.selectedItem.cell; - cell.contentView.layer.cornerRadius = self.finalSelectedCellCornerRadius; -} - -// Positions all of the non-selected items in their regular grid positions. -- (void)positionUnselectedItemsInRegularGrid { - for (GridTransitionLayoutItem* item in self.layout.items) { - if (item == self.layout.selectedItem) - continue; - [self positionAndScaleItemInRegularGrid:item]; - } -} - -// Positions |item| in its regular grid position. -- (void)positionAndScaleItemInRegularGrid:(GridTransitionLayoutItem*)item { - UIView* cell = item.cell; - cell.center = - [self.superview convertPoint:item.attributes.center fromView:nil]; +- (void)positionAndScaleActiveItemInGrid { + UICollectionViewCell* cell = self.layout.activeItem.cell; cell.transform = CGAffineTransformIdentity; + cell.frame = self.layout.activeItem.attributes.frame; + [self positionItemInGrid:self.layout.activeItem]; } -// Positions |item| in its expanded grid position. -- (void)positionAndScaleItemInExpandedGrid:(GridTransitionLayoutItem*)item { - UICollectionViewCell* cell = item.cell; - cell.bounds = item.attributes.bounds; - // Add a scale transform to the cell so it matches the x-scale of the - // open tab. Scaling is only based on the x-scale so that the aspect ratio of - // the cell will be preserved. - cell.transform = - CGAffineTransformScale(cell.transform, self.xScale, self.xScale); - cell.center = [self expandedCenterForItem:item]; +// Prepares all of the items for an expansion anumation. +- (void)prepareAllItemsForExpansion { + for (GridTransitionLayoutItem* item in self.layout.items) { + [self positionItemInGrid:item]; + } + [self positionItemInGrid:self.layout.selectionItem]; } -// Returns the center point for an item in the expanded grid position. This is -// computed by scaling its center point relative to the selected item's center -// point. The scaling factors are the ratios of the animation view's height and -// width to the selected cell's height and width. -- (CGPoint)expandedCenterForItem:(GridTransitionLayoutItem*)item { - // Convert item center from window coordinates. - CGPoint gridCenter = [self convertPoint:item.attributes.center fromView:nil]; - // Map that to the scale and position of the transition grid. - return CGPointMake( - self.center.x + ((gridCenter.x - self.selectedCenter.x) * self.xScale), - self.center.y + ((gridCenter.y - self.selectedCenter.y) * self.yScale)); +// Positions |item| in it grid position. +- (void)positionItemInGrid:(GridTransitionLayoutItem*)item { + UIView* cell = item.cell; + CGPoint attributeCenter = item.attributes.center; + CGPoint newCenter = + [self.superview convertPoint:attributeCenter fromView:nil]; + cell.center = newCenter; +} + +// Helper function to construct keyframe animation blocks. +// Given |start| and |duration| (in the [0.0-1.0] interval), returns an +// animation block which runs |animations| starting at |start| (relative to +// |self.duration|) and running for |duration| (likewise). +- (void (^)(void))keyframeAnimationWithRelativeStart:(double)start + relativeDuration:(double)duration + animations: + (void (^)(void))animations { + auto keyframe = ^{ + [UIView addKeyframeWithRelativeStartTime:start + relativeDuration:duration + animations:animations]; + }; + return ^{ + [UIView animateKeyframesWithDuration:self.duration + delay:0 + options:UIViewAnimationOptionLayoutSubviews + animations:keyframe + completion:nil]; + }; } @end
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h index c8de9ee3..8944963 100644 --- a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h +++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h
@@ -16,16 +16,25 @@ // All of the items in the layout. @property(nonatomic, copy, readonly) NSArray<GridTransitionLayoutItem*>* items; -// The item in the layout (if any) that's selected. -// Note that |selectedItem.cell.selected| doesn't need to be YES; the transition +// The item in the layout (if any) that's the 'active' item (the one that will +// expand and contract). +// Note that |activeItem.cell.selected| doesn't need to be YES; the transition // animation may set or unset that selection state as part of the animation. -@property(nonatomic, strong, readonly) GridTransitionLayoutItem* selectedItem; +@property(nonatomic, strong, readonly) GridTransitionLayoutItem* activeItem; -// Creates a new layout object with |items|, and |selectedItem| selected. +// An item that may be used to *only* show the selection state. +// |selectionItem| is not one of the items in |items|. +@property(nonatomic, strong, readonly) GridTransitionLayoutItem* selectionItem; + +// The rect, in UIWindow coordinates, that an "expanded" item should occupy. +@property(nonatomic) CGRect expandedRect; + +// Creates a new layout object with |items|, and |activeItem| selected. // |items| should be non-nil, but it may be empty. -// |selectedItem| must either be nil, or one of the members of |items|. +// |activeItem| must either be nil, or one of the members of |items|. + (instancetype)layoutWithItems:(NSArray<GridTransitionLayoutItem*>*)items - selectedItem:(GridTransitionLayoutItem*)selectedItem; + activeItem:(GridTransitionLayoutItem*)activeItem + selectionItem:(GridTransitionLayoutItem*)selectionItem; @end @@ -38,6 +47,10 @@ // ition, but the value of thie property should not be in any view hierarchy // when the layout item is created. @property(nonatomic, strong, readonly) UICollectionViewCell* cell; + +// An auxillary view in |cell|'s view hierarchy that may also be animated. +@property(nonatomic, weak, readonly) UIView* auxillaryView; + // The layout attributes for the cell in the collection view, normalized to // UIWindow coordinates. It's the responsibility of the setter to do this // normalization. @@ -47,9 +60,11 @@ // Creates a new layout item instance will |cell| and |attributes|, neither of // which can be nil. // It's an error if |cell| has a superview. +// It's an error if |auxillaryView| is not a subview of |cell|; it may be nil. // The properties (size, etc) of |attributes| don't need to match the corres- // ponding properties of |cell| when the item is created. + (instancetype)itemWithCell:(UICollectionViewCell*)cell + auxillaryView:(UIView*)auxillaryView attributes:(UICollectionViewLayoutAttributes*)attributes; @end
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.mm b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.mm index 14e4126..32b27f8f 100644 --- a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.mm +++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.mm
@@ -12,47 +12,55 @@ @interface GridTransitionLayout () @property(nonatomic, readwrite) NSArray<GridTransitionLayoutItem*>* items; -@property(nonatomic, readwrite) GridTransitionLayoutItem* selectedItem; +@property(nonatomic, readwrite) GridTransitionLayoutItem* activeItem; +@property(nonatomic, readwrite) GridTransitionLayoutItem* selectionItem; @end @implementation GridTransitionLayout -@synthesize selectedItem = _selectedItem; +@synthesize activeItem = _activeItem; +@synthesize selectionItem = _selectionItem; @synthesize items = _items; +@synthesize expandedRect = _expandedRect; + (instancetype)layoutWithItems:(NSArray<GridTransitionLayoutItem*>*)items - selectedItem:(GridTransitionLayoutItem*)selectedItem { + activeItem:(GridTransitionLayoutItem*)activeItem + selectionItem:(GridTransitionLayoutItem*)selectionItem { DCHECK(items); GridTransitionLayout* layout = [[GridTransitionLayout alloc] init]; layout.items = items; - layout.selectedItem = selectedItem; + layout.activeItem = activeItem; + layout.selectionItem = selectionItem; return layout; } -- (void)setSelectedItem:(GridTransitionLayoutItem*)selectedItem { - DCHECK(!selectedItem || [self.items containsObject:selectedItem]); - _selectedItem = selectedItem; +- (void)setActiveItem:(GridTransitionLayoutItem*)activeItem { + DCHECK(!activeItem || [self.items containsObject:activeItem]); + _activeItem = activeItem; } @end @interface GridTransitionLayoutItem () @property(nonatomic, readwrite) UICollectionViewCell* cell; +@property(nonatomic, readwrite) UIView* auxillaryView; @property(nonatomic, readwrite) UICollectionViewLayoutAttributes* attributes; @end @implementation GridTransitionLayoutItem @synthesize cell = _cell; +@synthesize auxillaryView = _auxillaryView; @synthesize attributes = _attributes; + (instancetype)itemWithCell:(UICollectionViewCell*)cell + auxillaryView:(UIView*)auxillaryView attributes:(UICollectionViewLayoutAttributes*)attributes { DCHECK(cell); - DCHECK(attributes); DCHECK(!cell.superview); + DCHECK(!auxillaryView || [auxillaryView isDescendantOfView:cell]); GridTransitionLayoutItem* item = [[GridTransitionLayoutItem alloc] init]; item.cell = cell; + item.auxillaryView = auxillaryView; item.attributes = attributes; return item; } - @end
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm b/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm index f34e263e..6f440bf 100644 --- a/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm +++ b/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm
@@ -5,13 +5,17 @@ #import "ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.h" #import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.h" +#import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h" #import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_state_providing.h" +#import "ios/chrome/browser/ui/util/layout_guide_names.h" +#import "ios/chrome/browser/ui/util/named_guide.h" +#import "ios/chrome/browser/ui/util/property_animator_group.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif -@interface TabToGridAnimator ()<GridTransitionAnimationDelegate> +@interface TabToGridAnimator () @property(nonatomic, weak) id<GridTransitionStateProviding> stateProvider; // Animation object for this transition. @property(nonatomic, strong) GridTransitionAnimation* animation; @@ -35,7 +39,7 @@ - (NSTimeInterval)transitionDuration: (id<UIViewControllerContextTransitioning>)transitionContext { - return 0.4; + return 0.5; } - (void)animateTransition: @@ -53,6 +57,8 @@ UIView* dismissingView = [transitionContext viewForKey:UITransitionContextFromViewKey]; + // Find the rect for the animating tab by getting the content area layout + // guide. // Add the grid view to the container. This isn't just for the transition; // this is how the grid view controller's view is added to the view // hierarchy. @@ -60,35 +66,52 @@ gridView.frame = [transitionContext finalFrameForViewController:gridViewController]; + // Ask the state provider for the views to use when inserting the animation. + UIView* proxyContainer = + [self.stateProvider proxyContainerForTransitionContext:transitionContext]; + // Get the layout of the grid for the transition. GridTransitionLayout* layout = [self.stateProvider layoutForTransitionContext:transitionContext]; + // Get the initial rect for the snapshotted content of the active tab. + // Conceptually this transition is dismissing a tab (a BVC). However, + // currently the BVC instances are themselves contanted within a BVCContainer + // view controller. This means that the |dismissingView| is not the BVC's + // view; rather it's the view of the view controller that contains the BVC. + // Unfortunatley, the layout guide needed here is attached to the BVC's view, + // which is the first (and only) subview of the BVCContainerViewController's + // view. + // TODO(crbug.com/860234) Clean up this arrangement. + UIView* viewWithNamedGuides = dismissingView.subviews[0]; + CGRect initialRect = + [NamedGuide guideWithName:kContentAreaGuide view:viewWithNamedGuides] + .layoutFrame; + layout.expandedRect = + [proxyContainer convertRect:initialRect fromView:viewWithNamedGuides]; + + NSTimeInterval duration = [self transitionDuration:transitionContext]; // Create the animation view and insert it. self.animation = [[GridTransitionAnimation alloc] initWithLayout:layout - delegate:self + duration:duration direction:GridAnimationDirectionContracting]; - // Ask the state provider for the views to use when inserting the animation. - UIView* proxyContainer = - [self.stateProvider proxyContainerForTransitionContext:transitionContext]; UIView* viewBehindProxies = [self.stateProvider proxyPositionForTransitionContext:transitionContext]; - [proxyContainer insertSubview:self.animation aboveSubview:viewBehindProxies]; - NSTimeInterval duration = [self transitionDuration:transitionContext]; + [self.animation.animator addCompletion:^(UIViewAnimatingPosition position) { + BOOL finished = (position == UIViewAnimatingPositionEnd); + [self gridTransitionAnimationDidFinish:finished]; + }]; - // Fade out active tab view. - [UIView animateWithDuration:duration / 5 - animations:^{ - dismissingView.alpha = 0; - } - completion:nil]; + // TODO(crbug.com/850507): Have the tab view animate itself out alongside this + // transition instead of just zeroing the alpha here. + dismissingView.alpha = 0; // Run the main animation. - [self.animation animateWithDuration:duration]; + [self.animation.animator startAnimation]; } - (void)gridTransitionAnimationDidFinish:(BOOL)finished {
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc index 902eea9..2a3a435d 100644 --- a/net/cert/cert_verify_proc_unittest.cc +++ b/net/cert/cert_verify_proc_unittest.cc
@@ -2813,7 +2813,14 @@ // Tries verifying a certificate chain that is missing an intermediate. The // intermediate is available via AIA. -TEST_P(CertVerifyProcInternalWithNetFetchingTest, IntermediateFromAia200) { +// TODO(crbug.com/860189): Failing on iOS +#if defined(OS_IOS) +#define MAYBE_IntermediateFromAia200 DISABLED_IntermediateFromAia200 +#else +#define MAYBE_IntermediateFromAia200 IntermediateFromAia200 +#endif +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + MAYBE_IntermediateFromAia200) { const char kHostname[] = "www.example.com"; base::FilePath certs_dir =
diff --git a/net/url_request/url_request_job_factory_impl_unittest.cc b/net/url_request/url_request_job_factory_impl_unittest.cc index 58ee048..efbccd9 100644 --- a/net/url_request/url_request_job_factory_impl_unittest.cc +++ b/net/url_request/url_request_job_factory_impl_unittest.cc
@@ -74,7 +74,8 @@ } TEST(URLRequestJobFactoryTest, BasicProtocolHandler) { - base::test::ScopedTaskEnvironment scoped_task_environment; + base::test::ScopedTaskEnvironment scoped_task_environment( + base::test::ScopedTaskEnvironment::MainThreadType::IO); TestDelegate delegate; URLRequestJobFactoryImpl job_factory; TestURLRequestContext request_context;
diff --git a/remoting/host/backoff_timer_unittest.cc b/remoting/host/backoff_timer_unittest.cc index fe52bcd..425e010 100644 --- a/remoting/host/backoff_timer_unittest.cc +++ b/remoting/host/backoff_timer_unittest.cc
@@ -19,7 +19,7 @@ } // namespace TEST(BackoffTimer, Basic) { - base::MockTimer* mock_timer = new base::MockTimer(false, false); + base::MockOneShotTimer* mock_timer = new base::MockOneShotTimer(); BackoffTimer backoff_timer; backoff_timer.SetTimerForTest(base::WrapUnique(mock_timer)); ASSERT_FALSE(backoff_timer.IsRunning());
diff --git a/remoting/protocol/capture_scheduler_unittest.cc b/remoting/protocol/capture_scheduler_unittest.cc index 05ae90f..004a6a0 100644 --- a/remoting/protocol/capture_scheduler_unittest.cc +++ b/remoting/protocol/capture_scheduler_unittest.cc
@@ -32,7 +32,7 @@ scheduler_->set_minimum_interval( base::TimeDelta::FromMilliseconds(kMinumumFrameIntervalMs)); scheduler_->SetTickClockForTest(&tick_clock_); - capture_timer_ = new base::MockTimer(false, false); + capture_timer_ = new base::MockOneShotTimer(); scheduler_->SetTimerForTest(base::WrapUnique(capture_timer_)); scheduler_->Start(); } @@ -79,7 +79,7 @@ base::SimpleTestTickClock tick_clock_; // Owned by |scheduler_|. - base::MockTimer* capture_timer_; + base::MockOneShotTimer* capture_timer_; bool capture_called_; };
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn index 8a7efc8..2e45760 100644 --- a/services/network/BUILD.gn +++ b/services/network/BUILD.gn
@@ -142,7 +142,6 @@ "//components/cookie_config", "//components/network_session_configurator/browser", "//components/network_session_configurator/common", - "//components/os_crypt", "//components/prefs", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", @@ -187,10 +186,6 @@ } defines = [ "IS_NETWORK_SERVICE_IMPL" ] - - if (is_chromecast) { - defines += [ "IS_CHROMECAST" ] - } } source_set("tests") {
diff --git a/services/network/DEPS b/services/network/DEPS index 2e5b4b7..b946204 100644 --- a/services/network/DEPS +++ b/services/network/DEPS
@@ -3,7 +3,6 @@ "+components/content_settings/core/common", "+components/cookie_config", "+components/network_session_configurator", - "+components/os_crypt", # Prefs are used to create an independent file with a persisted key:value # store for networking-related data (Like which servers support QUIC), rather # than to store user preferences.
diff --git a/services/network/network_context.cc b/services/network/network_context.cc index 9e0d6bb..9284ae8 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc
@@ -1093,6 +1093,8 @@ // have to figure out which of the latter needs to move to the network // process). TODO: http://crbug.com/789644 if (params_->cookie_path) { + net::CookieCryptoDelegate* crypto_delegate = nullptr; + scoped_refptr<base::SequencedTaskRunner> client_task_runner = base::MessageLoopCurrent::Get()->task_runner(); scoped_refptr<base::SequencedTaskRunner> background_task_runner = @@ -1109,15 +1111,6 @@ new net::DefaultChannelIDStore(channel_id_db.get())); } - net::CookieCryptoDelegate* crypto_delegate = nullptr; - if (params_->enable_encrypted_cookies) { -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(IS_CHROMECAST) - DCHECK(network_service_->os_crypt_config_set()) - << "NetworkService::SetCryptConfig must be called before creating a " - "NetworkContext with encrypted cookies."; -#endif - crypto_delegate = cookie_config::GetCookieCryptoDelegate(); - } scoped_refptr<net::SQLitePersistentCookieStore> sqlite_store( new net::SQLitePersistentCookieStore( params_->cookie_path.value(), client_task_runner,
diff --git a/services/network/network_service.cc b/services/network/network_service.cc index c4df8ec..1aba17c2 100644 --- a/services/network/network_service.cc +++ b/services/network/network_service.cc
@@ -14,6 +14,7 @@ #include "base/metrics/histogram_macros.h" #include "base/task_scheduler/post_task.h" #include "base/values.h" +#include "build/build_config.h" #include "components/certificate_transparency/sth_distributor.h" #include "components/certificate_transparency/sth_observer.h" #include "mojo/public/cpp/bindings/strong_binding.h" @@ -38,11 +39,6 @@ #include "third_party/boringssl/src/include/openssl/cpu.h" #endif -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(IS_CHROMECAST) -#include "components/os_crypt/key_storage_config_linux.h" -#include "components/os_crypt/os_crypt.h" -#endif - namespace network { namespace { @@ -367,21 +363,6 @@ sth_distributor_->NewSTHObserved(sth); } -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -void NetworkService::SetCryptConfig(mojom::CryptConfigPtr crypt_config) { -#if !defined(IS_CHROMECAST) - auto config = std::make_unique<os_crypt::Config>(); - config->store = crypt_config->store; - config->product_name = crypt_config->product_name; - config->main_thread_runner = base::ThreadTaskRunnerHandle::Get(); - config->should_use_preference = crypt_config->should_use_preference; - config->user_data_path = crypt_config->user_data_path; - OSCrypt::SetConfig(std::move(config)); - os_crypt_config_set_ = true; -#endif -} -#endif - net::HttpAuthHandlerFactory* NetworkService::GetHttpAuthHandlerFactory() { if (!http_auth_handler_factory_) { http_auth_handler_factory_ = net::HttpAuthHandlerFactory::CreateDefault(
diff --git a/services/network/network_service.h b/services/network/network_service.h index 839a5da9..df84df0 100644 --- a/services/network/network_service.h +++ b/services/network/network_service.h
@@ -14,7 +14,6 @@ #include "base/macros.h" #include "base/memory/scoped_refptr.h" #include "base/optional.h" -#include "build/build_config.h" #include "mojo/public/cpp/bindings/binding.h" #include "net/http/http_auth_preferences.h" #include "net/log/net_log.h" @@ -133,9 +132,6 @@ void GetTotalNetworkUsages( mojom::NetworkService::GetTotalNetworkUsagesCallback callback) override; void UpdateSignedTreeHead(const net::ct::SignedTreeHead& sth) override; -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - void SetCryptConfig(mojom::CryptConfigPtr crypt_config) override; -#endif // Returns the shared HttpAuthHandlerFactory for the NetworkService, creating // one if needed. @@ -159,8 +155,6 @@ certificate_transparency::STHReporter* sth_reporter(); - bool os_crypt_config_set() const { return os_crypt_config_set_; } - static NetworkService* GetNetworkServiceForTesting(); private: @@ -227,8 +221,6 @@ bool quic_disabled_ = false; - bool os_crypt_config_set_ = false; - std::unique_ptr<certificate_transparency::STHDistributor> sth_distributor_; DISALLOW_COPY_AND_ASSIGN(NetworkService);
diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc index e0eed6b..abf2d0c 100644 --- a/services/network/network_service_unittest.cc +++ b/services/network/network_service_unittest.cc
@@ -97,7 +97,6 @@ TEST_F(NetworkServiceTest, CreateContextWithoutChannelID) { mojom::NetworkContextParamsPtr params = CreateContextParams(); params->cookie_path = base::FilePath(); - params->enable_encrypted_cookies = false; mojom::NetworkContextPtr network_context; service()->CreateNetworkContext(mojo::MakeRequest(&network_context), std::move(params));
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn index e9d7bfe..8aab22f 100644 --- a/services/network/public/mojom/BUILD.gn +++ b/services/network/public/mojom/BUILD.gn
@@ -121,10 +121,4 @@ export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1" export_header_blink = "third_party/blink/public/platform/web_common.h" } - - # This is only needed on desktop linux, but the defines make this difficult - # because IS_CHROMECAST is not available in build/build_config.h. - if (is_linux && !is_chromeos) { - enabled_features = [ "needs_crypt_config" ] - } }
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom index 89f5818..38bc97a 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom
@@ -62,9 +62,6 @@ // Points to the cookie file. An in-memory cookie store is used if it's empty. mojo_base.mojom.FilePath? cookie_path; - // If true, cookies will be stored encrypted. - bool enable_encrypted_cookies = true; - // If the cookie file is given, this controls whether previously written // session cookies are restored. Otherwise it should be false. bool restore_old_session_cookies = false;
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom index 2eeab65..2c08cd6e 100644 --- a/services/network/public/mojom/network_service.mojom +++ b/services/network/public/mojom/network_service.mojom
@@ -163,22 +163,6 @@ string android_negotiate_account_type; }; -// Values for configuring OSCrypt. -[EnableIf=needs_crypt_config] -struct CryptConfig { - // Force OSCrypt to use a specific linux password store. - string store; - - // The product name to use for permission prompts. - string product_name; - - // Controls whether preference on using or ignoring backends is used. - bool should_use_preference; - - // Preferences are stored in a separate file in the user data directory. - mojo_base.mojom.FilePath user_data_path; -}; - // Browser interface to the network service. interface NetworkService { SetClient(NetworkServiceClient client); @@ -247,9 +231,4 @@ // Transparency. Broadcast to each NetworkContext using the NetworkService. // NetworkContextes ignore STHs from unrecognized logs. UpdateSignedTreeHead(SignedTreeHead signed_tree_head); - - // Sets up OSCrypt for the network service process. Must be called before - // encrypted cookies can be read or set. - [EnableIf=needs_crypt_config] - SetCryptConfig(CryptConfig crypt_config); };
diff --git a/services/network/resource_scheduler_unittest.cc b/services/network/resource_scheduler_unittest.cc index bd42c9f..08e651d 100644 --- a/services/network/resource_scheduler_unittest.cc +++ b/services/network/resource_scheduler_unittest.cc
@@ -23,7 +23,6 @@ #include "base/test/mock_entropy_provider.h" #include "base/test/scoped_feature_list.h" #include "base/test/test_mock_time_task_runner.h" -#include "base/timer/mock_timer.h" #include "base/timer/timer.h" #include "net/base/host_port_pair.h" #include "net/base/request_priority.h" @@ -171,8 +170,7 @@ void InitializeScheduler(bool enabled = true) { CleanupScheduler(); - // Destroys previous scheduler, also destroys any previously created - // mock_timer_. + // Destroys previous scheduler. scheduler_.reset(new ResourceScheduler(enabled)); scheduler()->SetResourceSchedulerParamsManagerForTests( @@ -292,11 +290,6 @@ request->ChangePriority(new_priority, intra_priority); } - void FireCoalescingTimer() { - EXPECT_TRUE(mock_timer_->IsRunning()); - mock_timer_->Fire(); - } - void RequestLimitOverrideConfigTestHelper(bool experiment_status) { InitializeThrottleDelayableExperiment(experiment_status, 0.0); @@ -440,7 +433,6 @@ base::MessageLoop message_loop_; std::unique_ptr<ResourceScheduler> scheduler_; - base::MockTimer* mock_timer_; net::HttpServerPropertiesImpl http_server_properties_; net::TestNetworkQualityEstimator network_quality_estimator_; net::TestURLRequestContext context_;
diff --git a/services/resource_coordinator/observers/ipc_volume_reporter_unittest.cc b/services/resource_coordinator/observers/ipc_volume_reporter_unittest.cc index de57a37..8d35567 100644 --- a/services/resource_coordinator/observers/ipc_volume_reporter_unittest.cc +++ b/services/resource_coordinator/observers/ipc_volume_reporter_unittest.cc
@@ -17,10 +17,10 @@ class TestIPCVolumeReporter : public IPCVolumeReporter { public: TestIPCVolumeReporter() - : IPCVolumeReporter(std::make_unique<base::MockTimer>(false, false)) {} + : IPCVolumeReporter(std::make_unique<base::MockOneShotTimer>()) {} - base::MockTimer* mock_timer() const { - return reinterpret_cast<base::MockTimer*>(timer()); + base::MockOneShotTimer* mock_timer() const { + return static_cast<base::MockOneShotTimer*>(timer()); } };
diff --git a/services/service_manager/sandbox/linux/sandbox_linux.h b/services/service_manager/sandbox/linux/sandbox_linux.h index e4d7a7a..dcf4eeee 100644 --- a/services/service_manager/sandbox/linux/sandbox_linux.h +++ b/services/service_manager/sandbox/linux/sandbox_linux.h
@@ -56,11 +56,11 @@ // This isn't the full list, values < 32 are reserved for methods called from // Skia, and values < 64 are reserved for libc_interceptor.cc. enum LinuxSandboxIPCMethods { - METHOD_GET_FALLBACK_FONT_FOR_CHAR = 64, + DEPRECATED_METHOD_GET_FALLBACK_FONT_FOR_CHAR = 64, DEPRECATED_METHOD_GET_CHILD_WITH_INODE, - METHOD_GET_STYLE_FOR_STRIKE, + DEPRECATED_METHOD_GET_STYLE_FOR_STRIKE, METHOD_MAKE_SHARED_MEMORY_SEGMENT, - METHOD_MATCH_WITH_FALLBACK, + DEPRECATED_METHOD_MATCH_WITH_FALLBACK, }; // These form a bitmask which describes the conditions of the Linux sandbox.
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json index f9b11e4..dc80b058 100644 --- a/testing/buildbot/chromium.clang.json +++ b/testing/buildbot/chromium.clang.json
@@ -49,6 +49,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -763,6 +769,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -12644,6 +12656,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -13354,6 +13372,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -14034,6 +14058,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -14724,6 +14754,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -15417,6 +15453,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -23612,6 +23654,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 4ffa1673..00e5f24 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -3894,6 +3894,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, {
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json index 8a61168..819b201c 100644 --- a/testing/buildbot/chromium.linux.json +++ b/testing/buildbot/chromium.linux.json
@@ -745,6 +745,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -1589,6 +1595,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -2382,6 +2394,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -3083,6 +3101,17 @@ } ] }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-16.04" + } + ] + }, "test": "blink_heap_unittests" }, {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index f36eea83..6046bf0 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -2641,6 +2641,16 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "args": [ + "--test-launcher-batch-limit=1", + "--test-launcher-print-test-stdio=always" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -3522,6 +3532,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -5459,6 +5475,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_heap_unittests" }, { @@ -6155,6 +6177,12 @@ "swarming": { "can_use_on_swarming_builders": true }, + "test": "blink_fuzzer_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "blink_platform_unittests" }, {
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index 234e28f..c3fdcdca 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -179,6 +179,10 @@ "label": "//third_party/blink/common:blink_common_unittests", "type": "console_test_launcher", }, + "blink_fuzzer_unittests": { + "label": "//third_party/blink/renderer/platform:blink_fuzzer_unittests", + "type": "console_test_launcher", + }, "blink_heap_unittests": { "label": "//third_party/blink/renderer/platform/heap:blink_heap_unittests", "type": "console_test_launcher",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 7bec85fc..99d8ee5 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -1840,6 +1840,7 @@ 'linux_specific_chromium_gtests': { # Linux only. # TODO(kbr): unclear why some of these aren't run more broadly. + 'blink_fuzzer_unittests': {}, 'filesystem_service_unittests': {}, 'leveldb_service_unittests': {}, 'traffic_annotation_auditor_unittests': {},
diff --git a/testing/buildbot/trybot_analyze_config.json b/testing/buildbot/trybot_analyze_config.json index 939cb0f..5a37c1d 100644 --- a/testing/buildbot/trybot_analyze_config.json +++ b/testing/buildbot/trybot_analyze_config.json
@@ -32,7 +32,8 @@ "testing/scripts/.*", "testing/test_env.py", "third_party/android_platform/development/scripts/.*", - "tools/clang/blink_gc_plugin/CMakeLists.txt" + "tools/clang/blink_gc_plugin/CMakeLists.txt", + "tools/luci-go/.*" ], "ignores": [ "infra/config/recipes.cfg",
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG index 6cf4f88..43a3ee2 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG +++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -323,7 +323,6 @@ crbug.com/591099 fast/block/basic/quirk-percent-height-table-cell.html [ Failure ] crbug.com/591099 fast/block/block-width-recalc-with-relative-height.html [ Failure ] crbug.com/591099 fast/block/float-avoids-padding-inline-ancestors.html [ Crash ] -crbug.com/810335 fast/block/float/003.html [ Failure ] crbug.com/591099 fast/block/float/floats-offset-image-strict-line-height.html [ Failure ] crbug.com/591099 fast/block/float/floats-offset-inline-block-strict-line-height.html [ Failure ] crbug.com/591099 fast/block/float/negative-margin-on-element-avoiding-floats-with-margin-on-parent.html [ Failure ] @@ -382,7 +381,6 @@ crbug.com/591099 fast/css-intrinsic-dimensions/fixed-height-stf-img-inline-child-percent-height.html [ Failure ] crbug.com/591099 fast/css-intrinsic-dimensions/height-positioned.html [ Failure ] crbug.com/807708 fast/css-intrinsic-dimensions/width-avoid-floats.html [ Failure ] -crbug.com/591099 fast/css/007.html [ Failure ] crbug.com/591099 fast/css/abs-pos-child-inside-rel-pos-inline-001.html [ Failure ] crbug.com/591099 fast/css/abs-pos-child-inside-rel-pos-inline-offset-001.html [ Failure ] crbug.com/591099 fast/css/absolute-inline-alignment-2.html [ Pass ] @@ -901,7 +899,6 @@ crbug.com/591099 printing/absolute-position-headers-and-footers.html [ Failure ] crbug.com/591099 printing/iframe-svg-in-object-print.html [ Failure ] crbug.com/591099 scrollbars/auto-scrollbar-fit-content.html [ Failure ] -crbug.com/591099 scrollbars/scrollbar-miss-mousemove-disabled.html [ Failure ] crbug.com/591099 shapedetection/detection-HTMLVideoElement.html [ Pass ] crbug.com/591099 storage/indexeddb/cursor-continue-validity.html [ Timeout ] crbug.com/591099 storage/indexeddb/index-cursor.html [ Pass Timeout ] @@ -932,7 +929,6 @@ crbug.com/591099 tables/mozilla/bugs/bug2973.html [ Failure ] crbug.com/591099 tables/mozilla/bugs/bug30692.html [ Failure ] crbug.com/591099 tables/mozilla/bugs/bug50695-2.html [ Failure ] -crbug.com/591099 tables/mozilla/bugs/bug53690-2.html [ Failure ] crbug.com/591099 tables/mozilla/bugs/bug55527.html [ Failure ] crbug.com/591099 tables/mozilla/bugs/bug57828-2.html [ Failure ] crbug.com/591099 tables/mozilla_expected_failures/bugs/bug3166-16.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index d4d977c..ffb838d3 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1998,7 +1998,7 @@ crbug.com/692560 external/wpt/html/semantics/document-metadata/styling/LinkStyle.html [ Failure Pass ] crbug.com/627706 external/wpt/html/semantics/embedded-content/the-img-element/invalid-src.html [ Skip ] -crbug.com/698135 external/wpt/editing/run/delete.html [ Crash Failure Timeout ] +crbug.com/860211 [ Mac ] external/wpt/editing/run/delete.html [ Failure ] crbug.com/813098 external/wpt/editing/run/removeformat.html [ Timeout Pass ] @@ -2751,7 +2751,6 @@ # These need to be updated but appear not to be related to that change. crbug.com/626703 http/tests/devtools/indexeddb/database-refresh-view.js [ Pass Failure ] crbug.com/626703 http/tests/devtools/extensions/extensions-sidebar.js [ Pass Failure ] -crbug.com/751952 external/wpt/editing/run/forwarddelete.html [ Failure Pass Timeout ] crbug.com/751952 fast/text/international/complex-text-rectangle.html [ Timeout Pass ] crbug.com/751952 [ Mac Win ] editing/selection/modify_extend/extend_by_character.html [ Failure Pass ] crbug.com/751952 http/tests/devtools/console/console-uncaught-promise.js [ Pass Failure ] @@ -2795,6 +2794,7 @@ crbug.com/849859 external/wpt/web-animations/timing-model/animations/pausing-an-animation.html [ Failure ] # ====== New tests from wpt-importer added here ====== +crbug.com/626703 external/wpt/console/console-timing-logging-manual.html [ Skip ] crbug.com/626703 external/wpt/css/css-scoping/host-specificity-002.html [ Failure ] crbug.com/626703 external/wpt/css/css-scoping/slotted-specificity.html [ Failure ] crbug.com/626703 external/wpt/css/css-text/white-space/break-spaces-001.html [ Failure ] @@ -4670,6 +4670,8 @@ # Sheriff 2018-06-11 crbug.com/850202 [ Linux ] http/tests/devtools/network/network-filters.js [ Pass Failure ] +crbug.com/851746 [ Linux ] fast/canvas/color-space/canvas-colorManaged-convertToBlob-roundtrip.html [ Pass Timeout ] +crbug.com/851746 [ Linux ] virtual/gpu/fast/canvas/color-space/canvas-colorManaged-convertToBlob-roundtrip.html [ Pass Timeout ] crbug.com/853360 [ Mac ] fast/css/input-search-padding.html [ Failure ] crbug.com/853360 [ Mac ] fast/forms/calendar-picker/calendar-picker-appearance-ar.html [ Failure ] @@ -4730,7 +4732,7 @@ crbug.com/857520 [ Mac Win ] external/wpt/accelerometer/Accelerometer-iframe-access.https.html [ Failure ] # Sheriff 2018-06-29 -crbug.com/859064 [ Linux Win10 Mac10.13 ] http/tests/devtools/console-resource-errors.js [ Failure ] +crbug.com/859064 http/tests/devtools/console-resource-errors.js [ Pass Failure ] crbug.com/859169 [ Win7 ] http/tests/devtools/layers/layer-scroll-rects-get.js [ Failure Pass ] crbug.com/859270 [ Mac10.12 ] external/wpt/gyroscope/Gyroscope-iframe-access.https.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json index 1bbad14..b2796c2 100644 --- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json +++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -187,6 +187,12 @@ {} ] ], + "console/console-timing-logging-manual.html": [ + [ + "/console/console-timing-logging-manual.html", + {} + ] + ], "css/CSS2/cascade/at-import-008.xht": [ [ "/css/CSS2/cascade/at-import-008.xht", @@ -104882,6 +104888,11 @@ {} ] ], + "cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.js": [ + [ + {} + ] + ], "cookie-store/serviceworker_cookieStore_subscriptions_mismatch.js": [ [ {} @@ -177198,6 +177209,12 @@ {} ] ], + "2dcontext/imagebitmap/createImageBitmap-bounds.html": [ + [ + "/2dcontext/imagebitmap/createImageBitmap-bounds.html", + {} + ] + ], "2dcontext/imagebitmap/createImageBitmap-drawImage.html": [ [ "/2dcontext/imagebitmap/createImageBitmap-drawImage.html", @@ -184440,6 +184457,24 @@ {} ] ], + "client-hints/accept_ch_lifetime_cross_origin_iframe.tentative.sub.https.html": [ + [ + "/client-hints/accept_ch_lifetime_cross_origin_iframe.tentative.sub.https.html", + {} + ] + ], + "client-hints/accept_ch_lifetime_same_origin_iframe.tentative.https.html": [ + [ + "/client-hints/accept_ch_lifetime_same_origin_iframe.tentative.https.html", + {} + ] + ], + "client-hints/accept_ch_lifetime_subresource.tentative.https.html": [ + [ + "/client-hints/accept_ch_lifetime_subresource.tentative.https.html", + {} + ] + ], "client-hints/accept_ch_malformed_header.https.html": [ [ "/client-hints/accept_ch_malformed_header.https.html", @@ -187192,6 +187227,12 @@ {} ] ], + "cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.tentative.https.html": [ + [ + "/cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.tentative.https.html", + {} + ] + ], "cookie-store/serviceworker_cookieStore_subscriptions_mismatch.tentative.https.html": [ [ "/cookie-store/serviceworker_cookieStore_subscriptions_mismatch.tentative.https.html", @@ -209976,6 +210017,12 @@ {} ] ], + "fetch/api/abort/destroyed-context.html": [ + [ + "/fetch/api/abort/destroyed-context.html", + {} + ] + ], "fetch/api/abort/general.any.js": [ [ "/fetch/api/abort/general.any.html", @@ -268299,6 +268346,10 @@ "5da74cfd37ef072aa5b50c9a5fb658754984216b", "support" ], + "2dcontext/imagebitmap/createImageBitmap-bounds.html": [ + "cf1d9de3474c61c5827094cead43313883bc2408", + "testharness" + ], "2dcontext/imagebitmap/createImageBitmap-drawImage.html": [ "adef50e6043c6ecb80bdc4a6b7f9d9a599a80656", "testharness" @@ -274468,7 +274519,7 @@ "support" ], "client-hints/accept_ch.tentative.https.html": [ - "342de3ae30d962249bc0c282aac5b6e4192badfd", + "64f3e60bbc14c208a2fbe8193c694429fe394b31", "testharness" ], "client-hints/accept_ch.tentative.sub.https.html": [ @@ -274480,7 +274531,19 @@ "support" ], "client-hints/accept_ch_lifetime.tentative.https.html": [ - "58a6a6dafb4a6a3e8daa8742a1da137399353370", + "0e1756a8ea48e53011ada28ed0fa6070a0713691", + "testharness" + ], + "client-hints/accept_ch_lifetime_cross_origin_iframe.tentative.sub.https.html": [ + "0006c59d1d9c305e94dfe60511c00539fb1f0887", + "testharness" + ], + "client-hints/accept_ch_lifetime_same_origin_iframe.tentative.https.html": [ + "020fc61613776711177970917ddaacf3ed641a82", + "testharness" + ], + "client-hints/accept_ch_lifetime_subresource.tentative.https.html": [ + "b6235b48fa3132c5f5e0cb6187f25134abdee57e", "testharness" ], "client-hints/accept_ch_malformed_header.https.html": [ @@ -274524,15 +274587,15 @@ "support" ], "client-hints/resources/accept_ch_lifetime.html.headers": [ - "6e96c4ec282ce390e9becb2c50944031fb36f4a0", + "22488fcaec4a0a0f227b972ccc8c911e006f3286", "support" ], "client-hints/resources/do_not_expect_client_hints_headers.html": [ - "f48190cb980ceef479f6858cd6cc121d136c52a3", + "0c036c2a388518922878380659da0f7e13d20543", "support" ], "client-hints/resources/expect_client_hints_headers.html": [ - "88247405d94960fc58d49b0969f5c9357ee84fad", + "1fcc20d9ff0da7712aeeaa10db51fffb2fd97c50", "support" ], "clipboard-apis/META.yml": [ @@ -274951,6 +275014,10 @@ "ee18f8a672534b478ca15990026638a73588fcf4", "testharness" ], + "console/console-timing-logging-manual.html": [ + "8b2d2e8d6675efacf99f49fec95c8be52f38407e", + "manual" + ], "content-security-policy/META.yml": [ "5819f0331b11875efb46ad15e02d28f59770cc9d", "support" @@ -277644,7 +277711,7 @@ "testharness" ], "cookie-store/serviceworker_cookieStore_subscriptions.js": [ - "7e140635e7587de1c606a0faf4a0bf8d8eb8c41e", + "7197cdc912d381a258a7b80daaa0d12e20a66fe5", "support" ], "cookie-store/serviceworker_cookieStore_subscriptions.tentative.https.html": [ @@ -277652,7 +277719,7 @@ "testharness" ], "cookie-store/serviceworker_cookieStore_subscriptions_basic.js": [ - "9b477518ee4c8cdc103d3cab11cca371b7b2f72d", + "503a93f435cf7784ff2b98dc7c3f5d1a1ba1fb14", "support" ], "cookie-store/serviceworker_cookieStore_subscriptions_basic.tentative.https.html": [ @@ -277660,15 +277727,23 @@ "testharness" ], "cookie-store/serviceworker_cookieStore_subscriptions_empty.js": [ - "fd8cf9d3dab06824cf0497c44a388c0cea7d02d6", + "a90b1e59e0708838ad84618c4acfb6291ec82bd9", "support" ], "cookie-store/serviceworker_cookieStore_subscriptions_empty.tentative.https.html": [ "3f8ec6177bc54738213fab8a1f8947d58714456d", "testharness" ], + "cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.js": [ + "4cc19c733cea09c1143dba9ef017ccb59aee8ddc", + "support" + ], + "cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.tentative.https.html": [ + "e0d44d998137cea66fc66d88a12b485386962b34", + "testharness" + ], "cookie-store/serviceworker_cookieStore_subscriptions_mismatch.js": [ - "fa57006fdb09070feb6c7a83553d300d725761b6", + "464447630cdf01b80e4469fb79756c9898653cc4", "support" ], "cookie-store/serviceworker_cookieStore_subscriptions_mismatch.tentative.https.html": [ @@ -352743,6 +352818,10 @@ "9465007a35059e9d72d4ab1dd8bff0d44f47c3d6", "testharness" ], + "fetch/api/abort/destroyed-context.html": [ + "5cc67576c6b355dcb16b7934f075506b1ebc970b", + "testharness" + ], "fetch/api/abort/general-serviceworker.https-expected.txt": [ "4881cf0f195fffbbe3ab01348d5ef09eea07b65a", "support" @@ -353592,7 +353671,7 @@ "testharness" ], "fetch/api/response/response-cancel-stream.html": [ - "453323c2a0f9a908966df3cb53211c0b0a1fa19d", + "ad839d3e6234d16aa61338b732f765572cf8291f", "testharness" ], "fetch/api/response/response-clone-expected.txt": [ @@ -375752,7 +375831,7 @@ "support" ], "interfaces/cookie-store.idl": [ - "468487d9aee05767b52a112275155fa883720aa0", + "fe873252f8a58c66f736fbabd90d6d37a15df139", "support" ], "interfaces/cors-rfc1918.idl": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/2dcontext/imagebitmap/createImageBitmap-bounds.html b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/imagebitmap/createImageBitmap-bounds.html new file mode 100644 index 0000000..544bd77 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/2dcontext/imagebitmap/createImageBitmap-bounds.html
@@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<title>createImageBitmap: clipping to the bitmap</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/canvas-tests.js"></script> +<script> +promise_test(function(t) { + return new Promise(function(resolve, reject) { + const image = new Image(); + image.onload = function() { resolve(image); }; + image.onerror = function() { reject(); }; + image.src = "/images/green-16x16.png"; + }).then(function(image) { + return createImageBitmap(image, 8, 8, 16, 16); + }).then(function(imageBitmap) { + const color = 204; + + const canvas = document.createElement("canvas"); + canvas.width = 16; + canvas.height = 16; + + // debug + document.body.appendChild(canvas); + canvas.setAttribute("style", "width: 100px; height: 100px;"); + + const ctx = canvas.getContext("2d"); + ctx.fillStyle = `rgb(${color}, ${color}, ${color})`; + ctx.fillRect(0, 0, 20, 20); + ctx.drawImage(imageBitmap, 0, 0); + + const expected = [ + [ 4, 4, 0,255,0,255], + [12, 4, color,color,color,255], + [ 4, 12, color,color,color,255], + [12, 12, color,color,color,255], + ]; + for (let [x, y, r, g, b, a] of expected) { + _assertPixel(canvas, x,y, r,g,b,a, `${x},${y}`, `${r},${g},${b},${a}`); + } + + }); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/console/console-timing-logging-manual.html b/third_party/WebKit/LayoutTests/external/wpt/console/console-timing-logging-manual.html new file mode 100644 index 0000000..3b9e5ce --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/console/console-timing-logging-manual.html
@@ -0,0 +1,70 @@ +<!DOCTYPE html> +<html> +<head> +<title>Console Timing Methods - Logging Manual Test</title> +<meta name="author" title="Dominic Farolino" href="mailto:domfarolino@gmail.com"> +<meta name="assert" content="Console timing methods"> +<link rel="help" href="https://console.spec.whatwg.org/#timing"> +</head> +<body> +<p>Open the console inside the developer tools. It should contain entries whose contents are:</p> +<p><code>default: <some time></code></p> +<p><code>default: <some time></code></p> + +<p><code>default: <some time></code></p> +<p><code>default: <some time> extra data</code></p> +<p><code>default: <some time></code></p> + +<p><code>default: <some time></code></p> +<p><code>default: <some time> extra data</code></p> +<p><code>default: <some time></code></p> + +<p><code>default: <some time></code></p> +<p><code>default: <some time> extra data</code></p> +<p><code>default: <some time></code></p> + +<p><code>custom toString(): <some time></code></p> +<p><code>custom toString(): <some time> extra data</code></p> +<p><code>custom toString(): <some time></code></p> + +<p><code>a label: <some time></code></p> +<p><code>a label: <some time> extra data</code></p> +<p><code>a label: <some time></code></p> + +<p style="color:grey;">[some warning message indicating that a timer for label "b" does not exist]</p> + +<script> +console.time(); +console.timeLog(); +console.timeEnd(); + +console.time(undefined); +console.timeLog(undefined); +console.timeLog(undefined, "extra data"); +console.timeEnd(undefined); + +console.time("default"); +console.timeLog("default"); +console.timeLog("default", "extra data"); +console.timeEnd("default"); + +console.time({toString() {return "default"}}); +console.timeLog({toString() {return "default"}}); +console.timeLog({toString() {return "default"}}, "extra data"); +console.timeEnd({toString() {return "default"}}); + +console.time({toString() {return "custom toString()"}}); +console.timeLog({toString() {return "custom toString()"}}); +console.timeLog({toString() {return "custom toString()"}}, "extra data"); +console.timeEnd({toString() {return "custom toString()"}}); + +console.time("a label"); +console.timeLog("a label"); +console.timeLog("a label", "extra data"); +console.timeEnd("a label"); + +console.timeLog("b"); // should produce a warning +console.timeEnd("b"); // should produce a warning +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-color-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-color-expected.txt index ae88111..5ae75b67 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-color-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-color-expected.txt
@@ -1,33 +1,43 @@ This is a testharness.js-based test. PASS Test that logical border-*-color properties are supported. PASS Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +PASS Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. FAIL Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', border-bottom-color expected "rgb(1, 1, 1)" but got "rgb(4, 4, 4)" +FAIL Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', border-bottom-color expected "rgb(1, 1, 1)" but got "rgb(0, 0, 0)" FAIL Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'border-inline-start-color' last on single declaration, 'writing-mode: sideways-rl; direction: rtl; ', border-block-end-color expected "rgb(1, 1, 1)" but got "rgb(4, 4, 4)" FAIL Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'border-inline-start-color', two declarations, 'writing-mode: sideways-rl; direction: rtl; ', border-block-end-color expected "rgb(1, 1, 1)" but got "rgb(4, 4, 4)" PASS Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +PASS Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. FAIL Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', border-top-color expected "rgb(1, 1, 1)" but got "rgb(3, 3, 3)" +FAIL Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', border-top-color expected "rgb(1, 1, 1)" but got "rgb(0, 0, 0)" FAIL Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'border-inline-start-color' last on single declaration, 'writing-mode: sideways-rl; direction: ltr; ', border-left-color expected "rgb(1, 1, 1)" but got "rgb(5, 5, 5)" FAIL Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'border-inline-start-color', two declarations, 'writing-mode: sideways-rl; direction: ltr; ', border-left-color expected "rgb(1, 1, 1)" but got "rgb(5, 5, 5)" PASS Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +PASS Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. FAIL Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', border-bottom-color expected "rgb(1, 1, 1)" but got "rgb(4, 4, 4)" +FAIL Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', border-bottom-color expected "rgb(1, 1, 1)" but got "rgb(0, 0, 0)" FAIL Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'border-inline-start-color' last on single declaration, 'writing-mode: sideways-lr; direction: ltr; ', border-left-color expected "rgb(1, 1, 1)" but got "rgb(5, 5, 5)" FAIL Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'border-inline-start-color', two declarations, 'writing-mode: sideways-lr; direction: ltr; ', border-left-color expected "rgb(1, 1, 1)" but got "rgb(5, 5, 5)" PASS Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +PASS Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. FAIL Test that logical border-*-color properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', border-top-color expected "rgb(1, 1, 1)" but got "rgb(3, 3, 3)" +FAIL Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', border-top-color expected "rgb(1, 1, 1)" but got "rgb(0, 0, 0)" FAIL Test that border-*-color properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'border-inline-start-color' last on single declaration, 'writing-mode: sideways-lr; direction: rtl; ', border-block-start-color expected "rgb(1, 1, 1)" but got "rgb(3, 3, 3)" FAIL Test that border-*-color properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'border-inline-start-color', two declarations, 'writing-mode: sideways-lr; direction: rtl; ', border-block-start-color expected "rgb(1, 1, 1)" but got "rgb(3, 3, 3)" Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-color.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-color.html index 9cc7e244..c279ef4 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-color.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-color.html
@@ -3,7 +3,7 @@ <title>CSS Logical Properties: Flow-Relative Border Colors</title> <link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" /> <link rel="help" href="https://drafts.csswg.org/css-logical/#border-color"> -<meta name="assert" content="This test checks the interaction of the flow-relative border-*-color longhand properties with the physical ones in different writing modes." /> +<meta name="assert" content="This test checks the interaction of the flow-relative border-*-color properties with the physical ones in different writing modes." /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-shorthands-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-shorthands-expected.txt index be33641..e0dc976f 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-shorthands-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-shorthands-expected.txt
@@ -1,33 +1,43 @@ This is a testharness.js-based test. PASS Test that logical border-* properties are supported. PASS Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +PASS Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. FAIL Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', border-bottom expected "1px solid rgb(1, 1, 1)" but got "4px double rgb(4, 4, 4)" +FAIL Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', border-bottom expected "1px solid rgb(1, 1, 1)" but got "0px none rgb(0, 0, 0)" FAIL Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'border-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: rtl; ', border-block-end expected "1px solid rgb(1, 1, 1)" but got "4px double rgb(4, 4, 4)" FAIL Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'border-inline-start', two declarations, 'writing-mode: sideways-rl; direction: rtl; ', border-block-end expected "1px solid rgb(1, 1, 1)" but got "4px double rgb(4, 4, 4)" PASS Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +PASS Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. FAIL Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', border-top expected "1px solid rgb(1, 1, 1)" but got "3px dotted rgb(3, 3, 3)" +FAIL Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', border-top expected "1px solid rgb(1, 1, 1)" but got "0px none rgb(0, 0, 0)" FAIL Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'border-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: ltr; ', border-left expected "1px solid rgb(1, 1, 1)" but got "5px groove rgb(5, 5, 5)" FAIL Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'border-inline-start', two declarations, 'writing-mode: sideways-rl; direction: ltr; ', border-left expected "1px solid rgb(1, 1, 1)" but got "5px groove rgb(5, 5, 5)" PASS Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +PASS Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. FAIL Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', border-bottom expected "1px solid rgb(1, 1, 1)" but got "4px double rgb(4, 4, 4)" +FAIL Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', border-bottom expected "1px solid rgb(1, 1, 1)" but got "0px none rgb(0, 0, 0)" FAIL Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'border-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: ltr; ', border-left expected "1px solid rgb(1, 1, 1)" but got "5px groove rgb(5, 5, 5)" FAIL Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'border-inline-start', two declarations, 'writing-mode: sideways-lr; direction: ltr; ', border-left expected "1px solid rgb(1, 1, 1)" but got "5px groove rgb(5, 5, 5)" PASS Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +PASS Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. FAIL Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', border-top expected "1px solid rgb(1, 1, 1)" but got "3px dotted rgb(3, 3, 3)" +FAIL Test that border-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', border-top expected "1px solid rgb(1, 1, 1)" but got "0px none rgb(0, 0, 0)" FAIL Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'border-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: rtl; ', border-block-start expected "1px solid rgb(1, 1, 1)" but got "3px dotted rgb(3, 3, 3)" FAIL Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'border-inline-start', two declarations, 'writing-mode: sideways-lr; direction: rtl; ', border-block-start expected "1px solid rgb(1, 1, 1)" but got "3px dotted rgb(3, 3, 3)" Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-style-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-style-expected.txt index a509119..7f039fb 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-style-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-style-expected.txt
@@ -1,33 +1,43 @@ This is a testharness.js-based test. PASS Test that logical border-*-style properties are supported. PASS Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +PASS Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. FAIL Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', border-bottom-style expected "solid" but got "double" +FAIL Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', border-bottom-style expected "solid" but got "none" FAIL Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'border-inline-start-style' last on single declaration, 'writing-mode: sideways-rl; direction: rtl; ', border-block-end-style expected "solid" but got "double" FAIL Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'border-inline-start-style', two declarations, 'writing-mode: sideways-rl; direction: rtl; ', border-block-end-style expected "solid" but got "double" PASS Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +PASS Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. FAIL Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', border-top-style expected "solid" but got "dotted" +FAIL Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', border-top-style expected "solid" but got "none" FAIL Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'border-inline-start-style' last on single declaration, 'writing-mode: sideways-rl; direction: ltr; ', border-left-style expected "solid" but got "groove" FAIL Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'border-inline-start-style', two declarations, 'writing-mode: sideways-rl; direction: ltr; ', border-left-style expected "solid" but got "groove" PASS Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +PASS Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. FAIL Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', border-bottom-style expected "solid" but got "double" +FAIL Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', border-bottom-style expected "solid" but got "none" FAIL Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'border-inline-start-style' last on single declaration, 'writing-mode: sideways-lr; direction: ltr; ', border-left-style expected "solid" but got "groove" FAIL Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'border-inline-start-style', two declarations, 'writing-mode: sideways-lr; direction: ltr; ', border-left-style expected "solid" but got "groove" PASS Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +PASS Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. FAIL Test that logical border-*-style properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', border-top-style expected "solid" but got "dotted" +FAIL Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', border-top-style expected "solid" but got "none" FAIL Test that border-*-style properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'border-inline-start-style' last on single declaration, 'writing-mode: sideways-lr; direction: rtl; ', border-block-start-style expected "solid" but got "dotted" FAIL Test that border-*-style properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'border-inline-start-style', two declarations, 'writing-mode: sideways-lr; direction: rtl; ', border-block-start-style expected "solid" but got "dotted" Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-style.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-style.html index 6a32f4e2..4954bac3 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-style.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-style.html
@@ -3,7 +3,7 @@ <title>CSS Logical Properties: Flow-Relative Border Styles</title> <link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" /> <link rel="help" href="https://drafts.csswg.org/css-logical/#border-style"> -<meta name="assert" content="This test checks the interaction of the flow-relative border-*-style longhand properties with the physical ones in different writing modes." /> +<meta name="assert" content="This test checks the interaction of the flow-relative border-*-style properties with the physical ones in different writing modes." /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-width-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-width-expected.txt index a2f56397..3f45255 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-width-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-width-expected.txt
@@ -1,33 +1,43 @@ This is a testharness.js-based test. PASS Test that logical border-*-width properties are supported. PASS Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +PASS Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. FAIL Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', border-bottom-width expected "1px" but got "4px" +FAIL Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', border-bottom-width expected "1px" but got "3px" FAIL Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'border-inline-start-width' last on single declaration, 'writing-mode: sideways-rl; direction: rtl; ', border-block-end-width expected "1px" but got "4px" FAIL Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'border-inline-start-width', two declarations, 'writing-mode: sideways-rl; direction: rtl; ', border-block-end-width expected "1px" but got "4px" PASS Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +PASS Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. FAIL Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', border-top-width expected "1px" but got "3px" +FAIL Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', border-top-width expected "1px" but got "3px" FAIL Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'border-inline-start-width' last on single declaration, 'writing-mode: sideways-rl; direction: ltr; ', border-left-width expected "1px" but got "5px" FAIL Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'border-inline-start-width', two declarations, 'writing-mode: sideways-rl; direction: ltr; ', border-left-width expected "1px" but got "5px" PASS Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +PASS Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. FAIL Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', border-bottom-width expected "1px" but got "4px" +FAIL Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', border-bottom-width expected "1px" but got "3px" FAIL Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'border-inline-start-width' last on single declaration, 'writing-mode: sideways-lr; direction: ltr; ', border-left-width expected "1px" but got "5px" FAIL Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'border-inline-start-width', two declarations, 'writing-mode: sideways-lr; direction: ltr; ', border-left-width expected "1px" but got "5px" PASS Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +PASS Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. FAIL Test that logical border-*-width properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', border-top-width expected "1px" but got "3px" +FAIL Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', border-top-width expected "1px" but got "3px" FAIL Test that border-*-width properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'border-inline-start-width' last on single declaration, 'writing-mode: sideways-lr; direction: rtl; ', border-block-start-width expected "1px" but got "3px" FAIL Test that border-*-width properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'border-inline-start-width', two declarations, 'writing-mode: sideways-lr; direction: rtl; ', border-block-start-width expected "1px" but got "3px" Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-width.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-width.html index 40d6634..7396ce7 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-width.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-border-width.html
@@ -3,7 +3,7 @@ <title>CSS Logical Properties: Flow-Relative Border Widths</title> <link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" /> <link rel="help" href="https://drafts.csswg.org/css-logical/#border-width"> -<meta name="assert" content="This test checks the interaction of the flow-relative border-*-width longhand properties with the physical ones in different writing modes." /> +<meta name="assert" content="This test checks the interaction of the flow-relative border-*-width properties with the physical ones in different writing modes." /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset-expected.txt index 38f431b6..62e3fc56 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset-expected.txt
@@ -1,33 +1,43 @@ This is a testharness.js-based test. FAIL Test that logical inset-* properties are supported. assert_equals: logical properties in inline style, inset-inline-start expected "1px" but got "" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: horizontal-tb; direction: ltr; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: horizontal-tb; direction: ltr; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: horizontal-tb; direction: ltr; ', left expected "5px" but got "1px" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: horizontal-tb; direction: ltr; ', left expected "5px" but got "1px" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: horizontal-tb; direction: rtl; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: horizontal-tb; direction: rtl; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: horizontal-tb; direction: rtl; ', inset-inline-end expected "1px" but got "" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: horizontal-tb; direction: rtl; ', inset-inline-end expected "1px" but got "" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: vertical-rl; direction: rtl; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: vertical-rl; direction: rtl; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: vertical-rl; direction: rtl; ', inset-block-end expected "1px" but got "" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: vertical-rl; direction: rtl; ', inset-block-end expected "1px" but got "" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: rtl; ', inset-block-end expected "1px" but got "" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-rl; direction: rtl; ', inset-block-end expected "1px" but got "" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: vertical-rl; direction: ltr; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: vertical-rl; direction: ltr; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: vertical-rl; direction: ltr; ', inset-block-end expected "1px" but got "" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: vertical-rl; direction: ltr; ', inset-block-end expected "1px" but got "" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: ltr; ', inset-block-end expected "1px" but got "" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-rl; direction: ltr; ', inset-block-end expected "1px" but got "" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: vertical-lr; direction: rtl; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: vertical-lr; direction: rtl; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: vertical-lr; direction: rtl; ', inset-block-start expected "1px" but got "" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: vertical-lr; direction: rtl; ', inset-block-start expected "1px" but got "" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: ltr; ', inset-block-start expected "1px" but got "" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-lr; direction: ltr; ', inset-block-start expected "1px" but got "" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: vertical-lr; direction: ltr; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: vertical-lr; direction: ltr; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: vertical-lr; direction: ltr; ', inset-block-start expected "1px" but got "" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: vertical-lr; direction: ltr; ', inset-block-start expected "1px" but got "" FAIL Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', inset-inline-start expected "1px" but got "" +FAIL Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', inset-inline-start expected "1px" but got "" FAIL Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'inset-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: rtl; ', inset-block-start expected "1px" but got "" FAIL Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'inset-inline-start', two declarations, 'writing-mode: sideways-lr; direction: rtl; ', inset-block-start expected "1px" but got "" Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset.html index 0741c79..005a269 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-inset.html
@@ -3,7 +3,7 @@ <title>CSS Logical Properties: Flow-Relative Offsets</title> <link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" /> <link rel="help" href="https://drafts.csswg.org/css-logical/#inset-properties"> -<meta name="assert" content="This test checks the interaction of the flow-relative inset-* longhand properties with the physical ones in different writing modes." /> +<meta name="assert" content="This test checks the interaction of the flow-relative inset-* properties with the physical ones in different writing modes." /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-margin-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-margin-expected.txt index f874011..080766a6 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-margin-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-margin-expected.txt
@@ -1,33 +1,43 @@ This is a testharness.js-based test. PASS Test that logical margin-* properties are supported. PASS Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +PASS Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. FAIL Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', margin-bottom expected "1px" but got "4px" +FAIL Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', margin-bottom expected "1px" but got "0px" FAIL Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'margin-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: rtl; ', margin-block-end expected "1px" but got "4px" FAIL Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'margin-inline-start', two declarations, 'writing-mode: sideways-rl; direction: rtl; ', margin-block-end expected "1px" but got "4px" PASS Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +PASS Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. FAIL Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', margin-top expected "1px" but got "3px" +FAIL Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', margin-top expected "1px" but got "0px" FAIL Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'margin-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: ltr; ', margin-left expected "1px" but got "5px" FAIL Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'margin-inline-start', two declarations, 'writing-mode: sideways-rl; direction: ltr; ', margin-left expected "1px" but got "5px" PASS Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +PASS Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. FAIL Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', margin-bottom expected "1px" but got "4px" +FAIL Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', margin-bottom expected "1px" but got "0px" FAIL Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'margin-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: ltr; ', margin-left expected "1px" but got "5px" FAIL Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'margin-inline-start', two declarations, 'writing-mode: sideways-lr; direction: ltr; ', margin-left expected "1px" but got "5px" PASS Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +PASS Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. FAIL Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', margin-top expected "1px" but got "3px" +FAIL Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', margin-top expected "1px" but got "0px" FAIL Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'margin-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: rtl; ', margin-block-start expected "1px" but got "3px" FAIL Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'margin-inline-start', two declarations, 'writing-mode: sideways-lr; direction: rtl; ', margin-block-start expected "1px" but got "3px" Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-margin.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-margin.html index 3ad6c8b..5fcd5c38 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-margin.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-margin.html
@@ -3,7 +3,7 @@ <title>CSS Logical Properties: Flow-Relative Margins</title> <link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" /> <link rel="help" href="https://drafts.csswg.org/css-logical/#margin-properties"> -<meta name="assert" content="This test checks the interaction of the flow-relative margin-* longhand properties with the physical ones in different writing modes." /> +<meta name="assert" content="This test checks the interaction of the flow-relative margin-* properties with the physical ones in different writing modes." /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-padding-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-padding-expected.txt index 1ef801d..7c041b8 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-padding-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-padding-expected.txt
@@ -1,33 +1,43 @@ This is a testharness.js-based test. PASS Test that logical padding-* properties are supported. PASS Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +PASS Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. PASS Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +PASS Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. PASS Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +PASS Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. PASS Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. FAIL Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', padding-bottom expected "1px" but got "4px" +FAIL Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: rtl; ', padding-bottom expected "1px" but got "0px" FAIL Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'padding-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: rtl; ', padding-block-end expected "1px" but got "4px" FAIL Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. assert_equals: 'padding-inline-start', two declarations, 'writing-mode: sideways-rl; direction: rtl; ', padding-block-end expected "1px" but got "4px" PASS Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +PASS Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. PASS Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. FAIL Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', padding-top expected "1px" but got "3px" +FAIL Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-rl; direction: ltr; ', padding-top expected "1px" but got "0px" FAIL Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'padding-inline-start' last on single declaration, 'writing-mode: sideways-rl; direction: ltr; ', padding-left expected "1px" but got "5px" FAIL Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. assert_equals: 'padding-inline-start', two declarations, 'writing-mode: sideways-rl; direction: ltr; ', padding-left expected "1px" but got "5px" PASS Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +PASS Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. PASS Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. FAIL Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', padding-bottom expected "1px" but got "4px" +FAIL Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: ltr; ', padding-bottom expected "1px" but got "0px" FAIL Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'padding-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: ltr; ', padding-left expected "1px" but got "5px" FAIL Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. assert_equals: 'padding-inline-start', two declarations, 'writing-mode: sideways-lr; direction: ltr; ', padding-left expected "1px" but got "5px" PASS Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +PASS Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. PASS Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. FAIL Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: logical properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', padding-top expected "1px" but got "3px" +FAIL Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: shorthand properties on one declaration, writing mode properties on another, 'writing-mode: sideways-lr; direction: rtl; ', padding-top expected "1px" but got "0px" FAIL Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'padding-inline-start' last on single declaration, 'writing-mode: sideways-lr; direction: rtl; ', padding-block-start expected "1px" but got "3px" FAIL Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. assert_equals: 'padding-inline-start', two declarations, 'writing-mode: sideways-lr; direction: rtl; ', padding-block-start expected "1px" but got "3px" Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-padding.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-padding.html index 9d99721..b3f6b6f 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-padding.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/logical-box-padding.html
@@ -3,7 +3,7 @@ <title>CSS Logical Properties: Flow-Relative Padding</title> <link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" /> <link rel="help" href="https://drafts.csswg.org/css-logical/#padding-properties"> -<meta name="assert" content="This test checks the interaction of the flow-relative padding-* longhand properties with the physical ones in different writing modes." /> +<meta name="assert" content="This test checks the interaction of the flow-relative padding-* properties with the physical ones in different writing modes." /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/resources/test-box-properties.js b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/resources/test-box-properties.js index da47747..5170769 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/resources/test-box-properties.js +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-logical/resources/test-box-properties.js
@@ -90,6 +90,9 @@ * }, logical: { * inlineStart: "margin-inline-start", inlineEnd: "margin-inline-end", * blockStart: "margin-block-start", blockEnd: "margin-block-end", + * }, shorthands: { + * inline: ["margin-inline-start", "margin-inline-end"], + * block: ["margin-block-start", "margin-block-end"], * }, type: ["length"], prerequisites: "...", property: "'margin-*'" } * * @param {string} property @@ -105,9 +108,18 @@ exports.createBoxPropertyGroup = function(property, descriptor) { const logical = {}; const physical = {}; - for (const logicalSide of ["inline-start", "inline-end", "block-start", "block-end"]) { - const camelCase = logicalSide.replace(/-(.)/g, (match, $1) => $1.toUpperCase()); - logical[camelCase] = property.replace("*", logicalSide); + const shorthands = {}; + for (const axis of ["inline", "block"]) { + const shorthand = property.replace("*", axis); + const longhands = []; + shorthands[shorthand] = longhands; + for (const side of ["start", "end"]) { + const logicalSide = axis + "-" + side; + const camelCase = logicalSide.replace(/-(.)/g, (match, $1) => $1.toUpperCase()); + const longhand = property.replace("*", logicalSide); + logical[camelCase] = longhand; + longhands.push(longhand); + } } const isInset = property === "inset-*"; let prerequisites = ""; @@ -116,7 +128,7 @@ prerequisites += makeDeclaration(descriptor.prerequisites, physicalSide); } const type = [].concat(descriptor.type); - return {name, logical, physical, type, prerequisites, property}; + return {name, logical, physical, shorthands, type, prerequisites, property}; }; /** @@ -153,6 +165,7 @@ }); const logicals = Object.values(group.logical); const physicals = Object.values(group.physical); + const shorthands = group.shorthands ? Object.entries(group.shorthands) : null; test(function() { const expected = []; @@ -196,6 +209,33 @@ }, `Test that logical ${group.property} properties share computed values ` + `with their physical associates, with '${writingModeDecl}'.`); + + // Test logical shorthand properties. + if (shorthands) { + test(function() { + for (const [shorthand, longhands] of shorthands) { + let shorthandValues; + if (group.type.length > 1) { + shorthandValues = [values[0]]; + } else { + shorthandValues = testValues[group.type].slice(0, longhands.length); + } + const decl = group.prerequisites + `${shorthand}: ${shorthandValues.join(" ")}; `; + const expected = []; + for (let [i, longhand] of longhands.entries()) { + const longhandValue = shorthandValues[group.type.length > 1 ? 0 : i]; + expected.push([longhand, longhandValue]); + expected.push([associated[longhand], longhandValue]); + } + testComputedValues("shorthand properties on one declaration, writing " + + `mode properties on another, '${writingModeDecl}'`, + `.test { ${writingModeDecl} } .test { ${decl} }`, + expected); + } + }, `Test that ${group.property} shorthands set the computed value of both ` + + `logical and physical longhands, with '${writingModeDecl}'.`); + } + // Test that logical and physical properties are cascaded together, // honoring their relative order on a single declaration // (a) with a single logical property after the physical ones
diff --git a/third_party/WebKit/LayoutTests/external/wpt/editing/run/delete-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/editing/run/delete-expected.txt index 28ba42d..f1f3dfc 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/editing/run/delete-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/editing/run/delete-expected.txt
@@ -1 +1,6746 @@ -#CRASHED - renderer +This is a testharness.js-based test. +Found 6742 tests; 6568 PASS, 174 FAIL, 0 TIMEOUT, 0 NOTRUN. +PASS [["delete",""]] "foo[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo[]bar" compare innerHTML +PASS [["delete",""]] "foo[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foo[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foo[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<span>foo</span>{}<span>bar</span>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span>foo</span>{}<span>bar</span>" checks for modifications to non-editable content +PASS [["delete",""]] "<span>foo</span>{}<span>bar</span>" compare innerHTML +PASS [["delete",""]] "<span>foo</span>{}<span>bar</span>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span>foo</span>{}<span>bar</span>" queryCommandState("delete") before +PASS [["delete",""]] "<span>foo</span>{}<span>bar</span>" queryCommandValue("delete") before +PASS [["delete",""]] "<span>foo</span>{}<span>bar</span>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span>foo</span>{}<span>bar</span>" queryCommandState("delete") after +PASS [["delete",""]] "<span>foo</span>{}<span>bar</span>" queryCommandValue("delete") after +PASS [["delete",""]] "<span>foo[</span><span>]bar</span>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span>foo[</span><span>]bar</span>" checks for modifications to non-editable content +FAIL [["delete",""]] "<span>foo[</span><span>]bar</span>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<span>foo</span><span>bar</span>" but got "<span>fo</span><span>bar</span>" +PASS [["delete",""]] "<span>foo[</span><span>]bar</span>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span>foo[</span><span>]bar</span>" queryCommandState("delete") before +PASS [["delete",""]] "<span>foo[</span><span>]bar</span>" queryCommandValue("delete") before +PASS [["delete",""]] "<span>foo[</span><span>]bar</span>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span>foo[</span><span>]bar</span>" queryCommandState("delete") after +PASS [["delete",""]] "<span>foo[</span><span>]bar</span>" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandIndeterm("stylewithcss") before +FAIL [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandState("stylewithcss") before assert_equals: Wrong result returned expected false but got true +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo<span style=display:none>bar</span>[]baz" queryCommandValue("delete") after +PASS [["delete",""]] "foo<script>bar</script>[]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<script>bar</script>[]baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<script>bar</script>[]baz" compare innerHTML +PASS [["delete",""]] "foo<script>bar</script>[]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<script>bar</script>[]baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<script>bar</script>[]baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<script>bar</script>[]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<script>bar</script>[]baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<script>bar</script>[]baz" queryCommandValue("delete") after +PASS [["delete",""]] "foö[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foö[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foö[]bar" compare innerHTML +PASS [["delete",""]] "foö[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foö[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foö[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foö[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foö[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foö[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foö[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foö[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foö[]bar" compare innerHTML +PASS [["delete",""]] "foö[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foö[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foö[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foö[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foö[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foö[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foö̧[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foö̧[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foö̧[]bar" compare innerHTML +PASS [["delete",""]] "foö̧[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foö̧[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foö̧[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foö̧[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foö̧[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foö̧[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "ö[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "ö[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "ö[]bar" compare innerHTML +PASS [["delete",""]] "ö[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "ö[]bar" queryCommandState("delete") before +PASS [["delete",""]] "ö[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "ö[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "ö[]bar" queryCommandState("delete") after +PASS [["delete",""]] "ö[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "ö[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "ö[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "ö[]bar" compare innerHTML +PASS [["delete",""]] "ö[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "ö[]bar" queryCommandState("delete") before +PASS [["delete",""]] "ö[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "ö[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "ö[]bar" queryCommandState("delete") after +PASS [["delete",""]] "ö[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "ö̧[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "ö̧[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "ö̧[]bar" compare innerHTML +PASS [["delete",""]] "ö̧[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "ö̧[]bar" queryCommandState("delete") before +PASS [["delete",""]] "ö̧[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "ö̧[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "ö̧[]bar" queryCommandState("delete") after +PASS [["delete",""]] "ö̧[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "שָׁ[]לוֹם": execCommand("delete", false, "") return value +PASS [["delete",""]] "שָׁ[]לוֹם" checks for modifications to non-editable content +PASS [["delete",""]] "שָׁ[]לוֹם" compare innerHTML +PASS [["delete",""]] "שָׁ[]לוֹם" queryCommandIndeterm("delete") before +PASS [["delete",""]] "שָׁ[]לוֹם" queryCommandState("delete") before +PASS [["delete",""]] "שָׁ[]לוֹם" queryCommandValue("delete") before +PASS [["delete",""]] "שָׁ[]לוֹם" queryCommandIndeterm("delete") after +PASS [["delete",""]] "שָׁ[]לוֹם" queryCommandState("delete") after +PASS [["delete",""]] "שָׁ[]לוֹם" queryCommandValue("delete") after +PASS [["delete",""]] "שָׁלוֹ[]ם": execCommand("delete", false, "") return value +PASS [["delete",""]] "שָׁלוֹ[]ם" checks for modifications to non-editable content +PASS [["delete",""]] "שָׁלוֹ[]ם" compare innerHTML +PASS [["delete",""]] "שָׁלוֹ[]ם" queryCommandIndeterm("delete") before +PASS [["delete",""]] "שָׁלוֹ[]ם" queryCommandState("delete") before +PASS [["delete",""]] "שָׁלוֹ[]ם" queryCommandValue("delete") before +PASS [["delete",""]] "שָׁלוֹ[]ם" queryCommandIndeterm("delete") after +PASS [["delete",""]] "שָׁלוֹ[]ם" queryCommandState("delete") after +PASS [["delete",""]] "שָׁלוֹ[]ם" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><p>[]bar</p>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo</p>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo</p>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo</p>[]bar" compare innerHTML +PASS [["delete",""]] "<p>foo</p>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo</p>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo</p>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo</p>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo</p>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo</p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br></p><p>[]bar</p>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<br></p>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<br></p>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo<br></p>[]bar" compare innerHTML +PASS [["delete",""]] "<p>foo<br></p>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<br></p>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<br></p>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<br></p>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<br></p>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<br></p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<br>bar</p>" but got "<p>foo<br>bar<br></p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<br>bar</p>" but got "<p>foo<br>bar<br></p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br></p><p>[]bar</p>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<br><br></p>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<br><br></p>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<p>foo<br><br></p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<br>bar</p>" but got "<p>foo<br>bar<br></p>" +PASS [["delete",""]] "<p>foo<br><br></p>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<br><br></p>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<br><br></p>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<br><br></p>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<br><br></p>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<br><br></p>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo<br><br><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<br><br><p>[]bar</p>" checks for modifications to non-editable content +PASS [["delete",""]] "foo<br><br><p>[]bar</p>" compare innerHTML +PASS [["delete",""]] "foo<br><br><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<br><br><p>[]bar</p>" queryCommandState("delete") before +PASS [["delete",""]] "foo<br><br><p>[]bar</p>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<br><br><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<br><br><p>[]bar</p>" queryCommandState("delete") after +PASS [["delete",""]] "foo<br><br><p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><div><p>[]bar</p></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo</p></div><div><p>[]bar</p></div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div><p>foo</p></div>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div><p>foo</p></div>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<div><p>foo</p></div>[]bar" compare innerHTML +PASS [["delete",""]] "<div><p>foo</p></div>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div><p>foo</p></div>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<div><p>foo</p></div>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<div><p>foo</p></div>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div><p>foo</p></div>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<div><p>foo</p></div>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><p>[]bar</p></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo</div><div>[]bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<pre>foo</pre>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<pre>foo</pre>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<pre>foo</pre>[]bar" compare innerHTML +PASS [["delete",""]] "<pre>foo</pre>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<pre>foo</pre>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<pre>foo</pre>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<pre>foo</pre>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<pre>foo</pre>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<pre>foo</pre>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo<br>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<br>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo<br>[]bar" compare innerHTML +PASS [["delete",""]] "foo<br>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<br>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foo<br>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo<br>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<br>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foo<br>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo<br><b>[]bar</b>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<br><b>[]bar</b>" checks for modifications to non-editable content +PASS [["delete",""]] "foo<br><b>[]bar</b>" compare innerHTML +PASS [["delete",""]] "foo<br><b>[]bar</b>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<br><b>[]bar</b>" queryCommandState("delete") before +PASS [["delete",""]] "foo<br><b>[]bar</b>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<br><b>[]bar</b>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<br><b>[]bar</b>" queryCommandState("delete") after +PASS [["delete",""]] "foo<br><b>[]bar</b>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<hr>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<hr>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo<hr>[]bar" compare innerHTML +PASS [["delete",""]] "foo<hr>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<hr>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foo<hr>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo<hr>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<hr>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foo<hr>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<hr><p>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<hr><p>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo<hr><p>[]bar" compare innerHTML +PASS [["delete",""]] "<p>foo<hr><p>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<hr><p>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<hr><p>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<hr><p>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<hr><p>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<hr><p>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo</p><br><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo</p><br><p>[]bar</p>" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo</p><br><p>[]bar</p>" compare innerHTML +PASS [["delete",""]] "<p>foo</p><br><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo</p><br><p>[]bar</p>" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo</p><br><p>[]bar</p>" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo</p><br><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo</p><br><p>[]bar</p>" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo</p><br><p>[]bar</p>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo</p><br><br><p>[]bar</p>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo</p><br><br><p>[]bar</p>" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo</p><br><br><p>[]bar</p>" compare innerHTML +PASS [["delete",""]] "<p>foo</p><br><br><p>[]bar</p>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo</p><br><br><p>[]bar</p>" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo</p><br><br><p>[]bar</p>" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo</p><br><br><p>[]bar</p>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo</p><br><br><p>[]bar</p>" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo</p><br><br><p>[]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p><img src=/img/lion.svg><p>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo<img src=/img/lion.svg>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<img src=/img/lion.svg>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo<img src=/img/lion.svg>[]bar" compare innerHTML +PASS [["delete",""]] "foo<img src=/img/lion.svg>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<img src=/img/lion.svg>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foo<img src=/img/lion.svg>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo<img src=/img/lion.svg>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<img src=/img/lion.svg>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foo<img src=/img/lion.svg>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<a>foo</a>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<a>foo</a>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<a>foo</a>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foobar" but got "<a>fo</a>bar" +PASS [["delete",""]] "<a>foo</a>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<a>foo</a>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<a>foo</a>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<a>foo</a>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<a>foo</a>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<a>foo</a>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<a href=/>foo</a>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<a href=/>foo</a>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<a href=/>foo</a>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foobar" but got "<a href=\"/\">fo</a>bar" +PASS [["delete",""]] "<a href=/>foo</a>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<a href=/>foo</a>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<a href=/>foo</a>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<a href=/>foo</a>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<a href=/>foo</a>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<a href=/>foo</a>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<a name=abc>foo</a>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<a name=abc>foo</a>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<a name=abc>foo</a>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foobar" but got "<a name=\"abc\">fo</a>bar" +PASS [["delete",""]] "<a name=abc>foo</a>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<a name=abc>foo</a>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<a name=abc>foo</a>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<a name=abc>foo</a>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<a name=abc>foo</a>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<a name=abc>foo</a>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<a href=/ name=abc>foo</a>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<a href=/ name=abc>foo</a>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<a href=/ name=abc>foo</a>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foobar" but got "<a href=\"/\" name=\"abc\">fo</a>bar" +PASS [["delete",""]] "<a href=/ name=abc>foo</a>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<a href=/ name=abc>foo</a>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<a href=/ name=abc>foo</a>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<a href=/ name=abc>foo</a>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<a href=/ name=abc>foo</a>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<a href=/ name=abc>foo</a>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<span><a>foo</a></span>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span><a>foo</a></span>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<span><a>foo</a></span>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<span>foo</span>bar" but got "<span><a>fo</a></span>bar" +PASS [["delete",""]] "<span><a>foo</a></span>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span><a>foo</a></span>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<span><a>foo</a></span>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<span><a>foo</a></span>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span><a>foo</a></span>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<span><a>foo</a></span>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<span><a href=/>foo</a></span>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span><a href=/>foo</a></span>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<span><a href=/>foo</a></span>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<span>foo</span>bar" but got "<span><a href=\"/\">fo</a></span>bar" +PASS [["delete",""]] "<span><a href=/>foo</a></span>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span><a href=/>foo</a></span>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<span><a href=/>foo</a></span>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<span><a href=/>foo</a></span>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span><a href=/>foo</a></span>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<span><a href=/>foo</a></span>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<span><a name=abc>foo</a></span>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span><a name=abc>foo</a></span>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<span><a name=abc>foo</a></span>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<span>foo</span>bar" but got "<span><a name=\"abc\">fo</a></span>bar" +PASS [["delete",""]] "<span><a name=abc>foo</a></span>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span><a name=abc>foo</a></span>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<span><a name=abc>foo</a></span>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<span><a name=abc>foo</a></span>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span><a name=abc>foo</a></span>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<span><a name=abc>foo</a></span>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<span><a href=/ name=abc>foo</a></span>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span><a href=/ name=abc>foo</a></span>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<span><a href=/ name=abc>foo</a></span>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<span>foo</span>bar" but got "<span><a href=\"/\" name=\"abc\">fo</a></span>bar" +PASS [["delete",""]] "<span><a href=/ name=abc>foo</a></span>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span><a href=/ name=abc>foo</a></span>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<span><a href=/ name=abc>foo</a></span>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<span><a href=/ name=abc>foo</a></span>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span><a href=/ name=abc>foo</a></span>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<span><a href=/ name=abc>foo</a></span>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo<a>[]bar</a>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<a>[]bar</a>" checks for modifications to non-editable content +PASS [["delete",""]] "foo<a>[]bar</a>" compare innerHTML +PASS [["delete",""]] "foo<a>[]bar</a>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<a>[]bar</a>" queryCommandState("delete") before +PASS [["delete",""]] "foo<a>[]bar</a>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<a>[]bar</a>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<a>[]bar</a>" queryCommandState("delete") after +PASS [["delete",""]] "foo<a>[]bar</a>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<a href=/>[]bar</a>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<a href=/>[]bar</a>" checks for modifications to non-editable content +PASS [["delete",""]] "foo<a href=/>[]bar</a>" compare innerHTML +PASS [["delete",""]] "foo<a href=/>[]bar</a>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<a href=/>[]bar</a>" queryCommandState("delete") before +PASS [["delete",""]] "foo<a href=/>[]bar</a>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<a href=/>[]bar</a>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<a href=/>[]bar</a>" queryCommandState("delete") after +PASS [["delete",""]] "foo<a href=/>[]bar</a>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<a name=abc>[]bar</a>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<a name=abc>[]bar</a>" checks for modifications to non-editable content +PASS [["delete",""]] "foo<a name=abc>[]bar</a>" compare innerHTML +PASS [["delete",""]] "foo<a name=abc>[]bar</a>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<a name=abc>[]bar</a>" queryCommandState("delete") before +PASS [["delete",""]] "foo<a name=abc>[]bar</a>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<a name=abc>[]bar</a>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<a name=abc>[]bar</a>" queryCommandState("delete") after +PASS [["delete",""]] "foo<a name=abc>[]bar</a>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<a href=/ name=abc>[]bar</a>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<a href=/ name=abc>[]bar</a>" checks for modifications to non-editable content +PASS [["delete",""]] "foo<a href=/ name=abc>[]bar</a>" compare innerHTML +PASS [["delete",""]] "foo<a href=/ name=abc>[]bar</a>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<a href=/ name=abc>[]bar</a>" queryCommandState("delete") before +PASS [["delete",""]] "foo<a href=/ name=abc>[]bar</a>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<a href=/ name=abc>[]bar</a>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<a href=/ name=abc>[]bar</a>" queryCommandState("delete") after +PASS [["delete",""]] "foo<a href=/ name=abc>[]bar</a>" queryCommandValue("delete") after +PASS [["delete",""]] "foo []": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo []" checks for modifications to non-editable content +PASS [["delete",""]] "foo []" compare innerHTML +PASS [["delete",""]] "foo []" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo []" queryCommandState("delete") before +PASS [["delete",""]] "foo []" queryCommandValue("delete") before +PASS [["delete",""]] "foo []" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo []" queryCommandState("delete") after +PASS [["delete",""]] "foo []" queryCommandValue("delete") after +PASS [["delete",""]] " [] foo": execCommand("delete", false, "") return value +PASS [["delete",""]] " [] foo" checks for modifications to non-editable content +PASS [["delete",""]] " [] foo" compare innerHTML +PASS [["delete",""]] " [] foo" queryCommandIndeterm("delete") before +PASS [["delete",""]] " [] foo" queryCommandState("delete") before +PASS [["delete",""]] " [] foo" queryCommandValue("delete") before +PASS [["delete",""]] " [] foo" queryCommandIndeterm("delete") after +PASS [["delete",""]] " [] foo" queryCommandState("delete") after +PASS [["delete",""]] " [] foo" queryCommandValue("delete") after +PASS [["delete",""]] "foo []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo []bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo []bar" compare innerHTML +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo []bar" queryCommandState("delete") before +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo []bar" queryCommandState("delete") after +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo []bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo []bar" compare innerHTML +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo []bar" queryCommandState("delete") before +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo []bar" queryCommandState("delete") after +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo []bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo []bar" compare innerHTML +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo []bar" queryCommandState("delete") before +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo []bar" queryCommandState("delete") after +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo []bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo []bar" compare innerHTML +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo []bar" queryCommandState("delete") before +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo []bar" queryCommandState("delete") after +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo [] bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo [] bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo [] bar" compare innerHTML +PASS [["delete",""]] "foo [] bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo [] bar" queryCommandState("delete") before +PASS [["delete",""]] "foo [] bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo [] bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo [] bar" queryCommandState("delete") after +PASS [["delete",""]] "foo [] bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo [] bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo [] bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo [] bar" compare innerHTML +PASS [["delete",""]] "foo [] bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo [] bar" queryCommandState("delete") before +PASS [["delete",""]] "foo [] bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo [] bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo [] bar" queryCommandState("delete") after +PASS [["delete",""]] "foo [] bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo []bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo []bar" compare innerHTML +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo []bar" queryCommandState("delete") before +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo []bar" queryCommandState("delete") after +PASS [["delete",""]] "foo []bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo []<span> </span> bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo []<span> </span> bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo []<span> </span> bar" compare innerHTML +PASS [["delete",""]] "foo []<span> </span> bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo []<span> </span> bar" queryCommandState("delete") before +PASS [["delete",""]] "foo []<span> </span> bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo []<span> </span> bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo []<span> </span> bar" queryCommandState("delete") after +PASS [["delete",""]] "foo []<span> </span> bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo <span> </span>[] bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo <span> </span>[] bar" checks for modifications to non-editable content +FAIL [["delete",""]] "foo <span> </span>[] bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo bar" but got "foo bar" +PASS [["delete",""]] "foo <span> </span>[] bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo <span> </span>[] bar" queryCommandState("delete") before +PASS [["delete",""]] "foo <span> </span>[] bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo <span> </span>[] bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo <span> </span>[] bar" queryCommandState("delete") after +PASS [["delete",""]] "foo <span> </span>[] bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo <span> </span> []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo <span> </span> []bar" checks for modifications to non-editable content +FAIL [["delete",""]] "foo <span> </span> []bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo <span> </span>bar" but got "foo <span> </span>bar" +PASS [["delete",""]] "foo <span> </span> []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo <span> </span> []bar" queryCommandState("delete") before +PASS [["delete",""]] "foo <span> </span> []bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo <span> </span> []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo <span> </span> []bar" queryCommandState("delete") after +PASS [["delete",""]] "foo <span> </span> []bar" queryCommandValue("delete") after +PASS [["delete",""]] "<b>foo </b> []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<b>foo </b> []bar" checks for modifications to non-editable content +PASS [["delete",""]] "<b>foo </b> []bar" compare innerHTML +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandState("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandValue("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandState("delete") after +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandValue("delete") after +PASS [["delete",""]] "<b>foo </b> []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<b>foo </b> []bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<b>foo </b> []bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<b>foo </b>bar" but got "<b>foo </b>bar" +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandState("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandValue("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandState("delete") after +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandValue("delete") after +PASS [["delete",""]] "<b>foo </b> []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<b>foo </b> []bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<b>foo </b> []bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<b>foo </b>bar" but got "<b>foo </b>bar" +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandState("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandValue("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandState("delete") after +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandValue("delete") after +PASS [["delete",""]] "<b>foo </b> []bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<b>foo </b> []bar" checks for modifications to non-editable content +PASS [["delete",""]] "<b>foo </b> []bar" compare innerHTML +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandState("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandValue("delete") before +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandState("delete") after +PASS [["delete",""]] "<b>foo </b> []bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo </p><p>[] bar</p>" queryCommandValue("delete") after +PASS [["delete",""]] "<pre>foo []</pre>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<pre>foo []</pre>" checks for modifications to non-editable content +PASS [["delete",""]] "<pre>foo []</pre>" compare innerHTML +PASS [["delete",""]] "<pre>foo []</pre>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<pre>foo []</pre>" queryCommandState("delete") before +PASS [["delete",""]] "<pre>foo []</pre>" queryCommandValue("delete") before +PASS [["delete",""]] "<pre>foo []</pre>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<pre>foo []</pre>" queryCommandState("delete") after +PASS [["delete",""]] "<pre>foo []</pre>" queryCommandValue("delete") after +PASS [["delete",""]] "<pre> [] foo</pre>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<pre> [] foo</pre>" checks for modifications to non-editable content +PASS [["delete",""]] "<pre> [] foo</pre>" compare innerHTML +PASS [["delete",""]] "<pre> [] foo</pre>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<pre> [] foo</pre>" queryCommandState("delete") before +PASS [["delete",""]] "<pre> [] foo</pre>" queryCommandValue("delete") before +PASS [["delete",""]] "<pre> [] foo</pre>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<pre> [] foo</pre>" queryCommandState("delete") after +PASS [["delete",""]] "<pre> [] foo</pre>" queryCommandValue("delete") after +PASS [["delete",""]] "<pre>foo []bar</pre>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<pre>foo []bar</pre>" checks for modifications to non-editable content +PASS [["delete",""]] "<pre>foo []bar</pre>" compare innerHTML +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandState("delete") before +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandValue("delete") before +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandState("delete") after +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandValue("delete") after +PASS [["delete",""]] "<pre>foo []bar</pre>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<pre>foo []bar</pre>" checks for modifications to non-editable content +PASS [["delete",""]] "<pre>foo []bar</pre>" compare innerHTML +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandState("delete") before +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandValue("delete") before +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandState("delete") after +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandValue("delete") after +PASS [["delete",""]] "<pre>foo []bar</pre>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<pre>foo []bar</pre>" checks for modifications to non-editable content +PASS [["delete",""]] "<pre>foo []bar</pre>" compare innerHTML +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandState("delete") before +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandValue("delete") before +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandState("delete") after +PASS [["delete",""]] "<pre>foo []bar</pre>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre>foo []</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre>foo []</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre>foo []</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre> [] foo</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre> [] foo</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre> [] foo</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre> [] foo</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre> [] foo</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre> [] foo</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre> [] foo</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre> [] foo</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre> [] foo</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap> [] foo</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-wrap> [] foo</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-wrap> [] foo</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-wrap> [] foo</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap> [] foo</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap> [] foo</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap> [] foo</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap> [] foo</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap> [] foo</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-wrap>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-line>foo []</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-line>foo []</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-line>foo []</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line> [] foo</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-line> [] foo</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-line> [] foo</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-line> [] foo</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line> [] foo</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line> [] foo</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line> [] foo</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line> [] foo</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line> [] foo</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:pre-line>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:nowrap>foo []</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:nowrap>foo []</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:nowrap>foo []</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap> [] foo</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:nowrap> [] foo</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:nowrap> [] foo</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:nowrap> [] foo</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap> [] foo</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap> [] foo</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap> [] foo</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap> [] foo</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap> [] foo</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" checks for modifications to non-editable content +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" compare innerHTML +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandState("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandState("delete") after +PASS [["delete",""]] "<div style=white-space:nowrap>foo []bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<table><tr><td>[]bar</table>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<table><tr><td>[]bar</table>baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<table><tr><td>[]bar</table>baz" compare innerHTML +PASS [["delete",""]] "foo<table><tr><td>[]bar</table>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<table><tr><td>[]bar</table>baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<table><tr><td>[]bar</table>baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<table><tr><td>[]bar</table>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<table><tr><td>[]bar</table>baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<table><tr><td>[]bar</table>baz" queryCommandValue("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar</table>[]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<table><tr><td>bar</table>[]baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<table><tr><td>bar</table>[]baz" compare innerHTML +PASS [["delete",""]] "foo<table><tr><td>bar</table>[]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar</table>[]baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar</table>[]baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar</table>[]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar</table>[]baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar</table>[]baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>[]bar</table><p>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<table><tr><td>[]bar</table><p>baz" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo<table><tr><td>[]bar</table><p>baz" compare innerHTML +PASS [["delete",""]] "<p>foo<table><tr><td>[]bar</table><p>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>[]bar</table><p>baz" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>[]bar</table><p>baz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>[]bar</table><p>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>[]bar</table><p>baz" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>[]bar</table><p>baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>bar</table><p>[]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<table><tr><td>bar</table><p>[]baz" checks for modifications to non-editable content +FAIL [["delete",""]] "<p>foo<table><tr><td>bar</table><p>[]baz" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo</p><table><tbody><tr><td>bar</td></tr></tbody></table><p>baz</p>" but got "<p>foo</p><table><tbody><tr><td>bar</td></tr></tbody></table>baz" +PASS [["delete",""]] "<p>foo<table><tr><td>bar</table><p>[]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>bar</table><p>[]baz" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>bar</table><p>[]baz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>bar</table><p>[]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>bar</table><p>[]baz" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>bar</table><p>[]baz" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tr><td>foo<td>[]bar</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tr><td>foo<td>[]bar</table>" checks for modifications to non-editable content +PASS [["delete",""]] "<table><tr><td>foo<td>[]bar</table>" compare innerHTML +PASS [["delete",""]] "<table><tr><td>foo<td>[]bar</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tr><td>foo<td>[]bar</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tr><td>foo<td>[]bar</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tr><td>foo<td>[]bar</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tr><td>foo<td>[]bar</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tr><td>foo<td>[]bar</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tr><td>foo<tr><td>[]bar</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tr><td>foo<tr><td>[]bar</table>" checks for modifications to non-editable content +PASS [["delete",""]] "<table><tr><td>foo<tr><td>[]bar</table>" compare innerHTML +PASS [["delete",""]] "<table><tr><td>foo<tr><td>[]bar</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tr><td>foo<tr><td>[]bar</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tr><td>foo<tr><td>[]bar</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tr><td>foo<tr><td>[]bar</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tr><td>foo<tr><td>[]bar</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tr><td>foo<tr><td>[]bar</table>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<br><table><tr><td>[]bar</table>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<br><table><tr><td>[]bar</table>baz" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<br><table><tr><td>[]bar</table>baz" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<table><tbody><tr><td>bar</td></tr></tbody></table>baz" but got "foo<br><table><tbody><tr><td>bar</td></tr></tbody></table>baz" +PASS [["delete",""]] "foo<br><table><tr><td>[]bar</table>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<br><table><tr><td>[]bar</table>baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<br><table><tr><td>[]bar</table>baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<br><table><tr><td>[]bar</table>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<br><table><tr><td>[]bar</table>baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<br><table><tr><td>[]bar</table>baz" queryCommandValue("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar<br></table>[]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<table><tr><td>bar<br></table>[]baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<table><tr><td>bar<br></table>[]baz" compare innerHTML +PASS [["delete",""]] "foo<table><tr><td>bar<br></table>[]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar<br></table>[]baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar<br></table>[]baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar<br></table>[]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar<br></table>[]baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar<br></table>[]baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<br><table><tr><td>[]bar</table><p>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<br><table><tr><td>[]bar</table><p>baz" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo<br><table><tr><td>[]bar</table><p>baz" compare innerHTML +PASS [["delete",""]] "<p>foo<br><table><tr><td>[]bar</table><p>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<br><table><tr><td>[]bar</table><p>baz" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<br><table><tr><td>[]bar</table><p>baz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<br><table><tr><td>[]bar</table><p>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<br><table><tr><td>[]bar</table><p>baz" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<br><table><tr><td>[]bar</table><p>baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br></table><p>[]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br></table><p>[]baz" checks for modifications to non-editable content +FAIL [["delete",""]] "<p>foo<table><tr><td>bar<br></table><p>[]baz" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo</p><table><tbody><tr><td>bar<br></td></tr></tbody></table><p>baz</p>" but got "<p>foo</p><table><tbody><tr><td>bar<br></td></tr></tbody></table>baz" +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br></table><p>[]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br></table><p>[]baz" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br></table><p>[]baz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br></table><p>[]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br></table><p>[]baz" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br></table><p>[]baz" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><td>[]bar</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tr><td>foo<br><td>[]bar</table>" checks for modifications to non-editable content +FAIL [["delete",""]] "<table><tr><td>foo<br><td>[]bar</table>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<table><tbody><tr><td>foo</td><td>bar</td></tr></tbody></table>" but got "<table><tbody><tr><td>foo<br></td><td>bar</td></tr></tbody></table>" +PASS [["delete",""]] "<table><tr><td>foo<br><td>[]bar</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><td>[]bar</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><td>[]bar</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><td>[]bar</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><td>[]bar</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><td>[]bar</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><tr><td>[]bar</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tr><td>foo<br><tr><td>[]bar</table>" checks for modifications to non-editable content +FAIL [["delete",""]] "<table><tr><td>foo<br><tr><td>[]bar</table>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<table><tbody><tr><td>foo</td></tr><tr><td>bar</td></tr></tbody></table>" but got "<table><tbody><tr><td>foo<br></td></tr><tr><td>bar</td></tr></tbody></table>" +PASS [["delete",""]] "<table><tr><td>foo<br><tr><td>[]bar</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><tr><td>[]bar</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><tr><td>[]bar</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><tr><td>[]bar</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><tr><td>[]bar</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><tr><td>[]bar</table>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<br><br><table><tr><td>[]bar</table>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<br><br><table><tr><td>[]bar</table>baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<br><br><table><tr><td>[]bar</table>baz" compare innerHTML +PASS [["delete",""]] "foo<br><br><table><tr><td>[]bar</table>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<br><br><table><tr><td>[]bar</table>baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<br><br><table><tr><td>[]bar</table>baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<br><br><table><tr><td>[]bar</table>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<br><br><table><tr><td>[]bar</table>baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<br><br><table><tr><td>[]bar</table>baz" queryCommandValue("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar<br><br></table>[]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<table><tr><td>bar<br><br></table>[]baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<table><tr><td>bar<br><br></table>[]baz" compare innerHTML +PASS [["delete",""]] "foo<table><tr><td>bar<br><br></table>[]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar<br><br></table>[]baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar<br><br></table>[]baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar<br><br></table>[]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar<br><br></table>[]baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar<br><br></table>[]baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<br><br><table><tr><td>[]bar</table><p>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<br><br><table><tr><td>[]bar</table><p>baz" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo<br><br><table><tr><td>[]bar</table><p>baz" compare innerHTML +PASS [["delete",""]] "<p>foo<br><br><table><tr><td>[]bar</table><p>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<br><br><table><tr><td>[]bar</table><p>baz" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<br><br><table><tr><td>[]bar</table><p>baz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<br><br><table><tr><td>[]bar</table><p>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<br><br><table><tr><td>[]bar</table><p>baz" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<br><br><table><tr><td>[]bar</table><p>baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br><br></table><p>[]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br><br></table><p>[]baz" checks for modifications to non-editable content +FAIL [["delete",""]] "<p>foo<table><tr><td>bar<br><br></table><p>[]baz" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo</p><table><tbody><tr><td>bar<br><br></td></tr></tbody></table><p>baz</p>" but got "<p>foo</p><table><tbody><tr><td>bar<br><br></td></tr></tbody></table>baz" +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br><br></table><p>[]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br><br></table><p>[]baz" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br><br></table><p>[]baz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br><br></table><p>[]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br><br></table><p>[]baz" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>bar<br><br></table><p>[]baz" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><br><td>[]bar</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tr><td>foo<br><br><td>[]bar</table>" checks for modifications to non-editable content +PASS [["delete",""]] "<table><tr><td>foo<br><br><td>[]bar</table>" compare innerHTML +PASS [["delete",""]] "<table><tr><td>foo<br><br><td>[]bar</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><br><td>[]bar</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><br><td>[]bar</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><br><td>[]bar</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><br><td>[]bar</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><br><td>[]bar</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><br><tr><td>[]bar</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tr><td>foo<br><br><tr><td>[]bar</table>" checks for modifications to non-editable content +PASS [["delete",""]] "<table><tr><td>foo<br><br><tr><td>[]bar</table>" compare innerHTML +PASS [["delete",""]] "<table><tr><td>foo<br><br><tr><td>[]bar</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><br><tr><td>[]bar</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><br><tr><td>[]bar</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tr><td>foo<br><br><tr><td>[]bar</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><br><tr><td>[]bar</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tr><td>foo<br><br><tr><td>[]bar</table>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<hr><table><tr><td>[]bar</table>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<hr><table><tr><td>[]bar</table>baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<hr><table><tr><td>[]bar</table>baz" compare innerHTML +PASS [["delete",""]] "foo<hr><table><tr><td>[]bar</table>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<hr><table><tr><td>[]bar</table>baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<hr><table><tr><td>[]bar</table>baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<hr><table><tr><td>[]bar</table>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<hr><table><tr><td>[]bar</table>baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<hr><table><tr><td>[]bar</table>baz" queryCommandValue("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar<hr></table>[]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<table><tr><td>bar<hr></table>[]baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<table><tr><td>bar<hr></table>[]baz" compare innerHTML +PASS [["delete",""]] "foo<table><tr><td>bar<hr></table>[]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar<hr></table>[]baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar<hr></table>[]baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<table><tr><td>bar<hr></table>[]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar<hr></table>[]baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<table><tr><td>bar<hr></table>[]baz" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tr><td>foo<hr><td>[]bar</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tr><td>foo<hr><td>[]bar</table>" checks for modifications to non-editable content +PASS [["delete",""]] "<table><tr><td>foo<hr><td>[]bar</table>" compare innerHTML +PASS [["delete",""]] "<table><tr><td>foo<hr><td>[]bar</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tr><td>foo<hr><td>[]bar</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tr><td>foo<hr><td>[]bar</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tr><td>foo<hr><td>[]bar</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tr><td>foo<hr><td>[]bar</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tr><td>foo<hr><td>[]bar</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tr><td>foo<hr><tr><td>[]bar</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tr><td>foo<hr><tr><td>[]bar</table>" checks for modifications to non-editable content +PASS [["delete",""]] "<table><tr><td>foo<hr><tr><td>[]bar</table>" compare innerHTML +PASS [["delete",""]] "<table><tr><td>foo<hr><tr><td>[]bar</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tr><td>foo<hr><tr><td>[]bar</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tr><td>foo<hr><tr><td>[]bar</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tr><td>foo<hr><tr><td>[]bar</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tr><td>foo<hr><tr><td>[]bar</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tr><td>foo<hr><tr><td>[]bar</table>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div>bar</div><ol><li>baz</li></ol>" but got "foobar<ol><li>baz</li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<p>bar</p><ol><li>baz</li></ol>" but got "foobar<ol><li>baz</li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div>bar</div><ol><li>baz</li></ol>" but got "foobar<ol><li>baz</li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<p>bar</p><ol><li>baz</li></ol>" but got "foobar<ol><li>baz</li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<br><br><div>bar</div><ol><li>baz</li></ol>" but got "foo<br><ol><li>bar</li><li>baz</li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<br><br><p>bar</p><ol><li>baz</li></ol>" but got "foo<br><ol><li>bar</li><li>baz</li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br><ol><li>[]bar<li>baz</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<li>[]bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<li>[]bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo<li>[]bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo<br>bar</li></ol>" but got "<ol><li>foobar</li></ol>" +PASS [["delete",""]] "<ol><li>foo<li>[]bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<li>[]bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<li>[]bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<li>[]bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<li>[]bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<li>[]bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<br><li>[]bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<br><li>[]bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo<br><li>[]bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo<br>bar</li></ol>" but got "<ol><li>foobar</li></ol>" +PASS [["delete",""]] "<ol><li>foo<br><li>[]bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<br><li>[]bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<br><li>[]bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<br><li>[]bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<br><li>[]bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<br><li>[]bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<br><br><li>[]bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<br><br><li>[]bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo<br><br><li>[]bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo<br><br>bar</li></ol>" but got "<ol><li>foo<br>bar<br></li></ol>" +PASS [["delete",""]] "<ol><li>foo<br><br><li>[]bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<br><br><li>[]bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<br><br><li>[]bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<br><br><li>[]bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<br><br><li>[]bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<br><br><li>[]bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<li>[]bar<br>baz</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<li>[]bar<br>baz</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo<li>[]bar<br>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo<br>bar<br>baz</li></ol>" but got "<ol><li>foobar</li><li>baz</li></ol>" +PASS [["delete",""]] "<ol><li>foo<li>[]bar<br>baz</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<li>[]bar<br>baz</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<li>[]bar<br>baz</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<li>[]bar<br>baz</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<li>[]bar<br>baz</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<li>[]bar<br>baz</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<br>bar<li>[]baz</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<br>bar<li>[]baz</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo<br>bar<li>[]baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo<br>bar<br>baz</li></ol>" but got "<ol><li>foo<br>barbaz</li></ol>" +PASS [["delete",""]] "<ol><li>foo<br>bar<li>[]baz</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<br>bar<li>[]baz</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<br>bar<li>[]baz</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<br>bar<li>[]baz</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<br>bar<li>[]baz</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<br>bar<li>[]baz</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li><p>foo</p>{}bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li><p>foo</p>{}bar</ol>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li><p>foo</p>{}bar</ol>" compare innerHTML +PASS [["delete",""]] "<ol><li><p>foo</p>{}bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li><p>foo</p>{}bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li><p>foo</p>{}bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li><p>foo</p>{}bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li><p>foo</p>{}bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li><p>foo</p>{}bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li><p>foo<li>[]bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li><p>foo<li>[]bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li><p>foo<li>[]bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li><p>foo</p>bar</li></ol>" but got "<ol><li><p>foobar</p></li></ol>" +PASS [["delete",""]] "<ol><li><p>foo<li>[]bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li><p>foo<li>[]bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li><p>foo<li>[]bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li><p>foo<li>[]bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li><p>foo<li>[]bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li><p>foo<li>[]bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<li><p>[]bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<li><p>[]bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo<li><p>[]bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo<p>bar</p></li></ol>" but got "<ol><li>foobar</li></ol>" +PASS [["delete",""]] "<ol><li>foo<li><p>[]bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<li><p>[]bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<li><p>[]bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<li><p>[]bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<li><p>[]bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<li><p>[]bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li><p>foo<li><p>[]bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li><p>foo<li><p>[]bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li><p>foo<li><p>[]bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li><p>foo</p><p>bar</p></li></ol>" but got "<ol><li><p>foobar</p></li></ol>" +PASS [["delete",""]] "<ol><li><p>foo<li><p>[]bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li><p>foo<li><p>[]bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li><p>foo<li><p>[]bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li><p>foo<li><p>[]bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li><p>foo<li><p>[]bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li><p>foo<li><p>[]bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<ul><li>[]bar</ul></ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<ul><li>[]bar</ul></ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo<ul><li>[]bar</ul></ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>bar</li></ol>" but got "<ol><li>foobar</li></ol>" +PASS [["delete",""]] "<ol><li>foo<ul><li>[]bar</ul></ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<ul><li>[]bar</ul></ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<ul><li>[]bar</ul></ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<ul><li>[]bar</ul></ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<ul><li>[]bar</ul></ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<ul><li>[]bar</ul></ol>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<ol><ol><li>[]bar</ol></ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<ol><ol><li>[]bar</ol></ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<ol><ol><li>[]bar</ol></ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<ol><li>bar</li></ol>" but got "foobar" +PASS [["delete",""]] "foo<ol><ol><li>[]bar</ol></ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<ol><ol><li>[]bar</ol></ol>" queryCommandState("delete") before +PASS [["delete",""]] "foo<ol><ol><li>[]bar</ol></ol>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<ol><ol><li>[]bar</ol></ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<ol><ol><li>[]bar</ol></ol>" queryCommandState("delete") after +PASS [["delete",""]] "foo<ol><ol><li>[]bar</ol></ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div><div>bar</div></div>" but got "foobar" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div><p>bar</p></div>" but got "foobar" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><ol><li>[]bar</ol></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div>bar</div><dl><dd>baz</dd></dl>" but got "foobar<dl><dd>baz</dd></dl>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<p>bar</p><dl><dd>baz</dd></dl>" but got "foobar<dl><dd>baz</dd></dl>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dt>[]bar<dd>baz</dl>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div>bar</div>" but got "foobar" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<p>bar</p>" but got "foobar" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<dl><dd>[]bar</dl>" queryCommandValue("delete") after +PASS [["delete",""]] "<dl><dt>foo<dd>[]bar</dl>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<dl><dt>foo<dd>[]bar</dl>" checks for modifications to non-editable content +FAIL [["delete",""]] "<dl><dt>foo<dd>[]bar</dl>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<dl><dt>foo<br>bar</dt></dl>" but got "<dl><dt>foobar</dt></dl>" +PASS [["delete",""]] "<dl><dt>foo<dd>[]bar</dl>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<dl><dt>foo<dd>[]bar</dl>" queryCommandState("delete") before +PASS [["delete",""]] "<dl><dt>foo<dd>[]bar</dl>" queryCommandValue("delete") before +PASS [["delete",""]] "<dl><dt>foo<dd>[]bar</dl>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<dl><dt>foo<dd>[]bar</dl>" queryCommandState("delete") after +PASS [["delete",""]] "<dl><dt>foo<dd>[]bar</dl>" queryCommandValue("delete") after +PASS [["delete",""]] "<dl><dt>foo<dt>[]bar<dd>baz</dl>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<dl><dt>foo<dt>[]bar<dd>baz</dl>" checks for modifications to non-editable content +FAIL [["delete",""]] "<dl><dt>foo<dt>[]bar<dd>baz</dl>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<dl><dt>foo<br>bar</dt><dd>baz</dd></dl>" but got "<dl><dt>foobar</dt><dd>baz</dd></dl>" +PASS [["delete",""]] "<dl><dt>foo<dt>[]bar<dd>baz</dl>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<dl><dt>foo<dt>[]bar<dd>baz</dl>" queryCommandState("delete") before +PASS [["delete",""]] "<dl><dt>foo<dt>[]bar<dd>baz</dl>" queryCommandValue("delete") before +PASS [["delete",""]] "<dl><dt>foo<dt>[]bar<dd>baz</dl>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<dl><dt>foo<dt>[]bar<dd>baz</dl>" queryCommandState("delete") after +PASS [["delete",""]] "<dl><dt>foo<dt>[]bar<dd>baz</dl>" queryCommandValue("delete") after +PASS [["delete",""]] "<dl><dt>foo<dd>bar<dd>[]baz</dl>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<dl><dt>foo<dd>bar<dd>[]baz</dl>" checks for modifications to non-editable content +FAIL [["delete",""]] "<dl><dt>foo<dd>bar<dd>[]baz</dl>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<dl><dt>foo</dt><dd>bar<br>baz</dd></dl>" but got "<dl><dt>foo</dt><dd>barbaz</dd></dl>" +PASS [["delete",""]] "<dl><dt>foo<dd>bar<dd>[]baz</dl>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<dl><dt>foo<dd>bar<dd>[]baz</dl>" queryCommandState("delete") before +PASS [["delete",""]] "<dl><dt>foo<dd>bar<dd>[]baz</dl>" queryCommandValue("delete") before +PASS [["delete",""]] "<dl><dt>foo<dd>bar<dd>[]baz</dl>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<dl><dt>foo<dd>bar<dd>[]baz</dl>" queryCommandState("delete") after +PASS [["delete",""]] "<dl><dt>foo<dd>bar<dd>[]baz</dl>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo</ol>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo</ol>[]bar" compare innerHTML +PASS [["delete",""]] "<ol><li>foo</ol>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<br></ol>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<br></ol>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo<br></ol>[]bar" compare innerHTML +PASS [["delete",""]] "<ol><li>foo<br></ol>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<br></ol>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<br></ol>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<br></ol>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<br></ol>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<br></ol>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<br><br></ol>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<br><br></ol>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo<br><br></ol>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo<br>bar</li></ol>" but got "<ol><li>foo<br>bar<br></li></ol>" +PASS [["delete",""]] "<ol><li>foo<br><br></ol>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<br><br></ol>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<br><br></ol>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<br><br></ol>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<br><br></ol>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<br><br></ol>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li><br></ol>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li><br></ol>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li><br></ol>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>bar</li></ol>" but got "<ol><li>bar<br></li></ol>" +PASS [["delete",""]] "<ol><li><br></ol>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li><br></ol>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li><br></ol>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li><br></ol>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li><br></ol>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li><br></ol>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<li><br></ol>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<li><br></ol>[]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo<li><br></ol>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>bar</li></ol>" but got "<ol><li>foo</li><li>bar<br></li></ol>" +PASS [["delete",""]] "<ol><li>foo<li><br></ol>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<li><br></ol>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<li><br></ol>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<li><br></ol>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<li><br></ol>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<li><br></ol>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo<br>bar</li></ol>" but got "<ol><li>foo<br>bar<br></li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo<br>bar</li></ol>" but got "<ol><li>foo<br>bar<br></li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>bar</li></ol>" but got "<ol><li>bar<br></li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>bar</li></ol>" but got "<ol><li>bar<br></li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>bar</li></ol>" but got "<ol><li>foo</li><li>bar<br></li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>bar</li></ol>" but got "<ol><li>foo</li><li>bar<br></li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo</ol>{}<br>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo</ol>{}<br>" compare innerHTML +PASS [["delete",""]] "<ol><li>foo</ol>{}<br>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<br></ol>{}<br>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<br></ol>{}<br>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo<br></ol>{}<br>" compare innerHTML +PASS [["delete",""]] "<ol><li>foo<br></ol>{}<br>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<br></ol>{}<br>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<br></ol>{}<br>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<br></ol>{}<br>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<br></ol>{}<br>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<br></ol>{}<br>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<br><br></ol>{}<br>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<br><br></ol>{}<br>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo<br><br></ol>{}<br>" compare innerHTML +PASS [["delete",""]] "<ol><li>foo<br><br></ol>{}<br>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<br><br></ol>{}<br>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<br><br></ol>{}<br>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<br><br></ol>{}<br>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<br><br></ol>{}<br>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<br><br></ol>{}<br>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li><br></ol>{}<br>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li><br></ol>{}<br>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li><br></ol>{}<br>" compare innerHTML +PASS [["delete",""]] "<ol><li><br></ol>{}<br>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li><br></ol>{}<br>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li><br></ol>{}<br>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li><br></ol>{}<br>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li><br></ol>{}<br>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li><br></ol>{}<br>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo<li><br></ol>{}<br>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo<li><br></ol>{}<br>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo<li><br></ol>{}<br>" compare innerHTML +PASS [["delete",""]] "<ol><li>foo<li><br></ol>{}<br>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo<li><br></ol>{}<br>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo<li><br></ol>{}<br>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo<li><br></ol>{}<br>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo<li><br></ol>{}<br>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo<li><br></ol>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br></ol><p>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<br><br></ol><p>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><br></ol><p>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo<li><br></ol><p>{}<br>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<blockquote>[]bar</blockquote>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<blockquote>[]bar</blockquote>" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<blockquote>[]bar</blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<br>bar" but got "foobar" +PASS [["delete",""]] "foo<blockquote>[]bar</blockquote>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<blockquote>[]bar</blockquote>" queryCommandState("delete") before +PASS [["delete",""]] "foo<blockquote>[]bar</blockquote>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<blockquote>[]bar</blockquote>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<blockquote>[]bar</blockquote>" queryCommandState("delete") after +PASS [["delete",""]] "foo<blockquote>[]bar</blockquote>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<blockquote><blockquote>[]bar</blockquote></blockquote>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<blockquote><blockquote>[]bar</blockquote></blockquote>" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<blockquote><blockquote>[]bar</blockquote></blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<blockquote>bar</blockquote>" but got "foobar" +PASS [["delete",""]] "foo<blockquote><blockquote>[]bar</blockquote></blockquote>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<blockquote><blockquote>[]bar</blockquote></blockquote>" queryCommandState("delete") before +PASS [["delete",""]] "foo<blockquote><blockquote>[]bar</blockquote></blockquote>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<blockquote><blockquote>[]bar</blockquote></blockquote>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<blockquote><blockquote>[]bar</blockquote></blockquote>" queryCommandState("delete") after +PASS [["delete",""]] "foo<blockquote><blockquote>[]bar</blockquote></blockquote>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<blockquote><div>[]bar</div></blockquote>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<blockquote><div>[]bar</div></blockquote>" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<blockquote><div>[]bar</div></blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div>bar</div>" but got "foobar" +PASS [["delete",""]] "foo<blockquote><div>[]bar</div></blockquote>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<blockquote><div>[]bar</div></blockquote>" queryCommandState("delete") before +PASS [["delete",""]] "foo<blockquote><div>[]bar</div></blockquote>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<blockquote><div>[]bar</div></blockquote>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<blockquote><div>[]bar</div></blockquote>" queryCommandState("delete") after +PASS [["delete",""]] "foo<blockquote><div>[]bar</div></blockquote>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div style=\"color:rgb(0, 0, 255)\">bar</div>" but got "foo<span style=\"color:rgb(0, 0, 255)\">bar</span>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div style=\"color:rgb(0, 0, 255)\">bar</div>" but got "foo<span style=\"color:rgb(0, 0, 255)\">bar</span>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\">[]bar</blockquote>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<blockquote><blockquote><p>[]bar<p>baz</blockquote></blockquote>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<blockquote><blockquote><p>[]bar<p>baz</blockquote></blockquote>" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<blockquote><blockquote><p>[]bar<p>baz</blockquote></blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<blockquote><p>bar</p><blockquote><p>baz</p></blockquote></blockquote>" but got "foobar<blockquote><blockquote><p>baz</p></blockquote></blockquote>" +PASS [["delete",""]] "foo<blockquote><blockquote><p>[]bar<p>baz</blockquote></blockquote>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<blockquote><blockquote><p>[]bar<p>baz</blockquote></blockquote>" queryCommandState("delete") before +PASS [["delete",""]] "foo<blockquote><blockquote><p>[]bar<p>baz</blockquote></blockquote>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<blockquote><blockquote><p>[]bar<p>baz</blockquote></blockquote>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<blockquote><blockquote><p>[]bar<p>baz</blockquote></blockquote>" queryCommandState("delete") after +PASS [["delete",""]] "foo<blockquote><blockquote><p>[]bar<p>baz</blockquote></blockquote>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<blockquote><div><p>[]bar<p>baz</div></blockquote>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<blockquote><div><p>[]bar<p>baz</div></blockquote>" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<blockquote><div><p>[]bar<p>baz</div></blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div><p>bar</p><blockquote><p>baz</p></blockquote></div>" but got "foobar<blockquote><p>baz</p></blockquote>" +PASS [["delete",""]] "foo<blockquote><div><p>[]bar<p>baz</div></blockquote>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<blockquote><div><p>[]bar<p>baz</div></blockquote>" queryCommandState("delete") before +PASS [["delete",""]] "foo<blockquote><div><p>[]bar<p>baz</div></blockquote>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<blockquote><div><p>[]bar<p>baz</div></blockquote>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<blockquote><div><p>[]bar<p>baz</div></blockquote>" queryCommandState("delete") after +PASS [["delete",""]] "foo<blockquote><div><p>[]bar<p>baz</div></blockquote>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div style=\"color:rgb(0, 0, 255)\"><p>bar</p><blockquote><p>baz</p></blockquote></div>" but got "foo<span style=\"color:rgb(0, 0, 255)\">bar</span><blockquote style=\"color:rgb(0, 0, 255)\"><p>baz</p></blockquote>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<div style=\"color:rgb(0, 0, 255)\"><p>bar</p><blockquote><p>baz</p></blockquote></div>" but got "foo<span style=\"color:rgb(0, 0, 255)\">bar</span><blockquote style=\"color:rgb(0, 0, 255)\"><p>baz</p></blockquote>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote style=\"color: blue\"><p>[]bar<p>baz</blockquote>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<blockquote><p><b>[]bar</b><p>baz</blockquote>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<blockquote><p><b>[]bar</b><p>baz</blockquote>" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<blockquote><p><b>[]bar</b><p>baz</blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<p><b>bar</b></p><blockquote><p>baz</p></blockquote>" but got "foo<b>bar</b><blockquote><p>baz</p></blockquote>" +PASS [["delete",""]] "foo<blockquote><p><b>[]bar</b><p>baz</blockquote>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<blockquote><p><b>[]bar</b><p>baz</blockquote>" queryCommandState("delete") before +PASS [["delete",""]] "foo<blockquote><p><b>[]bar</b><p>baz</blockquote>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<blockquote><p><b>[]bar</b><p>baz</blockquote>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<blockquote><p><b>[]bar</b><p>baz</blockquote>" queryCommandState("delete") after +PASS [["delete",""]] "foo<blockquote><p><b>[]bar</b><p>baz</blockquote>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<blockquote><p><strong>[]bar</strong><p>baz</blockquote>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<blockquote><p><strong>[]bar</strong><p>baz</blockquote>" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<blockquote><p><strong>[]bar</strong><p>baz</blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<p><strong>bar</strong></p><blockquote><p>baz</p></blockquote>" but got "foo<strong>bar</strong><blockquote><p>baz</p></blockquote>" +PASS [["delete",""]] "foo<blockquote><p><strong>[]bar</strong><p>baz</blockquote>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<blockquote><p><strong>[]bar</strong><p>baz</blockquote>" queryCommandState("delete") before +PASS [["delete",""]] "foo<blockquote><p><strong>[]bar</strong><p>baz</blockquote>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<blockquote><p><strong>[]bar</strong><p>baz</blockquote>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<blockquote><p><strong>[]bar</strong><p>baz</blockquote>" queryCommandState("delete") after +PASS [["delete",""]] "foo<blockquote><p><strong>[]bar</strong><p>baz</blockquote>" queryCommandValue("delete") after +PASS [["delete",""]] "foo<blockquote><p><span>[]bar</span><p>baz</blockquote>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<blockquote><p><span>[]bar</span><p>baz</blockquote>" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<blockquote><p><span>[]bar</span><p>baz</blockquote>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<p><span>bar</span></p><blockquote><p>baz</p></blockquote>" but got "foobar<blockquote><p>baz</p></blockquote>" +PASS [["delete",""]] "foo<blockquote><p><span>[]bar</span><p>baz</blockquote>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<blockquote><p><span>[]bar</span><p>baz</blockquote>" queryCommandState("delete") before +PASS [["delete",""]] "foo<blockquote><p><span>[]bar</span><p>baz</blockquote>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<blockquote><p><span>[]bar</span><p>baz</blockquote>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<blockquote><p><span>[]bar</span><p>baz</blockquote>" queryCommandState("delete") after +PASS [["delete",""]] "foo<blockquote><p><span>[]bar</span><p>baz</blockquote>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<blockquote><div>bar</div></blockquote><p>extra</p>" but got "foobar<p>extra</p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<blockquote><p>bar</p></blockquote><p>extra</p>" but got "foobar<p>extra</p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote><ol><li>[]bar</ol></blockquote><p>extra" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<blockquote>bar<div>baz</div>quz</blockquote><p>extra</p>" but got "foo<blockquote>barbaz<br>quz</blockquote><p>extra</p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<blockquote>bar<p>baz</p>quz</blockquote><p>extra</p>" but got "foo<blockquote>barbaz<br>quz</blockquote><p>extra</p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<blockquote>bar<ol><li>[]baz</ol>quz</blockquote><p>extra" queryCommandValue("delete") after +PASS [["delete",""]] "foo<blockquote><ol><li>bar</li><ol><li>[]baz</ol><li>quz</ol></blockquote><p>extra": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<blockquote><ol><li>bar</li><ol><li>[]baz</ol><li>quz</ol></blockquote><p>extra" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<blockquote><ol><li>bar</li><ol><li>[]baz</ol><li>quz</ol></blockquote><p>extra" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<blockquote><ol><li>bar</li><li>baz</li><li>quz</li></ol></blockquote><p>extra</p>" but got "foo<blockquote><ol><li>barbaz</li><li>quz</li></ol></blockquote><p>extra</p>" +PASS [["delete",""]] "foo<blockquote><ol><li>bar</li><ol><li>[]baz</ol><li>quz</ol></blockquote><p>extra" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<blockquote><ol><li>bar</li><ol><li>[]baz</ol><li>quz</ol></blockquote><p>extra" queryCommandState("delete") before +PASS [["delete",""]] "foo<blockquote><ol><li>bar</li><ol><li>[]baz</ol><li>quz</ol></blockquote><p>extra" queryCommandValue("delete") before +PASS [["delete",""]] "foo<blockquote><ol><li>bar</li><ol><li>[]baz</ol><li>quz</ol></blockquote><p>extra" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<blockquote><ol><li>bar</li><ol><li>[]baz</ol><li>quz</ol></blockquote><p>extra" queryCommandState("delete") after +PASS [["delete",""]] "foo<blockquote><ol><li>bar</li><ol><li>[]baz</ol><li>quz</ol></blockquote><p>extra" queryCommandValue("delete") after +PASS [["delete",""]] "foo<span></span>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<span></span>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo<span></span>[]bar" compare innerHTML +PASS [["delete",""]] "foo<span></span>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<span></span>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foo<span></span>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo<span></span>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<span></span>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foo<span></span>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo<span><span></span></span>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<span><span></span></span>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo<span><span></span></span>[]bar" compare innerHTML +PASS [["delete",""]] "foo<span><span></span></span>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<span><span></span></span>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foo<span><span></span></span>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo<span><span></span></span>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<span><span></span></span>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foo<span><span></span></span>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo<quasit></quasit>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<quasit></quasit>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo<quasit></quasit>[]bar" compare innerHTML +PASS [["delete",""]] "foo<quasit></quasit>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<quasit></quasit>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foo<quasit></quasit>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo<quasit></quasit>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<quasit></quasit>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foo<quasit></quasit>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo<br><span></span>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<br><span></span>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo<br><span></span>[]bar" compare innerHTML +PASS [["delete",""]] "foo<br><span></span>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<br><span></span>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "foo<br><span></span>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo<br><span></span>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<br><span></span>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "foo<br><span></span>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<span>foo<span></span></span>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span>foo<span></span></span>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<span>foo<span></span></span>[]bar" compare innerHTML +PASS [["delete",""]] "<span>foo<span></span></span>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span>foo<span></span></span>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<span>foo<span></span></span>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<span>foo<span></span></span>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span>foo<span></span></span>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<span>foo<span></span></span>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo<span></span><span>[]bar</span>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<span></span><span>[]bar</span>" checks for modifications to non-editable content +PASS [["delete",""]] "foo<span></span><span>[]bar</span>" compare innerHTML +PASS [["delete",""]] "foo<span></span><span>[]bar</span>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<span></span><span>[]bar</span>" queryCommandState("delete") before +PASS [["delete",""]] "foo<span></span><span>[]bar</span>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<span></span><span>[]bar</span>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<span></span><span>[]bar</span>" queryCommandState("delete") after +PASS [["delete",""]] "foo<span></span><span>[]bar</span>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p>[]bar</div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><p><!--abc-->[]bar</div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><div><!--abc--><p>[]bar</div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<div><!--abc--><div><p>[]bar</div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<!--abc--><div><div><p>[]bar</div></div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div><div><p>foo</div></div>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div><div><p>foo</div></div>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<div><div><p>foo</div></div>[]bar" compare innerHTML +PASS [["delete",""]] "<div><div><p>foo</div></div>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div><div><p>foo</div></div>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<div><div><p>foo</div></div>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<div><div><p>foo</div></div>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div><div><p>foo</div></div>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<div><div><p>foo</div></div>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<div><div><p>foo</div></div><!--abc-->[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div><div><p>foo</div></div><!--abc-->[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<div><div><p>foo</div></div><!--abc-->[]bar" compare innerHTML +PASS [["delete",""]] "<div><div><p>foo</div></div><!--abc-->[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div><div><p>foo</div></div><!--abc-->[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<div><div><p>foo</div></div><!--abc-->[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<div><div><p>foo</div></div><!--abc-->[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div><div><p>foo</div></div><!--abc-->[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<div><div><p>foo</div></div><!--abc-->[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<div><div><p>foo</div><!--abc--></div>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div><div><p>foo</div><!--abc--></div>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<div><div><p>foo</div><!--abc--></div>[]bar" compare innerHTML +PASS [["delete",""]] "<div><div><p>foo</div><!--abc--></div>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div><div><p>foo</div><!--abc--></div>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<div><div><p>foo</div><!--abc--></div>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<div><div><p>foo</div><!--abc--></div>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div><div><p>foo</div><!--abc--></div>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<div><div><p>foo</div><!--abc--></div>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<div><div><p>foo</p><!--abc--></div></div>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div><div><p>foo</p><!--abc--></div></div>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<div><div><p>foo</p><!--abc--></div></div>[]bar" compare innerHTML +PASS [["delete",""]] "<div><div><p>foo</p><!--abc--></div></div>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div><div><p>foo</p><!--abc--></div></div>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<div><div><p>foo</p><!--abc--></div></div>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<div><div><p>foo</p><!--abc--></div></div>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div><div><p>foo</p><!--abc--></div></div>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<div><div><p>foo</p><!--abc--></div></div>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<div><div><p>foo<!--abc--></div></div>[]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div><div><p>foo<!--abc--></div></div>[]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<div><div><p>foo<!--abc--></div></div>[]bar" compare innerHTML +PASS [["delete",""]] "<div><div><p>foo<!--abc--></div></div>[]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div><div><p>foo<!--abc--></div></div>[]bar" queryCommandState("delete") before +PASS [["delete",""]] "<div><div><p>foo<!--abc--></div></div>[]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<div><div><p>foo<!--abc--></div></div>[]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div><div><p>foo<!--abc--></div></div>[]bar" queryCommandState("delete") after +PASS [["delete",""]] "<div><div><p>foo<!--abc--></div></div>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo<!--abc--></p></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p><!--abc--></div></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div><!--abc--></div><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><!--abc--><div><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><!--abc--><div><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><span style=\"color:rgb(0, 0, 255)\">foo</span>bar</p>" but got "<p style=\"color:rgb(0, 0, 255)\">foobar</p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><font color=\"#0000ff\">foo</font>bar</p>" but got "<p style=\"color:rgb(0, 0, 255)\">foobar</p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><span style=\"color:rgb(0, 0, 255)\">foo</span>bar</p>" but got "<p style=\"color:rgb(0, 0, 255)\">foobar</p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><font color=\"#0000ff\">foo</font>bar</p>" but got "<p style=\"color:rgb(0, 0, 255)\">foobar</p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p style=\"color:rgb(0, 0, 255)\">foo<font color=\"#a52a2a\">bar</font></p>" but got "<p style=\"color:rgb(0, 0, 255)\">foo<span style=\"color:rgb(165, 42, 42)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p style=\"color:rgb(0, 0, 255)\">foo<font color=\"#a52a2a\">bar</font></p>" but got "<p style=\"color:rgb(0, 0, 255)\">foo<span style=\"color:rgb(165, 42, 42)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" compare innerHTML +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" compare innerHTML +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<p style=color:rgba(0,0,255,1)>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" compare innerHTML +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" compare innerHTML +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:transparent>foo<p style=color:rgba(0,0,0,0)>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<font color=\"#a52a2a\">bar</font></p>" but got "<p>foo<span style=\"color:rgb(165, 42, 42)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<font color=\"#a52a2a\">bar</font></p>" but got "<p>foo<span style=\"color:rgb(165, 42, 42)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><font color=\"blue\">foo</font><font color=\"brown\">bar</font></p>" but got "<p><font color=\"blue\">foo</font><span style=\"color:rgb(165, 42, 42)\">bar</span></p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><font color=\"blue\">foo</font><font color=\"brown\">bar</font></p>" but got "<p><font color=\"blue\">foo</font><span style=\"color:rgb(165, 42, 42)\">bar</span></p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><font color=blue>foo</font><p><font color=brown>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<font color=\"brown\">bar</font></p>" but got "<p>foo<span style=\"color:rgb(165, 42, 42)\">bar</span></p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<font color=\"brown\">bar</font></p>" but got "<p>foo<span style=\"color:rgb(165, 42, 42)\">bar</span></p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><font color=brown>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=color:blue>foo</font><p><span style=color:brown>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=color:brown>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p style=\"background-color:rgb(0, 255, 255)\">foobar</p>" but got "<p style=\"background-color:rgb(0, 255, 255)\">foo<span style=\"background-color:rgb(210, 180, 140)\">bar</span></p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p style=\"background-color:rgb(0, 255, 255)\">foobar</p>" but got "<p style=\"background-color:rgb(0, 255, 255)\">foo<span style=\"background-color:rgb(210, 180, 140)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p style=\"background-color:rgb(0, 255, 255)\">foobar</p>" but got "<p style=\"background-color:rgb(0, 255, 255)\">foo<span style=\"background-color:rgb(210, 180, 140)\">bar</span></p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p style=\"background-color:rgb(0, 255, 255)\">foobar</p>" but got "<p style=\"background-color:rgb(0, 255, 255)\">foo<span style=\"background-color:rgb(210, 180, 140)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=background-color:aqua>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foobar</p>" but got "<p>foo<span style=\"background-color:rgb(210, 180, 140)\">bar</span></p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foobar</p>" but got "<p>foo<span style=\"background-color:rgb(210, 180, 140)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foobar</p>" but got "<p>foo<span style=\"background-color:rgb(210, 180, 140)\">bar</span></p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foobar</p>" but got "<p>foo<span style=\"background-color:rgb(210, 180, 140)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=background-color:tan>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><span style=background-color:aqua>foo</font><p><span style=background-color:tan>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><span style=background-color:tan>[]bar</font>" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><span style=\"text-decoration:underline\">foo</span>bar</p>" but got "<p style=\"text-decoration:underline\">foobar</p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><u>foo</u>bar</p>" but got "<p style=\"text-decoration:underline\">foobar</p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><span style=\"text-decoration:underline\">foo</span>bar</p>" but got "<p style=\"text-decoration:underline\">foobar</p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><u>foo</u>bar</p>" but got "<p style=\"text-decoration:underline\">foobar</p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><span style=\"text-decoration:underline\">foo</span><span style=\"text-decoration:line-through\">bar</span></p>" but got "<p style=\"text-decoration:underline\">foo<span style=\"text-decoration-line:line-through\">bar</span></p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><u>foo</u><s>bar</s></p>" but got "<p style=\"text-decoration:underline\">foo<span style=\"text-decoration-line:line-through\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><span style=\"text-decoration:underline\">foo</span><span style=\"text-decoration:line-through\">bar</span></p>" but got "<p style=\"text-decoration:underline\">foo<span style=\"text-decoration-line:line-through\">bar</span></p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><u>foo</u><s>bar</s></p>" but got "<p style=\"text-decoration:underline\">foo<span style=\"text-decoration-line:line-through\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<span style=\"text-decoration:line-through\">bar</span></p>" but got "<p>foo<span style=\"text-decoration-line:line-through\">bar</span></p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<s>bar</s></p>" but got "<p>foo<span style=\"text-decoration-line:line-through\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<span style=\"text-decoration:line-through\">bar</span></p>" but got "<p>foo<span style=\"text-decoration-line:line-through\">bar</span></p>" +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<s>bar</s></p>" but got "<p>foo<span style=\"text-decoration-line:line-through\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p style=text-decoration:line-through>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p>[]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><u>foo</u><p><s>[]bar</s>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<p><s>[]bar</s>" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><span style=\"color:rgb(0, 0, 255)\">foo</span>bar</p>" but got "<p style=\"color:rgb(0, 0, 255)\">foobar</p>" +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><font color=\"#0000ff\">foo</font>bar</p>" but got "<p style=\"color:rgb(0, 0, 255)\">foobar</p>" +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p style=color:blue>foo</p>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<font color=\"#a52a2a\">bar</font>" but got "foo<span style=\"color:rgb(165, 42, 42)\">bar</span>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<font color=\"#a52a2a\">bar</font>" but got "foo<span style=\"color:rgb(165, 42, 42)\">bar</span>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "foo<p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<div><p><span style=\"color:rgb(0, 128, 0)\">foo</span>bar</p></div>" but got "<div style=\"color:rgb(0, 0, 255)\"><p style=\"color:rgb(0, 128, 0)\">foobar</p></div>" +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<div><p><font color=\"#008000\">foo</font>bar</p></div>" but got "<div style=\"color:rgb(0, 0, 255)\"><p style=\"color:rgb(0, 128, 0)\">foobar</p></div>" +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<div style=\"color:rgb(0, 0, 255)\"><p style=\"color:rgb(0, 128, 0)\">foo<font color=\"#a52a2a\">bar</font></p></div>" but got "<div style=\"color:rgb(0, 0, 255)\"><p style=\"color:rgb(0, 128, 0)\">foo<span style=\"color:rgb(165, 42, 42)\">bar</span></p></div>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<div style=\"color:rgb(0, 0, 255)\"><p style=\"color:rgb(0, 128, 0)\">foo<font color=\"#a52a2a\">bar</font></p></div>" but got "<div style=\"color:rgb(0, 0, 255)\"><p style=\"color:rgb(0, 128, 0)\">foo<span style=\"color:rgb(165, 42, 42)\">bar</span></p></div>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<div style=color:blue><p style=color:green>foo</div><p style=color:brown>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p style=\"color:rgb(0, 0, 255)\">foo<font color=\"#008000\">bar</font></p>" but got "<p style=\"color:rgb(0, 0, 255)\">foo<span style=\"color:rgb(0, 128, 0)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p style=\"color:rgb(0, 0, 255)\">foo<font color=\"#008000\">bar</font></p>" but got "<p style=\"color:rgb(0, 0, 255)\">foo<span style=\"color:rgb(0, 128, 0)\">bar</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p style=color:blue>foo<div style=color:brown><p style=color:green>[]bar" queryCommandValue("delete") after +PASS [["delete",""]] "foo[bar]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo[bar]baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo[bar]baz" compare innerHTML +PASS [["delete",""]] "foo[bar]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo[bar]baz" queryCommandState("delete") before +PASS [["delete",""]] "foo[bar]baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo[bar]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo[bar]baz" queryCommandState("delete") after +PASS [["delete",""]] "foo[bar]baz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar]</span>baz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar}</span>baz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo{<span style=color:#aBcDeF>bar</span>}baz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>[foo<span style=color:#aBcDeF>bar]</span>baz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>{foo<span style=color:#aBcDeF>bar}</span>baz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span>baz]" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>{bar</span>baz}" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz]</span>quz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>[bar]</b>baz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo<b>{bar}</b>baz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "foo{<b>bar</b>}baz" queryCommandValue("delete") after +PASS [["delete",""]] "foo<span>[bar]</span>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<span>[bar]</span>baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<span>[bar]</span>baz" compare innerHTML +PASS [["delete",""]] "foo<span>[bar]</span>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<span>[bar]</span>baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<span>[bar]</span>baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<span>[bar]</span>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<span>[bar]</span>baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<span>[bar]</span>baz" queryCommandValue("delete") after +PASS [["delete",""]] "foo<span>{bar}</span>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<span>{bar}</span>baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo<span>{bar}</span>baz" compare innerHTML +PASS [["delete",""]] "foo<span>{bar}</span>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<span>{bar}</span>baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<span>{bar}</span>baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<span>{bar}</span>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<span>{bar}</span>baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<span>{bar}</span>baz" queryCommandValue("delete") after +PASS [["delete",""]] "foo{<span>bar</span>}baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo{<span>bar</span>}baz" checks for modifications to non-editable content +PASS [["delete",""]] "foo{<span>bar</span>}baz" compare innerHTML +PASS [["delete",""]] "foo{<span>bar</span>}baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo{<span>bar</span>}baz" queryCommandState("delete") before +PASS [["delete",""]] "foo{<span>bar</span>}baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo{<span>bar</span>}baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo{<span>bar</span>}baz" queryCommandState("delete") after +PASS [["delete",""]] "foo{<span>bar</span>}baz" queryCommandValue("delete") after +PASS [["delete",""]] "<b>foo[bar</b><i>baz]quz</i>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<b>foo[bar</b><i>baz]quz</i>" checks for modifications to non-editable content +PASS [["delete",""]] "<b>foo[bar</b><i>baz]quz</i>" compare innerHTML +PASS [["delete",""]] "<b>foo[bar</b><i>baz]quz</i>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<b>foo[bar</b><i>baz]quz</i>" queryCommandState("delete") before +PASS [["delete",""]] "<b>foo[bar</b><i>baz]quz</i>" queryCommandValue("delete") before +PASS [["delete",""]] "<b>foo[bar</b><i>baz]quz</i>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<b>foo[bar</b><i>baz]quz</i>" queryCommandState("delete") after +PASS [["delete",""]] "<b>foo[bar</b><i>baz]quz</i>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo</p><p>[bar]</p><p>baz</p>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo</p><p>[bar]</p><p>baz</p>" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo</p><p>[bar]</p><p>baz</p>" compare innerHTML +PASS [["delete",""]] "<p>foo</p><p>[bar]</p><p>baz</p>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo</p><p>[bar]</p><p>baz</p>" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo</p><p>[bar]</p><p>baz</p>" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo</p><p>[bar]</p><p>baz</p>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo</p><p>[bar]</p><p>baz</p>" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo</p><p>[bar]</p><p>baz</p>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo</p><p>{bar}</p><p>baz</p>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo</p><p>{bar}</p><p>baz</p>" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo</p><p>{bar}</p><p>baz</p>" compare innerHTML +PASS [["delete",""]] "<p>foo</p><p>{bar}</p><p>baz</p>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo</p><p>{bar}</p><p>baz</p>" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo</p><p>{bar}</p><p>baz</p>" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo</p><p>{bar}</p><p>baz</p>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo</p><p>{bar}</p><p>baz</p>" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo</p><p>{bar}</p><p>baz</p>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo</p><p>{bar</p>}<p>baz</p>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo</p><p>{bar</p>}<p>baz</p>" checks for modifications to non-editable content +FAIL [["delete",""]] "<p>foo</p><p>{bar</p>}<p>baz</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo</p><p><br></p><p>baz</p>" but got "<p>foo</p><p>baz<br></p>" +PASS [["delete",""]] "<p>foo</p><p>{bar</p>}<p>baz</p>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo</p><p>{bar</p>}<p>baz</p>" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo</p><p>{bar</p>}<p>baz</p>" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo</p><p>{bar</p>}<p>baz</p>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo</p><p>{bar</p>}<p>baz</p>" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo</p><p>{bar</p>}<p>baz</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo</p><br><p>baz</p>" but got "<p>foo</p><p><br></p><p>baz</p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo</p><br><p>baz</p>" but got "<p>foo</p><p><br></p><p>baz</p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar}</p><p>baz</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo</p><p>baz</p>" but got "<p>foo</p><p>baz<br></p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo</p><p>baz</p>" but got "<p>foo</p><p>baz<br></p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo</p>{<p>bar</p>}<p>baz</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p>baz]quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<div>baz]quz</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo[bar<h1>baz]quz</h1>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo[bar<h1>baz]quz</h1>" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo[bar<h1>baz]quz</h1>" compare innerHTML +PASS [["delete",""]] "<p>foo[bar<h1>baz]quz</h1>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo[bar<h1>baz]quz</h1>" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo[bar<h1>baz]quz</h1>" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo[bar<h1>baz]quz</h1>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo[bar<h1>baz]quz</h1>" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo[bar<h1>baz]quz</h1>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div>foo[bar</div><p>baz]quz" queryCommandValue("delete") after +PASS [["delete",""]] "<blockquote>foo[bar</blockquote><pre>baz]quz</pre>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<blockquote>foo[bar</blockquote><pre>baz]quz</pre>" checks for modifications to non-editable content +PASS [["delete",""]] "<blockquote>foo[bar</blockquote><pre>baz]quz</pre>" compare innerHTML +PASS [["delete",""]] "<blockquote>foo[bar</blockquote><pre>baz]quz</pre>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<blockquote>foo[bar</blockquote><pre>baz]quz</pre>" queryCommandState("delete") before +PASS [["delete",""]] "<blockquote>foo[bar</blockquote><pre>baz]quz</pre>" queryCommandValue("delete") before +PASS [["delete",""]] "<blockquote>foo[bar</blockquote><pre>baz]quz</pre>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<blockquote>foo[bar</blockquote><pre>baz]quz</pre>" queryCommandState("delete") after +PASS [["delete",""]] "<blockquote>foo[bar</blockquote><pre>baz]quz</pre>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p><b>foo[bar</b><p>baz]quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo[bar</div><p>baz]quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<font color=\"#0000ff\">quz</font></p>" but got "<p>foo<span style=\"color:rgb(0, 0, 255)\">quz</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" compare innerHTML +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("delete") after +PASS [["stylewithcss","true"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<font color=\"#0000ff\">quz</font></p>" but got "<p>foo<span style=\"color:rgb(0, 0, 255)\">quz</span></p>" +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("defaultparagraphseparator") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("defaultparagraphseparator") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandState("delete") after +PASS [["stylewithcss","false"],["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p style=color:blue>baz]quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[bar<p><b>baz]quz</b>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<div><p>foo</p><p><br></p></div>" but got "<p>foo</p><p><br></p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<div><p>foo</p><p><br></p></div>" but got "<p>foo</p><p><br></p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<div><p>foo<p>[bar<p>baz]</div>" queryCommandValue("delete") after +PASS [["delete",""]] "foo[<br>]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo[<br>]bar" checks for modifications to non-editable content +PASS [["delete",""]] "foo[<br>]bar" compare innerHTML +PASS [["delete",""]] "foo[<br>]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo[<br>]bar" queryCommandState("delete") before +PASS [["delete",""]] "foo[<br>]bar" queryCommandValue("delete") before +PASS [["delete",""]] "foo[<br>]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo[<br>]bar" queryCommandState("delete") after +PASS [["delete",""]] "foo[<br>]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foobar<br>baz</p>" but got "<p>foobar</p><p>baz</p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foobar<br>baz</p>" but got "<p>foobar</p><p>baz</p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo[</p><p>]bar<br>baz</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>}bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>}bar</p>" queryCommandValue("delete") after +PASS [["delete",""]] "foo[<p>]bar<br>baz</p>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo[<p>]bar<br>baz</p>" checks for modifications to non-editable content +PASS [["delete",""]] "foo[<p>]bar<br>baz</p>" compare innerHTML +PASS [["delete",""]] "foo[<p>]bar<br>baz</p>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo[<p>]bar<br>baz</p>" queryCommandState("delete") before +PASS [["delete",""]] "foo[<p>]bar<br>baz</p>" queryCommandValue("delete") before +PASS [["delete",""]] "foo[<p>]bar<br>baz</p>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo[<p>]bar<br>baz</p>" queryCommandState("delete") after +PASS [["delete",""]] "foo[<p>]bar<br>baz</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<p>]bar</p>baz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar</p>}baz" queryCommandValue("delete") after +PASS [["delete",""]] "foo<p>{bar</p>}baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<p>{bar</p>}baz" checks for modifications to non-editable content +FAIL [["delete",""]] "foo<p>{bar</p>}baz" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<p>baz</p>" but got "foo<p>baz<br></p>" +PASS [["delete",""]] "foo<p>{bar</p>}baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<p>{bar</p>}baz" queryCommandState("delete") before +PASS [["delete",""]] "foo<p>{bar</p>}baz" queryCommandValue("delete") before +PASS [["delete",""]] "foo<p>{bar</p>}baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<p>{bar</p>}baz" queryCommandState("delete") after +PASS [["delete",""]] "foo<p>{bar</p>}baz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo{<p>bar}</p>baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo[</p>]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo[</p>]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo[</p>]bar" compare innerHTML +PASS [["delete",""]] "<p>foo[</p>]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo[</p>]bar" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo[</p>]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo[</p>]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo[</p>]bar" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo[</p>]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo{</p>}bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo{</p>}bar" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo{</p>}bar" compare innerHTML +PASS [["delete",""]] "<p>foo{</p>}bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo{</p>}bar" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo{</p>}bar" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo{</p>}bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo{</p>}bar" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo{</p>}bar" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo[</p>]bar<br>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo[</p>]bar<br>baz" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo[</p>]bar<br>baz" compare innerHTML +PASS [["delete",""]] "<p>foo[</p>]bar<br>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo[</p>]bar<br>baz" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo[</p>]bar<br>baz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo[</p>]bar<br>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo[</p>]bar<br>baz" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo[</p>]bar<br>baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo[</p>]bar<p>baz</p>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo[</p>]bar<p>baz</p>" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo[</p>]bar<p>baz</p>" compare innerHTML +PASS [["delete",""]] "<p>foo[</p>]bar<p>baz</p>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo[</p>]bar<p>baz</p>" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo[</p>]bar<p>baz</p>" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo[</p>]bar<p>baz</p>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo[</p>]bar<p>baz</p>" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo[</p>]bar<p>baz</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div><p>foo[</p></div>]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div><p>foo[</p></div>]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<div><p>foo[</p></div>]bar" compare innerHTML +PASS [["delete",""]] "<div><p>foo[</p></div>]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div><p>foo[</p></div>]bar" queryCommandState("delete") before +PASS [["delete",""]] "<div><p>foo[</p></div>]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<div><p>foo[</p></div>]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div><p>foo[</p></div>]bar" queryCommandState("delete") after +PASS [["delete",""]] "<div><p>foo[</p></div>]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo[<div><p>]bar</p>baz</div>" queryCommandValue("delete") after +PASS [["delete",""]] "foo[<div>]bar<p>baz</p></div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo[<div>]bar<p>baz</p></div>" checks for modifications to non-editable content +PASS [["delete",""]] "foo[<div>]bar<p>baz</p></div>" compare innerHTML +PASS [["delete",""]] "foo[<div>]bar<p>baz</p></div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo[<div>]bar<p>baz</p></div>" queryCommandState("delete") before +PASS [["delete",""]] "foo[<div>]bar<p>baz</p></div>" queryCommandValue("delete") before +PASS [["delete",""]] "foo[<div>]bar<p>baz</p></div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo[<div>]bar<p>baz</p></div>" queryCommandState("delete") after +PASS [["delete",""]] "foo[<div>]bar<p>baz</p></div>" queryCommandValue("delete") after +PASS [["delete",""]] "<div><p>foo</p>bar[</div>]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div><p>foo</p>bar[</div>]baz" checks for modifications to non-editable content +PASS [["delete",""]] "<div><p>foo</p>bar[</div>]baz" compare innerHTML +PASS [["delete",""]] "<div><p>foo</p>bar[</div>]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div><p>foo</p>bar[</div>]baz" queryCommandState("delete") before +PASS [["delete",""]] "<div><p>foo</p>bar[</div>]baz" queryCommandValue("delete") before +PASS [["delete",""]] "<div><p>foo</p>bar[</div>]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div><p>foo</p>bar[</div>]baz" queryCommandState("delete") after +PASS [["delete",""]] "<div><p>foo</p>bar[</div>]baz" queryCommandValue("delete") after +PASS [["delete",""]] "<div>foo<p>bar[</p></div>]baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div>foo<p>bar[</p></div>]baz" checks for modifications to non-editable content +PASS [["delete",""]] "<div>foo<p>bar[</p></div>]baz" compare innerHTML +PASS [["delete",""]] "<div>foo<p>bar[</p></div>]baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div>foo<p>bar[</p></div>]baz" queryCommandState("delete") before +PASS [["delete",""]] "<div>foo<p>bar[</p></div>]baz" queryCommandValue("delete") before +PASS [["delete",""]] "<div>foo<p>bar[</p></div>]baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div>foo<p>bar[</p></div>]baz" queryCommandState("delete") after +PASS [["delete",""]] "<div>foo<p>bar[</p></div>]baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<br>{</p>]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<br>{</p>]bar" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo<br>{</p>]bar" compare innerHTML +PASS [["delete",""]] "<p>foo<br>{</p>]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<br>{</p>]bar" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<br>{</p>]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<br>{</p>]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<br>{</p>]bar" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<br>{</p>]bar" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<br><br>{</p>]bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<br><br>{</p>]bar" checks for modifications to non-editable content +FAIL [["delete",""]] "<p>foo<br><br>{</p>]bar" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<br>bar</p>" but got "<p>foo<br>bar<br></p>" +PASS [["delete",""]] "<p>foo<br><br>{</p>]bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<br><br>{</p>]bar" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<br><br>{</p>]bar" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<br><br>{</p>]bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<br><br>{</p>]bar" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<br><br>{</p>]bar" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br>{<p>]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<br>bar" but got "foo<br><p>bar</p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "foo<br>bar" but got "foo<br><p>bar</p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "foo<br><br>{<p>]bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br>{</p><p>}bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<br>bar</p>" but got "<p>foo<br>bar<br></p>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo<br>bar</p>" but got "<p>foo<br>bar<br></p>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<br><br>{</p><p>}bar</p>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>" checks for modifications to non-editable content +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>" compare innerHTML +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" checks for modifications to non-editable content +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" compare innerHTML +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" checks for modifications to non-editable content +FAIL [["delete",""]] "<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<table><tbody><tr><th>fo</th><th><br></th><th>az</th></tr><tr><td>quz</td><td>qoz</td><td>qiz</td></tr></tbody></table>" but got "<table><tbody><tr><th>fo</th><th></th><th>az</th></tr><tr><td>quz</td><td>qoz</td><td>qiz</td></tr></tbody></table>" +PASS [["delete",""]] "<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>" checks for modifications to non-editable content +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>" compare innerHTML +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>" checks for modifications to non-editable content +FAIL [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<table><tbody><tr><th><br></th><th><br></th><th><br></th></tr><tr><td>quz</td><td>qoz</td><td>qiz</td></tr></tbody></table>" but got "<table><tbody><tr><th><br></th><th></th><th></th></tr><tr><td>quz</td><td>qoz</td><td>qiz</td></tr></tbody></table>" +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>" checks for modifications to non-editable content +FAIL [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<table><tbody><tr><th><br></th><th><br></th><th><br></th></tr><tr><td><br></td><td><br></td><td><br></td></tr></tbody></table>" but got "<br>" +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>" queryCommandValue("delete") after +PASS [["delete",""]] "{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}": execCommand("delete", false, "") return value +PASS [["delete",""]] "{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}" checks for modifications to non-editable content +PASS [["delete",""]] "{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}" compare innerHTML +PASS [["delete",""]] "{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}" queryCommandIndeterm("delete") before +PASS [["delete",""]] "{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}" queryCommandState("delete") before +PASS [["delete",""]] "{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}" queryCommandValue("delete") before +PASS [["delete",""]] "{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}" queryCommandIndeterm("delete") after +PASS [["delete",""]] "{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}" queryCommandState("delete") after +PASS [["delete",""]] "{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}" queryCommandValue("delete") after +PASS [["delete",""]] "<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>" checks for modifications to non-editable content +FAIL [["delete",""]] "<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<table><tbody><tr><td>foo</td><td>ba</td></tr><tr><td><br></td><td><br></td></tr><tr><td>oz</td><td>qiz</td></tr></tbody></table>" but got "<table><tbody><tr><td>foo</td><td>ba</td></tr><tr><td>oz</td><td>qiz</td></tr></tbody></table>" +PASS [["delete",""]] "<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>" queryCommandState("delete") before +PASS [["delete",""]] "<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>" queryCommandValue("delete") before +PASS [["delete",""]] "<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>" queryCommandState("delete") after +PASS [["delete",""]] "<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>fo[o<table><tr><td>b]ar</table><p>baz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>fo[o<table><tr><td>b]ar</table><p>baz" checks for modifications to non-editable content +PASS [["delete",""]] "<p>fo[o<table><tr><td>b]ar</table><p>baz" compare innerHTML +PASS [["delete",""]] "<p>fo[o<table><tr><td>b]ar</table><p>baz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>fo[o<table><tr><td>b]ar</table><p>baz" queryCommandState("delete") before +PASS [["delete",""]] "<p>fo[o<table><tr><td>b]ar</table><p>baz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>fo[o<table><tr><td>b]ar</table><p>baz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>fo[o<table><tr><td>b]ar</table><p>baz" queryCommandState("delete") after +PASS [["delete",""]] "<p>fo[o<table><tr><td>b]ar</table><p>baz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>ba[r</table><p>b]az": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<table><tr><td>ba[r</table><p>b]az" checks for modifications to non-editable content +FAIL [["delete",""]] "<p>foo<table><tr><td>ba[r</table><p>b]az" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p>foo</p><table><tbody><tr><td>ba</td></tr></tbody></table><p>az</p>" but got "<p>foo</p><table><tbody><tr><td>baaz</td></tr></tbody></table>" +PASS [["delete",""]] "<p>foo<table><tr><td>ba[r</table><p>b]az" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>ba[r</table><p>b]az" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>ba[r</table><p>b]az" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<table><tr><td>ba[r</table><p>b]az" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>ba[r</table><p>b]az" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<table><tr><td>ba[r</table><p>b]az" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<table><tr><td>bar</table><p>b]az" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<ol><li>ba[r<li>b]az</ol><p>quz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<ol><li>ba[r<li>b]az</ol><p>quz" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo<ol><li>ba[r<li>b]az</ol><p>quz" compare innerHTML +PASS [["delete",""]] "<p>foo<ol><li>ba[r<li>b]az</ol><p>quz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<ol><li>ba[r<li>b]az</ol><p>quz" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<ol><li>ba[r<li>b]az</ol><p>quz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<ol><li>ba[r<li>b]az</ol><p>quz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<ol><li>ba[r<li>b]az</ol><p>quz" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<ol><li>ba[r<li>b]az</ol><p>quz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>foo<ol><li>bar<li>[baz]</ol><p>quz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>foo<ol><li>bar<li>[baz]</ol><p>quz" checks for modifications to non-editable content +PASS [["delete",""]] "<p>foo<ol><li>bar<li>[baz]</ol><p>quz" compare innerHTML +PASS [["delete",""]] "<p>foo<ol><li>bar<li>[baz]</ol><p>quz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>foo<ol><li>bar<li>[baz]</ol><p>quz" queryCommandState("delete") before +PASS [["delete",""]] "<p>foo<ol><li>bar<li>[baz]</ol><p>quz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>foo<ol><li>bar<li>[baz]</ol><p>quz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>foo<ol><li>bar<li>[baz]</ol><p>quz" queryCommandState("delete") after +PASS [["delete",""]] "<p>foo<ol><li>bar<li>[baz]</ol><p>quz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz" checks for modifications to non-editable content +PASS [["delete",""]] "<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz" compare innerHTML +PASS [["delete",""]] "<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz" queryCommandState("delete") before +PASS [["delete",""]] "<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz" queryCommandState("delete") after +PASS [["delete",""]] "<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz" queryCommandValue("delete") after +PASS [["delete",""]] "<p>fo[o<ol><li>bar<li>b]az</ol><p>quz": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>fo[o<ol><li>bar<li>b]az</ol><p>quz" checks for modifications to non-editable content +PASS [["delete",""]] "<p>fo[o<ol><li>bar<li>b]az</ol><p>quz" compare innerHTML +PASS [["delete",""]] "<p>fo[o<ol><li>bar<li>b]az</ol><p>quz" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>fo[o<ol><li>bar<li>b]az</ol><p>quz" queryCommandState("delete") before +PASS [["delete",""]] "<p>fo[o<ol><li>bar<li>b]az</ol><p>quz" queryCommandValue("delete") before +PASS [["delete",""]] "<p>fo[o<ol><li>bar<li>b]az</ol><p>quz" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>fo[o<ol><li>bar<li>b]az</ol><p>quz" queryCommandState("delete") after +PASS [["delete",""]] "<p>fo[o<ol><li>bar<li>b]az</ol><p>quz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>fo[o</ol><ol><li>b]ar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>fo[o</ol><ol><li>b]ar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>fo[o</ol><ol><li>b]ar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foar</li></ol>" but got "<ol><li>fo</li><li>ar</li></ol>" +PASS [["delete",""]] "<ol><li>fo[o</ol><ol><li>b]ar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>fo[o</ol><ol><li>b]ar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>fo[o</ol><ol><li>b]ar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>fo[o</ol><ol><li>b]ar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>fo[o</ol><ol><li>b]ar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>fo[o</ol><ol><li>b]ar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>fo[o</ol><ul><li>b]ar</ul>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>fo[o</ol><ul><li>b]ar</ul>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>fo[o</ol><ul><li>b]ar</ul>" compare innerHTML +PASS [["delete",""]] "<ol><li>fo[o</ol><ul><li>b]ar</ul>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>fo[o</ol><ul><li>b]ar</ul>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>fo[o</ol><ul><li>b]ar</ul>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>fo[o</ol><ul><li>b]ar</ul>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>fo[o</ol><ul><li>b]ar</ul>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>fo[o</ol><ul><li>b]ar</ul>" queryCommandValue("delete") after +PASS [["delete",""]] "foo[<ol><li>]bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo[<ol><li>]bar</ol>" checks for modifications to non-editable content +PASS [["delete",""]] "foo[<ol><li>]bar</ol>" compare innerHTML +PASS [["delete",""]] "foo[<ol><li>]bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo[<ol><li>]bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "foo[<ol><li>]bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "foo[<ol><li>]bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo[<ol><li>]bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "foo[<ol><li>]bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo[<li>]bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo[<li>]bar</ol>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo[<li>]bar</ol>" compare innerHTML +PASS [["delete",""]] "<ol><li>foo[<li>]bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo[<li>]bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo[<li>]bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo[<li>]bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo[<li>]bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo[<li>]bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "foo[<dl><dt>]bar<dd>baz</dl>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo[<dl><dt>]bar<dd>baz</dl>" checks for modifications to non-editable content +PASS [["delete",""]] "foo[<dl><dt>]bar<dd>baz</dl>" compare innerHTML +PASS [["delete",""]] "foo[<dl><dt>]bar<dd>baz</dl>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo[<dl><dt>]bar<dd>baz</dl>" queryCommandState("delete") before +PASS [["delete",""]] "foo[<dl><dt>]bar<dd>baz</dl>" queryCommandValue("delete") before +PASS [["delete",""]] "foo[<dl><dt>]bar<dd>baz</dl>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo[<dl><dt>]bar<dd>baz</dl>" queryCommandState("delete") after +PASS [["delete",""]] "foo[<dl><dt>]bar<dd>baz</dl>" queryCommandValue("delete") after +PASS [["delete",""]] "foo[<dl><dd>]bar</dl>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo[<dl><dd>]bar</dl>" checks for modifications to non-editable content +PASS [["delete",""]] "foo[<dl><dd>]bar</dl>" compare innerHTML +PASS [["delete",""]] "foo[<dl><dd>]bar</dl>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo[<dl><dd>]bar</dl>" queryCommandState("delete") before +PASS [["delete",""]] "foo[<dl><dd>]bar</dl>" queryCommandValue("delete") before +PASS [["delete",""]] "foo[<dl><dd>]bar</dl>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo[<dl><dd>]bar</dl>" queryCommandState("delete") after +PASS [["delete",""]] "foo[<dl><dd>]bar</dl>" queryCommandValue("delete") after +PASS [["delete",""]] "<dl><dt>foo[<dd>]bar</dl>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<dl><dt>foo[<dd>]bar</dl>" checks for modifications to non-editable content +PASS [["delete",""]] "<dl><dt>foo[<dd>]bar</dl>" compare innerHTML +PASS [["delete",""]] "<dl><dt>foo[<dd>]bar</dl>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<dl><dt>foo[<dd>]bar</dl>" queryCommandState("delete") before +PASS [["delete",""]] "<dl><dt>foo[<dd>]bar</dl>" queryCommandValue("delete") before +PASS [["delete",""]] "<dl><dt>foo[<dd>]bar</dl>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<dl><dt>foo[<dd>]bar</dl>" queryCommandState("delete") after +PASS [["delete",""]] "<dl><dt>foo[<dd>]bar</dl>" queryCommandValue("delete") after +PASS [["delete",""]] "<dl><dt>foo[<dt>]bar<dd>baz</dl>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<dl><dt>foo[<dt>]bar<dd>baz</dl>" checks for modifications to non-editable content +PASS [["delete",""]] "<dl><dt>foo[<dt>]bar<dd>baz</dl>" compare innerHTML +PASS [["delete",""]] "<dl><dt>foo[<dt>]bar<dd>baz</dl>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<dl><dt>foo[<dt>]bar<dd>baz</dl>" queryCommandState("delete") before +PASS [["delete",""]] "<dl><dt>foo[<dt>]bar<dd>baz</dl>" queryCommandValue("delete") before +PASS [["delete",""]] "<dl><dt>foo[<dt>]bar<dd>baz</dl>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<dl><dt>foo[<dt>]bar<dd>baz</dl>" queryCommandState("delete") after +PASS [["delete",""]] "<dl><dt>foo[<dt>]bar<dd>baz</dl>" queryCommandValue("delete") after +PASS [["delete",""]] "<dl><dt>foo<dd>bar[<dd>]baz</dl>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<dl><dt>foo<dd>bar[<dd>]baz</dl>" checks for modifications to non-editable content +PASS [["delete",""]] "<dl><dt>foo<dd>bar[<dd>]baz</dl>" compare innerHTML +PASS [["delete",""]] "<dl><dt>foo<dd>bar[<dd>]baz</dl>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<dl><dt>foo<dd>bar[<dd>]baz</dl>" queryCommandState("delete") before +PASS [["delete",""]] "<dl><dt>foo<dd>bar[<dd>]baz</dl>" queryCommandValue("delete") before +PASS [["delete",""]] "<dl><dt>foo<dd>bar[<dd>]baz</dl>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<dl><dt>foo<dd>bar[<dd>]baz</dl>" queryCommandState("delete") after +PASS [["delete",""]] "<dl><dt>foo<dd>bar[<dd>]baz</dl>" queryCommandValue("delete") after +PASS [["delete",""]] "<b>foo [ </b>bar]": execCommand("delete", false, "") return value +PASS [["delete",""]] "<b>foo [ </b>bar]" checks for modifications to non-editable content +PASS [["delete",""]] "<b>foo [ </b>bar]" compare innerHTML +PASS [["delete",""]] "<b>foo [ </b>bar]" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<b>foo [ </b>bar]" queryCommandState("delete") before +PASS [["delete",""]] "<b>foo [ </b>bar]" queryCommandValue("delete") before +PASS [["delete",""]] "<b>foo [ </b>bar]" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<b>foo [ </b>bar]" queryCommandState("delete") after +PASS [["delete",""]] "<b>foo [ </b>bar]" queryCommandValue("delete") after +PASS [["delete",""]] "foo<b> [ bar]</b>": execCommand("delete", false, "") return value +PASS [["delete",""]] "foo<b> [ bar]</b>" checks for modifications to non-editable content +PASS [["delete",""]] "foo<b> [ bar]</b>" compare innerHTML +PASS [["delete",""]] "foo<b> [ bar]</b>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "foo<b> [ bar]</b>" queryCommandState("delete") before +PASS [["delete",""]] "foo<b> [ bar]</b>" queryCommandValue("delete") before +PASS [["delete",""]] "foo<b> [ bar]</b>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "foo<b> [ bar]</b>" queryCommandState("delete") after +PASS [["delete",""]] "foo<b> [ bar]</b>" queryCommandValue("delete") after +PASS [["delete",""]] "<b>[foo ] </b>bar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<b>[foo ] </b>bar" checks for modifications to non-editable content +PASS [["delete",""]] "<b>[foo ] </b>bar" compare innerHTML +PASS [["delete",""]] "<b>[foo ] </b>bar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<b>[foo ] </b>bar" queryCommandState("delete") before +PASS [["delete",""]] "<b>[foo ] </b>bar" queryCommandValue("delete") before +PASS [["delete",""]] "<b>[foo ] </b>bar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<b>[foo ] </b>bar" queryCommandState("delete") after +PASS [["delete",""]] "<b>[foo ] </b>bar" queryCommandValue("delete") after +PASS [["delete",""]] "[foo<b> ] bar</b>": execCommand("delete", false, "") return value +PASS [["delete",""]] "[foo<b> ] bar</b>" checks for modifications to non-editable content +PASS [["delete",""]] "[foo<b> ] bar</b>" compare innerHTML +PASS [["delete",""]] "[foo<b> ] bar</b>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "[foo<b> ] bar</b>" queryCommandState("delete") before +PASS [["delete",""]] "[foo<b> ] bar</b>" queryCommandValue("delete") before +PASS [["delete",""]] "[foo<b> ] bar</b>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "[foo<b> ] bar</b>" queryCommandState("delete") after +PASS [["delete",""]] "[foo<b> ] bar</b>" queryCommandValue("delete") after +PASS [["delete",""]] "<p style=display:inline>fo[o<p style=display:inline>b]ar": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p style=display:inline>fo[o<p style=display:inline>b]ar" checks for modifications to non-editable content +PASS [["delete",""]] "<p style=display:inline>fo[o<p style=display:inline>b]ar" compare innerHTML +PASS [["delete",""]] "<p style=display:inline>fo[o<p style=display:inline>b]ar" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p style=display:inline>fo[o<p style=display:inline>b]ar" queryCommandState("delete") before +PASS [["delete",""]] "<p style=display:inline>fo[o<p style=display:inline>b]ar" queryCommandValue("delete") before +PASS [["delete",""]] "<p style=display:inline>fo[o<p style=display:inline>b]ar" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p style=display:inline>fo[o<p style=display:inline>b]ar" queryCommandState("delete") after +PASS [["delete",""]] "<p style=display:inline>fo[o<p style=display:inline>b]ar" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" checks for modifications to non-editable content +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" compare innerHTML +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" checks for modifications to non-editable content +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" compare innerHTML +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<span style=display:block>fo[o</span><span style=display:block>b]ar</span>" queryCommandValue("delete") after +PASS [["delete",""]] "<span style=display:inline-block>fo[o</span><span style=display:inline-block>b]ar</span>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span style=display:inline-block>fo[o</span><span style=display:inline-block>b]ar</span>" checks for modifications to non-editable content +PASS [["delete",""]] "<span style=display:inline-block>fo[o</span><span style=display:inline-block>b]ar</span>" compare innerHTML +PASS [["delete",""]] "<span style=display:inline-block>fo[o</span><span style=display:inline-block>b]ar</span>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span style=display:inline-block>fo[o</span><span style=display:inline-block>b]ar</span>" queryCommandState("delete") before +PASS [["delete",""]] "<span style=display:inline-block>fo[o</span><span style=display:inline-block>b]ar</span>" queryCommandValue("delete") before +PASS [["delete",""]] "<span style=display:inline-block>fo[o</span><span style=display:inline-block>b]ar</span>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span style=display:inline-block>fo[o</span><span style=display:inline-block>b]ar</span>" queryCommandState("delete") after +PASS [["delete",""]] "<span style=display:inline-block>fo[o</span><span style=display:inline-block>b]ar</span>" queryCommandValue("delete") after +PASS [["delete",""]] "<span style=display:inline-table>fo[o</span><span style=display:inline-table>b]ar</span>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span style=display:inline-table>fo[o</span><span style=display:inline-table>b]ar</span>" checks for modifications to non-editable content +PASS [["delete",""]] "<span style=display:inline-table>fo[o</span><span style=display:inline-table>b]ar</span>" compare innerHTML +PASS [["delete",""]] "<span style=display:inline-table>fo[o</span><span style=display:inline-table>b]ar</span>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span style=display:inline-table>fo[o</span><span style=display:inline-table>b]ar</span>" queryCommandState("delete") before +PASS [["delete",""]] "<span style=display:inline-table>fo[o</span><span style=display:inline-table>b]ar</span>" queryCommandValue("delete") before +PASS [["delete",""]] "<span style=display:inline-table>fo[o</span><span style=display:inline-table>b]ar</span>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span style=display:inline-table>fo[o</span><span style=display:inline-table>b]ar</span>" queryCommandState("delete") after +PASS [["delete",""]] "<span style=display:inline-table>fo[o</span><span style=display:inline-table>b]ar</span>" queryCommandValue("delete") after +PASS [["delete",""]] "<span style=display:none>fo[o</span><span style=display:none>b]ar</span>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<span style=display:none>fo[o</span><span style=display:none>b]ar</span>" checks for modifications to non-editable content +FAIL [["delete",""]] "<span style=display:none>fo[o</span><span style=display:none>b]ar</span>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<span style=\"display:none\">fo</span><span style=\"display:none\">ar</span>" but got "" +PASS [["delete",""]] "<span style=display:none>fo[o</span><span style=display:none>b]ar</span>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<span style=display:none>fo[o</span><span style=display:none>b]ar</span>" queryCommandState("delete") before +PASS [["delete",""]] "<span style=display:none>fo[o</span><span style=display:none>b]ar</span>" queryCommandValue("delete") before +PASS [["delete",""]] "<span style=display:none>fo[o</span><span style=display:none>b]ar</span>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<span style=display:none>fo[o</span><span style=display:none>b]ar</span>" queryCommandState("delete") after +PASS [["delete",""]] "<span style=display:none>fo[o</span><span style=display:none>b]ar</span>" queryCommandValue("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>": execCommand("stylewithcss", false, "true") return value +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>": execCommand("delete", false, "") return value +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" checks for modifications to non-editable content +FAIL [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<quasit style=\"display:block\">foar</quasit>" but got "<quasit style=\"display:block\">fo<span style=\"text-align:inherit\">ar</span></quasit>" +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandState("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandState("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandIndeterm("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandState("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandValue("delete") before +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandIndeterm("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandState("delete") after +PASS [["stylewithcss","true"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandValue("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>": execCommand("stylewithcss", false, "false") return value +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>": execCommand("delete", false, "") return value +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" checks for modifications to non-editable content +FAIL [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<quasit style=\"display:block\">foar</quasit>" but got "<quasit style=\"display:block\">fo<span style=\"text-align:inherit\">ar</span></quasit>" +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandIndeterm("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandState("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandValue("stylewithcss") before +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandIndeterm("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandState("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandValue("stylewithcss") after +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandIndeterm("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandState("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandValue("delete") before +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandIndeterm("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandState("delete") after +PASS [["stylewithcss","false"],["delete",""]] "<quasit style=display:block>fo[o</quasit><quasit style=display:block>b]ar</quasit>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol><li>bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo</ol>{}<br><ol><li>bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>bar</li></ol>" but got "<ol><li>foo</li></ol><ol><li>bar</li></ol>" +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>bar</li></ol>" but got "<ol><li>foo</li></ol><ol><li>bar</li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>bar</li></ol>" but got "<ol><li>foo</li></ol><ol><li>bar</li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li><p>foo</p></li><li>bar</li></ol>" but got "<ol><li><p>foo</p></li></ol><ol><li>bar</li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li><p>foo</p></li><li>bar</li></ol>" but got "<ol><li><p>foo</p></li></ol><ol><li>bar</li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo</ol><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol><li>bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol><li>bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol id=\"a\"><li>foo</li><li>bar</li></ol>" but got "<ol id=\"a\"><li>foo</li></ol><ol><li>bar</li></ol>" +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol id=b><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol id=b><li>bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo</ol>{}<br><ol id=b><li>bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>bar</li></ol>" but got "<ol><li>foo</li></ol><ol id=\"b\"><li>bar</li></ol>" +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol id=b><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol id=b><li>bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol id=b><li>bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol id=\"a\"><li>foo</li><li>bar</li></ol>" but got "<ol id=\"a\"><li>foo</li></ol><ol id=\"b\"><li>bar</li></ol>" +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol id=a><li>foo</ol>{}<br><ol id=b><li>bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol class=a><li>foo</ol>{}<br><ol class=b><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol class=a><li>foo</ol>{}<br><ol class=b><li>bar</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol class=a><li>foo</ol>{}<br><ol class=b><li>bar</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol class=\"a\"><li>foo</li><li>bar</li></ol>" but got "<ol class=\"a\"><li>foo</li></ol><ol class=\"b\"><li>bar</li></ol>" +PASS [["delete",""]] "<ol class=a><li>foo</ol>{}<br><ol class=b><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol class=a><li>foo</ol>{}<br><ol class=b><li>bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol class=a><li>foo</ol>{}<br><ol class=b><li>bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol class=a><li>foo</ol>{}<br><ol class=b><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol class=a><li>foo</ol>{}<br><ol class=b><li>bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol class=a><li>foo</ol>{}<br><ol class=b><li>bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><ol><li>foo</ol><li>{}<br></li><ol><li>bar</ol></ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><ol><li>foo</ol><li>{}<br></li><ol><li>bar</ol></ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><ol><li>foo</ol><li>{}<br></li><ol><li>bar</ol></ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><ol><li>foo</li><li>bar</li></ol></ol>" but got "<ol><ol><li>foo</li></ol><ol><li>bar</li></ol></ol>" +PASS [["delete",""]] "<ol><ol><li>foo</ol><li>{}<br></li><ol><li>bar</ol></ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><ol><li>foo</ol><li>{}<br></li><ol><li>bar</ol></ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><ol><li>foo</ol><li>{}<br></li><ol><li>bar</ol></ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><ol><li>foo</ol><li>{}<br></li><ol><li>bar</ol></ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><ol><li>foo</ol><li>{}<br></li><ol><li>bar</ol></ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><ol><li>foo</ol><li>{}<br></li><ol><li>bar</ol></ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo[</ol>bar]<ol><li>baz</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo[</ol>bar]<ol><li>baz</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo[</ol>bar]<ol><li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>baz</li></ol>" but got "<ol><li>foo</li></ol><ol><li>baz</li></ol>" +PASS [["delete",""]] "<ol><li>foo[</ol>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo[</ol>bar]<ol><li>baz</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo[</ol>bar]<ol><li>baz</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo[</ol>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo[</ol>bar]<ol><li>baz</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo[</ol>bar]<ol><li>baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>baz</li></ol>" but got "<ol><li>foo</li></ol><ol><li>baz</li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>baz</li></ol>" but got "<ol><li>foo</li></ol><ol><li>baz</li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li><p>foo</p></li><li>baz</li></ol>" but got "<ol><li><p>foo</p></li></ol><ol><li>baz</li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li><p>foo</p></li><li>baz</li></ol>" but got "<ol><li><p>foo</p></li></ol><ol><li>baz</li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li><p>foo[</ol><p>bar]<ol><li>baz</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo[]</ol><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo[]</ol><ol><li>bar</ol>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo[]</ol><ol><li>bar</ol>" compare innerHTML +PASS [["delete",""]] "<ol><li>foo[]</ol><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo[]</ol><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo[]</ol><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo[]</ol><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo[]</ol><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo[]</ol><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>[bar<ol><li>]baz</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo</ol>[bar<ol><li>]baz</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo</ol>[bar<ol><li>]baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li></ol>baz" but got "<ol><li>foo</li></ol><ol><li>baz</li></ol>" +PASS [["delete",""]] "<ol><li>foo</ol>[bar<ol><li>]baz</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>[bar<ol><li>]baz</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>[bar<ol><li>]baz</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>[bar<ol><li>]baz</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>[bar<ol><li>]baz</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>[bar<ol><li>]baz</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li>]baz</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li>]baz</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li>]baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li></ol><p>baz</p>" but got "<ol><li>foo</li></ol><ol><li>baz</li></ol>" +PASS [["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li>]baz</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li>]baz</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li>]baz</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li>]baz</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li>]baz</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li>]baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li></ol><p>baz</p>" but got "<ol><li>foo</li></ol><ol><li><p>baz</p></li></ol>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li></ol><p>baz</p>" but got "<ol><li>foo</li></ol><ol><li><p>baz</p></li></ol>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>[bar<ol><li><p>]baz</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo</ol><ol><li>b[]ar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo</ol><ol><li>b[]ar</ol>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo</ol><ol><li>b[]ar</ol>" compare innerHTML +PASS [["delete",""]] "<ol><li>foo</ol><ol><li>b[]ar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo</ol><ol><li>b[]ar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo</ol><ol><li>b[]ar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo</ol><ol><li>b[]ar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo</ol><ol><li>b[]ar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo</ol><ol><li>b[]ar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><ol><li>foo[</ol><li>bar</ol>baz]<ol><li>quz</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><ol><li>foo[</ol><li>bar</ol>baz]<ol><li>quz</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><ol><li>foo[</ol><li>bar</ol>baz]<ol><li>quz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><ol><li>foo</li></ol><li>quz</li></ol>" but got "<ol><ol><li>foo</li></ol></ol><ol><li>quz</li></ol>" +PASS [["delete",""]] "<ol><ol><li>foo[</ol><li>bar</ol>baz]<ol><li>quz</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><ol><li>foo[</ol><li>bar</ol>baz]<ol><li>quz</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><ol><li>foo[</ol><li>bar</ol>baz]<ol><li>quz</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><ol><li>foo[</ol><li>bar</ol>baz]<ol><li>quz</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><ol><li>foo[</ol><li>bar</ol>baz]<ol><li>quz</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><ol><li>foo[</ol><li>bar</ol>baz]<ol><li>quz</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ul><li>bar</ul>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ul><li>bar</ul>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ul><li>foo</ul>{}<br><ul><li>bar</ul>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ul><li>foo</li><li>bar</li></ul>" but got "<ul><li>foo</li></ul><ul><li>bar</li></ul>" +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ul><li>bar</ul>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ul><li>bar</ul>" queryCommandState("delete") before +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ul><li>bar</ul>" queryCommandValue("delete") before +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ul><li>bar</ul>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ul><li>bar</ul>" queryCommandState("delete") after +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ul><li>bar</ul>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ul><li>foo</li><li>bar</li></ul>" but got "<ul><li>foo</li></ul><ul><li>bar</li></ul>" +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" checks for modifications to non-editable content +FAIL [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ul><li>foo</li><li>bar</li></ul>" but got "<ul><li>foo</li></ul><ul><li>bar</li></ul>" +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo[<li>bar]</ol><ol><li>baz</ol><ol><li>quz</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo[<li>bar]</ol><ol><li>baz</ol><ol><li>quz</ol>" checks for modifications to non-editable content +FAIL [["delete",""]] "<ol><li>foo[<li>bar]</ol><ol><li>baz</ol><ol><li>quz</ol>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<ol><li>foo</li><li>baz</li><li>quz</li></ol>" but got "<ol><li>foo</li></ol><ol><li>baz</li></ol><ol><li>quz</li></ol>" +PASS [["delete",""]] "<ol><li>foo[<li>bar]</ol><ol><li>baz</ol><ol><li>quz</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo[<li>bar]</ol><ol><li>baz</ol><ol><li>quz</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo[<li>bar]</ol><ol><li>baz</ol><ol><li>quz</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo[<li>bar]</ol><ol><li>baz</ol><ol><li>quz</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo[<li>bar]</ol><ol><li>baz</ol><ol><li>quz</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo[<li>bar]</ol><ol><li>baz</ol><ol><li>quz</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ul><li>bar</ul>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ul><li>bar</ul>" checks for modifications to non-editable content +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ul><li>bar</ul>" compare innerHTML +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ul><li>bar</ul>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ul><li>bar</ul>" queryCommandState("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ul><li>bar</ul>" queryCommandValue("delete") before +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ul><li>bar</ul>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ul><li>bar</ul>" queryCommandState("delete") after +PASS [["delete",""]] "<ol><li>foo</ol>{}<br><ul><li>bar</ul>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ol><li>foo</ol><p>{}<br></p><ul><li>bar</ul>" queryCommandValue("delete") after +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ol><li>bar</ol>" checks for modifications to non-editable content +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ol><li>bar</ol>" compare innerHTML +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["delete",""]] "<ul><li>foo</ul>{}<br><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>": execCommand("defaultparagraphseparator", false, "div") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" compare innerHTML +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","div"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>": execCommand("defaultparagraphseparator", false, "p") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>": execCommand("delete", false, "") return value +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" checks for modifications to non-editable content +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" compare innerHTML +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandState("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("defaultparagraphseparator") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") before +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandIndeterm("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandState("delete") after +PASS [["defaultparagraphseparator","p"],["delete",""]] "<ul><li>foo</ul><p>{}<br></p><ol><li>bar</ol>" queryCommandValue("delete") after +PASS [["delete",""]] "<p><b>[foo]</b>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p><b>[foo]</b>" checks for modifications to non-editable content +FAIL [["delete",""]] "<p><b>[foo]</b>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><b><br></b></p>" but got "<p><br></p>" +PASS [["delete",""]] "<p><b>[foo]</b>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p><b>[foo]</b>" queryCommandState("delete") before +PASS [["delete",""]] "<p><b>[foo]</b>" queryCommandValue("delete") before +PASS [["delete",""]] "<p><b>[foo]</b>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p><b>[foo]</b>" queryCommandState("delete") after +PASS [["delete",""]] "<p><b>[foo]</b>" queryCommandValue("delete") after +PASS [["delete",""]] "<p><quasit>[foo]</quasit>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p><quasit>[foo]</quasit>" checks for modifications to non-editable content +FAIL [["delete",""]] "<p><quasit>[foo]</quasit>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><quasit><br></quasit></p>" but got "<p><br></p>" +PASS [["delete",""]] "<p><quasit>[foo]</quasit>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p><quasit>[foo]</quasit>" queryCommandState("delete") before +PASS [["delete",""]] "<p><quasit>[foo]</quasit>" queryCommandValue("delete") before +PASS [["delete",""]] "<p><quasit>[foo]</quasit>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p><quasit>[foo]</quasit>" queryCommandState("delete") after +PASS [["delete",""]] "<p><quasit>[foo]</quasit>" queryCommandValue("delete") after +PASS [["delete",""]] "<p><b><i>[foo]</i></b>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p><b><i>[foo]</i></b>" checks for modifications to non-editable content +FAIL [["delete",""]] "<p><b><i>[foo]</i></b>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><b><i><br></i></b></p>" but got "<p><br></p>" +PASS [["delete",""]] "<p><b><i>[foo]</i></b>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p><b><i>[foo]</i></b>" queryCommandState("delete") before +PASS [["delete",""]] "<p><b><i>[foo]</i></b>" queryCommandValue("delete") before +PASS [["delete",""]] "<p><b><i>[foo]</i></b>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p><b><i>[foo]</i></b>" queryCommandState("delete") after +PASS [["delete",""]] "<p><b><i>[foo]</i></b>" queryCommandValue("delete") after +PASS [["delete",""]] "<p><b>{foo}</b>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p><b>{foo}</b>" checks for modifications to non-editable content +FAIL [["delete",""]] "<p><b>{foo}</b>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><b><br></b></p>" but got "<p><br></p>" +PASS [["delete",""]] "<p><b>{foo}</b>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p><b>{foo}</b>" queryCommandState("delete") before +PASS [["delete",""]] "<p><b>{foo}</b>" queryCommandValue("delete") before +PASS [["delete",""]] "<p><b>{foo}</b>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p><b>{foo}</b>" queryCommandState("delete") after +PASS [["delete",""]] "<p><b>{foo}</b>" queryCommandValue("delete") after +PASS [["delete",""]] "<p>{<b>foo</b>}": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p>{<b>foo</b>}" checks for modifications to non-editable content +FAIL [["delete",""]] "<p>{<b>foo</b>}" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><b><br></b></p>" but got "<p><br></p>" +PASS [["delete",""]] "<p>{<b>foo</b>}" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p>{<b>foo</b>}" queryCommandState("delete") before +PASS [["delete",""]] "<p>{<b>foo</b>}" queryCommandValue("delete") before +PASS [["delete",""]] "<p>{<b>foo</b>}" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p>{<b>foo</b>}" queryCommandState("delete") after +PASS [["delete",""]] "<p>{<b>foo</b>}" queryCommandValue("delete") after +PASS [["delete",""]] "<p><b>f[]</b>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<p><b>f[]</b>" checks for modifications to non-editable content +FAIL [["delete",""]] "<p><b>f[]</b>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<p><b><br></b></p>" but got "<p><br></p>" +PASS [["delete",""]] "<p><b>f[]</b>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<p><b>f[]</b>" queryCommandState("delete") before +PASS [["delete",""]] "<p><b>f[]</b>" queryCommandValue("delete") before +PASS [["delete",""]] "<p><b>f[]</b>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<p><b>f[]</b>" queryCommandState("delete") after +PASS [["delete",""]] "<p><b>f[]</b>" queryCommandValue("delete") after +PASS [["delete",""]] "<b>[foo]</b>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<b>[foo]</b>" checks for modifications to non-editable content +FAIL [["delete",""]] "<b>[foo]</b>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<b><br></b>" but got "<br>" +PASS [["delete",""]] "<b>[foo]</b>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<b>[foo]</b>" queryCommandState("delete") before +PASS [["delete",""]] "<b>[foo]</b>" queryCommandValue("delete") before +PASS [["delete",""]] "<b>[foo]</b>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<b>[foo]</b>" queryCommandState("delete") after +PASS [["delete",""]] "<b>[foo]</b>" queryCommandValue("delete") after +PASS [["delete",""]] "<div><b>[foo]</b></div>": execCommand("delete", false, "") return value +PASS [["delete",""]] "<div><b>[foo]</b></div>" checks for modifications to non-editable content +FAIL [["delete",""]] "<div><b>[foo]</b></div>" compare innerHTML assert_equals: Unexpected innerHTML (after normalizing inline style) expected "<div><b><br></b></div>" but got "<br>" +PASS [["delete",""]] "<div><b>[foo]</b></div>" queryCommandIndeterm("delete") before +PASS [["delete",""]] "<div><b>[foo]</b></div>" queryCommandState("delete") before +PASS [["delete",""]] "<div><b>[foo]</b></div>" queryCommandValue("delete") before +PASS [["delete",""]] "<div><b>[foo]</b></div>" queryCommandIndeterm("delete") after +PASS [["delete",""]] "<div><b>[foo]</b></div>" queryCommandState("delete") after +PASS [["delete",""]] "<div><b>[foo]</b></div>" queryCommandValue("delete") after +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/abort/destroyed-context.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/abort/destroyed-context.html new file mode 100644 index 0000000..161d39b --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/abort/destroyed-context.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +// This is a regression test for crbug.com/860063. +window.controller = new AbortController(); +async_test(t => { + onmessage = t.step_func(event => { + assert_equals(event.data, 'started'); + const iframe = document.querySelector('iframe'); + document.body.removeChild(iframe); + controller.abort(); + t.done(); + }); +}, 'aborting a fetch in a destroyed context should not crash'); +</script> +<iframe srcdoc=" + <!DOCTYPE html> + <meta charset=utf-8> + <script> + fetch('../resources/infinite-slow-response.py', { signal: parent.controller.signal }).then(() => { + parent.postMessage('started', '*'); + }); + </script> + "> +</iframe>
diff --git a/third_party/WebKit/LayoutTests/fast/block/rtl-abs-pos-container-with-scrollbar-expected.html b/third_party/WebKit/LayoutTests/fast/block/rtl-abs-pos-container-with-scrollbar-expected.html new file mode 100644 index 0000000..bf38238 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/block/rtl-abs-pos-container-with-scrollbar-expected.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<style> + #outer { + direction: rtl; + width: 200px; + height: 200px; + overflow-y: scroll; + } + #inner { + width: 100%; + height: 300px; + background: green; + } + #absolute { + width: 100px; + height: 100px; + background: blue; + position: absolute; + } +</style> + +<div id="outer"> + <div id="inner"> + <div id="absolute"></div> + </div> +</div>
diff --git a/third_party/WebKit/LayoutTests/fast/block/rtl-abs-pos-container-with-scrollbar.html b/third_party/WebKit/LayoutTests/fast/block/rtl-abs-pos-container-with-scrollbar.html new file mode 100644 index 0000000..8d867c0c --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/block/rtl-abs-pos-container-with-scrollbar.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<style> + #outer { + direction: rtl; + position: relative; + width: 200px; + height: 200px; + overflow-y: scroll; + } + #inner { + width: 100%; + height: 300px; + background: green; + } + #absolute { + width: 100px; + height: 100px; + background: blue; + position: absolute; + } +</style> + +<div id="outer"> + <div id="inner"> + <div id="absolute"></div> + </div> +</div>
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/invalid/table-residual-style-crash-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/invalid/table-residual-style-crash-expected.txt deleted file mode 100644 index 596d6eb..0000000 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/invalid/table-residual-style-crash-expected.txt +++ /dev/null
@@ -1,13 +0,0 @@ -layer at (0,0) size 800x600 - LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x600 - LayoutNGBlockFlow {HTML} at (0,0) size 800x600 - LayoutNGBlockFlow {BODY} at (8,16) size 784x576 - LayoutNGBlockFlow (anonymous) at (0,0) size 784x0 - LayoutInline {FONT} at (0,0) size 0x0 - LayoutText {#text} at (0,0) size 0x0 - LayoutNGBlockFlow (anonymous) at (0,0) size 784x0 - LayoutNGBlockFlow {FORM} at (0,0) size 784x0 - LayoutNGBlockFlow (anonymous) at (0,0) size 784x0 - LayoutInline {FONT} at (0,0) size 0x0 - LayoutTable {TABLE} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/inspector-protocol-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/inspector-protocol-test.js index 3016884..38bc9f2 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/inspector-protocol-test.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
@@ -520,7 +520,7 @@ DevToolsAPI._sendCommandOrDie = function(method, params) { return DevToolsAPI._sendCommand(method, params).then(message => { if (message.error) - DevToolsAPI._die('Error communicating with harness', new Error(message.error)); + DevToolsAPI._die('Error communicating with harness', new Error(JSON.stringify(message.error))); return message.result; }); };
diff --git a/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt index a32a51fb..6bce4802 100644 --- a/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt
@@ -33,6 +33,8 @@ baselineShift blockSize border +borderBlock +borderBlockColor borderBlockEnd borderBlockEndColor borderBlockEndStyle @@ -41,6 +43,8 @@ borderBlockStartColor borderBlockStartStyle borderBlockStartWidth +borderBlockStyle +borderBlockWidth borderBottom borderBottomColor borderBottomLeftRadius @@ -55,6 +59,8 @@ borderImageSlice borderImageSource borderImageWidth +borderInline +borderInlineColor borderInlineEnd borderInlineEndColor borderInlineEndStyle @@ -63,6 +69,8 @@ borderInlineStartColor borderInlineStartStyle borderInlineStartWidth +borderInlineStyle +borderInlineWidth borderLeft borderLeftColor borderLeftStyle @@ -195,9 +203,11 @@ listStylePosition listStyleType margin +marginBlock marginBlockEnd marginBlockStart marginBottom +marginInline marginInlineEnd marginInlineStart marginLeft @@ -247,9 +257,11 @@ overscrollBehaviorX overscrollBehaviorY padding +paddingBlock paddingBlockEnd paddingBlockStart paddingBottom +paddingInline paddingInlineEnd paddingInlineStart paddingLeft
diff --git a/third_party/WebKit/LayoutTests/webexposed/css-property-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/css-property-listing-expected.txt index 21f978e1..5086356e 100644 --- a/third_party/WebKit/LayoutTests/webexposed/css-property-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/css-property-listing-expected.txt
@@ -450,6 +450,12 @@ border-top-color border-top-style border-top-width + border-block + border-block-end + border-block-start + border-block-color + border-block-end-color + border-block-start-color border-block-end border-block-end-color border-block-end-style @@ -458,6 +464,12 @@ border-block-start-color border-block-start-style border-block-start-width + border-block-style + border-block-end-style + border-block-start-style + border-block-width + border-block-end-width + border-block-start-width border-bottom border-bottom-color border-bottom-style @@ -473,6 +485,12 @@ border-image-slice border-image-source border-image-width + border-inline + border-inline-end + border-inline-start + border-inline-color + border-inline-end-color + border-inline-start-color border-inline-end border-inline-end-color border-inline-end-style @@ -481,6 +499,12 @@ border-inline-start-color border-inline-start-style border-inline-start-width + border-inline-style + border-inline-end-style + border-inline-start-style + border-inline-width + border-inline-end-width + border-inline-start-width border-left border-left-color border-left-style @@ -582,6 +606,12 @@ margin-left margin-right margin-top + margin-block + margin-block-end + margin-block-start + margin-inline + margin-inline-end + margin-inline-start marker marker-end marker-mid @@ -607,6 +637,12 @@ padding-left padding-right padding-top + padding-block + padding-block-end + padding-block-start + padding-inline + padding-inline-end + padding-inline-start page-break-after break-after page-break-before
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn index 3bbc183..3e0a2f4 100644 --- a/third_party/blink/public/BUILD.gn +++ b/third_party/blink/public/BUILD.gn
@@ -739,6 +739,7 @@ ":web_client_hints_types_mojo_bindings", ":web_feature_mojo_bindings", "//components/payments/mojom", + "//components/services/font/public/interfaces", "//device/bluetooth/public/mojom", "//mojo/public/mojom/base", "//services/device/public/mojom",
diff --git a/third_party/blink/public/mojom/use_counter/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/css_property_id.mojom index 13a6afe..0dec4c15 100644 --- a/third_party/blink/public/mojom/use_counter/css_property_id.mojom +++ b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
@@ -4,7 +4,7 @@ module blink.mojom; -const int32 kMaximumCSSSampleId = 617; +const int32 kMaximumCSSSampleId = 629; // This CSSSampleId represents page load for CSS histograms. It is recorded once // per page visit for each CSS histogram being logged on the blink side and the
diff --git a/third_party/blink/public/web/web_embedded_worker.h b/third_party/blink/public/web/web_embedded_worker.h index 0406137..f8f58fd 100644 --- a/third_party/blink/public/web/web_embedded_worker.h +++ b/third_party/blink/public/web/web_embedded_worker.h
@@ -57,6 +57,7 @@ std::unique_ptr<WebServiceWorkerContextClient>, std::unique_ptr<WebServiceWorkerInstalledScriptsManager>, mojo::ScopedMessagePipeHandle content_settings_handle, + mojo::ScopedMessagePipeHandle cache_storage, mojo::ScopedMessagePipeHandle interface_provider); virtual ~WebEmbeddedWorker() = default;
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc index b341b43..55f9850 100644 --- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc +++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
@@ -7,6 +7,7 @@ #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/core/v8/scheduled_action.h b/third_party/blink/renderer/bindings/core/v8/scheduled_action.h index b532cb7..4a62e0f 100644 --- a/third_party/blink/renderer/bindings/core/v8/scheduled_action.h +++ b/third_party/blink/renderer/bindings/core/v8/scheduled_action.h
@@ -36,6 +36,7 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/wtf/forward.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "v8/include/v8.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc index 974c8f4..d5de7130 100644 --- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc +++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc
@@ -17,6 +17,7 @@ #include "third_party/blink/renderer/core/html/html_element.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" #include "v8/include/v8.h"
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_context_snapshot.cc b/third_party/blink/renderer/bindings/core/v8/v8_context_snapshot.cc index 109473b..41ef61f5 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_context_snapshot.cc +++ b/third_party/blink/renderer/bindings/core/v8/v8_context_snapshot.cc
@@ -17,6 +17,7 @@ #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" #include "third_party/blink/renderer/platform/bindings/origin_trial_features.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.h b/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.h index 3ffb391..66ad07a 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.h +++ b/third_party/blink/renderer/bindings/core/v8/v8_v0_custom_element_lifecycle_callbacks.h
@@ -41,6 +41,7 @@ namespace blink { +class V0CustomElementBinding; class V0CustomElementLifecycleCallbacks; class Element; class V8PerContextData;
diff --git a/third_party/blink/renderer/bindings/core/v8/window_proxy.h b/third_party/blink/renderer/bindings/core/v8/window_proxy.h index 04403ed..112c808 100644 --- a/third_party/blink/renderer/bindings/core/v8/window_proxy.h +++ b/third_party/blink/renderer/bindings/core/v8/window_proxy.h
@@ -43,6 +43,7 @@ class DOMWindow; class Frame; +struct WrapperTypeInfo; // WindowProxy implements the split window model of a window for a frame. In the // HTML standard, the split window model is composed of the Window interface
diff --git a/third_party/blink/renderer/bindings/scripts/generate_origin_trial_features.py b/third_party/blink/renderer/bindings/scripts/generate_origin_trial_features.py index 537112e..5ce2b04 100755 --- a/third_party/blink/renderer/bindings/scripts/generate_origin_trial_features.py +++ b/third_party/blink/renderer/bindings/scripts/generate_origin_trial_features.py
@@ -162,6 +162,7 @@ 'core/origin_trials/origin_trials.h', 'platform/bindings/origin_trial_features.h', 'platform/bindings/script_state.h', + 'platform/bindings/v8_per_context_data.h', # TODO(iclelland): Remove the need to explicitly include this; it is # here because the ContextFeatureSettings code needs it. 'bindings/core/v8/v8_window.h',
diff --git a/third_party/blink/renderer/bindings/scripts/v8_attributes.py b/third_party/blink/renderer/bindings/scripts/v8_attributes.py index d8d9ab6..082c09b1 100644 --- a/third_party/blink/renderer/bindings/scripts/v8_attributes.py +++ b/third_party/blink/renderer/bindings/scripts/v8_attributes.py
@@ -120,6 +120,10 @@ if is_cached_accessor: includes.add('platform/bindings/v8_private_property.h') + # [LogActivity] + if 'LogActivity' in extended_attributes: + includes.add('platform/bindings/v8_per_context_data.h') + context = { 'activity_logging_world_list_for_getter': v8_utilities.activity_logging_world_list(attribute, 'Getter'), # [ActivityLogging] 'activity_logging_world_list_for_setter': v8_utilities.activity_logging_world_list(attribute, 'Setter'), # [ActivityLogging]
diff --git a/third_party/blink/renderer/bindings/scripts/v8_callback_interface.py b/third_party/blink/renderer/bindings/scripts/v8_callback_interface.py index 0f5b8341..d3d8728 100644 --- a/third_party/blink/renderer/bindings/scripts/v8_callback_interface.py +++ b/third_party/blink/renderer/bindings/scripts/v8_callback_interface.py
@@ -50,6 +50,7 @@ ]) LEGACY_CALLBACK_INTERFACE_H_INCLUDES = frozenset([ 'platform/bindings/dom_wrapper_world.h', + 'platform/bindings/wrapper_type_info.h', ]) LEGACY_CALLBACK_INTERFACE_CPP_INCLUDES = frozenset([ 'bindings/core/v8/v8_dom_configuration.h',
diff --git a/third_party/blink/renderer/bindings/scripts/v8_interface.py b/third_party/blink/renderer/bindings/scripts/v8_interface.py index 4fa121b7..6356594 100644 --- a/third_party/blink/renderer/bindings/scripts/v8_interface.py +++ b/third_party/blink/renderer/bindings/scripts/v8_interface.py
@@ -342,6 +342,7 @@ ' specified on partial interface definitions: ' '%s' % interface.name) if named_constructor: + includes.add('platform/bindings/v8_per_context_data.h') includes.add('platform/bindings/v8_private_property.h') includes.add('platform/bindings/v8_object_constructor.h') @@ -476,6 +477,8 @@ sorted(origin_trial_features(interface, context['constants'], context['attributes'], context['methods']) + context_enabled_features(context['attributes'])), }) + if context['optional_features']: + includes.add('platform/bindings/v8_per_context_data.h') # Cross-origin interceptors has_cross_origin_named_getter = False
diff --git a/third_party/blink/renderer/bindings/scripts/v8_methods.py b/third_party/blink/renderer/bindings/scripts/v8_methods.py index 37f20d1..4e5e737 100644 --- a/third_party/blink/renderer/bindings/scripts/v8_methods.py +++ b/third_party/blink/renderer/bindings/scripts/v8_methods.py
@@ -178,6 +178,10 @@ else: side_effect_type = 'V8DOMConfiguration::kHasSideEffect' + # [LogActivity] + if 'LogActivity' in extended_attributes: + includes.add('platform/bindings/v8_per_context_data.h') + argument_contexts = [ argument_context(interface, method, argument, index, is_visible=is_visible) for index, argument in enumerate(arguments)]
diff --git a/third_party/blink/renderer/bindings/tests/results/core/origin_trial_features_for_core.cc b/third_party/blink/renderer/bindings/tests/results/core/origin_trial_features_for_core.cc index 4311e26..013a13f 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/origin_trial_features_for_core.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/origin_trial_features_for_core.cc
@@ -19,6 +19,7 @@ #include "third_party/blink/renderer/core/origin_trials/origin_trials.h" #include "third_party/blink/renderer/platform/bindings/origin_trial_features.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc index c1f0b7d..f686d4b82 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc
@@ -20,6 +20,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/get_ptr.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc index 1aa0bd7..58bb7bc 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
@@ -24,6 +24,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_messages.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/wtf/get_ptr.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc index de3717b..46ff0a0 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc
@@ -18,6 +18,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_messages.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/wtf/get_ptr.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc index b96ca84..ab2112a7 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
@@ -21,6 +21,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/wtf/get_ptr.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc index 01dd0d9..998dd91 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc
@@ -19,6 +19,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_messages.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/wtf/get_ptr.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.h b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.h index 6502a6c..f99cf7db0 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.h +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.h
@@ -15,6 +15,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/bindings/callback_interface_base.h" #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" +#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc index 72d299ee..d9e9701e 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -74,6 +74,7 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/get_ptr.h"
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/origin_trial_features_for_modules.cc b/third_party/blink/renderer/bindings/tests/results/modules/origin_trial_features_for_modules.cc index fc782ddd..546c2905 100644 --- a/third_party/blink/renderer/bindings/tests/results/modules/origin_trial_features_for_modules.cc +++ b/third_party/blink/renderer/bindings/tests/results/modules/origin_trial_features_for_modules.cc
@@ -19,6 +19,7 @@ #include "third_party/blink/renderer/core/origin_trials/origin_trials.h" #include "third_party/blink/renderer/platform/bindings/origin_trial_features.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc index 7f1a3f4..4e96d6d 100644 --- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc +++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
@@ -28,6 +28,7 @@ #include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/get_ptr.h"
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn index 1dee664..7d68fa3 100644 --- a/third_party/blink/renderer/core/css/BUILD.gn +++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -756,14 +756,22 @@ "properties/shorthands/background_custom.cc", "properties/shorthands/background_position_custom.cc", "properties/shorthands/background_repeat_custom.cc", + "properties/shorthands/border_block_color_custom.cc", + "properties/shorthands/border_block_custom.cc", "properties/shorthands/border_block_end_custom.cc", "properties/shorthands/border_block_start_custom.cc", + "properties/shorthands/border_block_style_custom.cc", + "properties/shorthands/border_block_width_custom.cc", "properties/shorthands/border_bottom_custom.cc", "properties/shorthands/border_color_custom.cc", "properties/shorthands/border_custom.cc", "properties/shorthands/border_image_custom.cc", + "properties/shorthands/border_inline_color_custom.cc", + "properties/shorthands/border_inline_custom.cc", "properties/shorthands/border_inline_end_custom.cc", "properties/shorthands/border_inline_start_custom.cc", + "properties/shorthands/border_inline_style_custom.cc", + "properties/shorthands/border_inline_width_custom.cc", "properties/shorthands/border_left_custom.cc", "properties/shorthands/border_radius_custom.cc", "properties/shorthands/border_right_custom.cc", @@ -787,13 +795,17 @@ "properties/shorthands/grid_row_gap_custom.cc", "properties/shorthands/grid_template_custom.cc", "properties/shorthands/list_style_custom.cc", + "properties/shorthands/margin_block_custom.cc", "properties/shorthands/margin_custom.cc", + "properties/shorthands/margin_inline_custom.cc", "properties/shorthands/marker_custom.cc", "properties/shorthands/offset_custom.cc", "properties/shorthands/outline_custom.cc", "properties/shorthands/overflow_custom.cc", "properties/shorthands/overscroll_behavior_custom.cc", + "properties/shorthands/padding_block_custom.cc", "properties/shorthands/padding_custom.cc", + "properties/shorthands/padding_inline_custom.cc", "properties/shorthands/page_break_after_custom.cc", "properties/shorthands/page_break_before_custom.cc", "properties/shorthands/page_break_inside_custom.cc",
diff --git a/third_party/blink/renderer/core/css/CSSProperties.json5 b/third_party/blink/renderer/core/css/CSSProperties.json5 index ed8ce36..2207614 100644 --- a/third_party/blink/renderer/core/css/CSSProperties.json5 +++ b/third_party/blink/renderer/core/css/CSSProperties.json5
@@ -4520,6 +4520,18 @@ property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], }, { + name: "border-block", + longhands: ["border-block-start", "border-block-end"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { + name: "border-block-color", + longhands: ["border-block-start-color", "border-block-end-color"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { name: "border-block-end", longhands: [ "border-block-end-width", "border-block-end-style", @@ -4544,6 +4556,18 @@ }, }, { + name: "border-block-style", + longhands: ["border-block-start-style", "border-block-end-style"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { + name: "border-block-width", + longhands: ["border-block-start-width", "border-block-end-width"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { name: "border-bottom", longhands: [ "border-bottom-width", "border-bottom-style", "border-bottom-color" @@ -4567,6 +4591,18 @@ property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], }, { + name: "border-inline", + longhands: ["border-inline-start", "border-inline-end"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { + name: "border-inline-color", + longhands: ["border-inline-start-color", "border-inline-end-color"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { name: "border-inline-end", longhands: [ "border-inline-end-width", "border-inline-end-style", @@ -4591,6 +4627,18 @@ }, }, { + name: "border-inline-style", + longhands: ["border-inline-start-style", "border-inline-end-style"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { + name: "border-inline-width", + longhands: ["border-inline-start-width", "border-inline-end-width"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { name: "border-left", longhands: [ "border-left-width", "border-left-style", "border-left-color" @@ -4751,6 +4799,18 @@ layout_dependent: true, }, { + name: "margin-block", + longhands: ["margin-block-start", "margin-block-end"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { + name: "margin-inline", + longhands: ["margin-inline-start", "margin-inline-end"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { name: "marker", longhands: ["marker-start", "marker-mid", "marker-end"], property_methods: ["ParseShorthand"], @@ -4788,6 +4848,18 @@ layout_dependent: true, }, { + name: "padding-block", + longhands: ["padding-block-start", "padding-block-end"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { + name: "padding-inline", + longhands: ["padding-inline-start", "padding-inline-end"], + property_methods: ["ParseShorthand"], + runtime_flag: "CSSLogical", + }, + { name: "page-break-after", longhands: ["break-after"], property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"],
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc index 1ec7f49e..a33826a7 100644 --- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc +++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -955,6 +955,46 @@ return true; } +bool ConsumeBorderShorthand(CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSValue*& result_width, + const CSSValue*& result_style, + const CSSValue*& result_color) { + while (!result_width || !result_style || !result_color) { + if (!result_width) { + result_width = CSSPropertyParserHelpers::ConsumeLineWidth( + range, context.Mode(), + CSSPropertyParserHelpers::UnitlessQuirk::kForbid); + if (result_width) + continue; + } + if (!result_style) { + result_style = CSSPropertyParserHelpers::ParseLonghand( + CSSPropertyBorderLeftStyle, CSSPropertyBorder, context, range); + if (result_style) + continue; + } + if (!result_color) { + result_color = + CSSPropertyParserHelpers::ConsumeColor(range, context.Mode()); + if (result_color) + continue; + } + break; + } + + if (!result_width && !result_style && !result_color) + return false; + + if (!result_width) + result_width = CSSInitialValue::Create(); + if (!result_style) + result_style = CSSInitialValue::Create(); + if (!result_color) + result_color = CSSInitialValue::Create(); + return true; +} + // This should go away once we drop support for -webkit-gradient static CSSPrimitiveValue* ConsumeDeprecatedGradientPoint( CSSParserTokenRange& args,
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h index f1870b17..bd4cd25c 100644 --- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h +++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
@@ -101,6 +101,11 @@ UnitlessQuirk, CSSValue*& result_x, CSSValue*& result_y); +bool ConsumeBorderShorthand(CSSParserTokenRange&, + const CSSParserContext&, + const CSSValue*& result_width, + const CSSValue*& result_style, + const CSSValue*& result_color); enum class ConsumeGeneratedImagePolicy { kAllow, kForbid };
diff --git a/third_party/blink/renderer/core/css/parser/css_proto_converter.cc b/third_party/blink/renderer/core/css/parser/css_proto_converter.cc index 03175244..4fce488 100644 --- a/third_party/blink/renderer/core/css/parser/css_proto_converter.cc +++ b/third_party/blink/renderer/core/css/parser/css_proto_converter.cc
@@ -1458,6 +1458,18 @@ "border-block-end", "border-inline-start", "border-inline-end", + "margin-block", + "margin-inline", + "padding-block", + "padding-inline", + "border-block-width", + "border-block-style", + "border-block-color", + "border-inline-width", + "border-inline-style", + "border-inline-color", + "border-block", + "border-inline", "INVALID_PROPERTY", };
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/border_block_color_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/border_block_color_custom.cc new file mode 100644 index 0000000..a067b26f --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/border_block_color_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/border_block_color.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool BorderBlockColor::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + borderBlockColorShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/border_block_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/border_block_custom.cc new file mode 100644 index 0000000..111168b --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/border_block_custom.cc
@@ -0,0 +1,39 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/border_block.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool BorderBlock::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + const CSSValue* width = nullptr; + const CSSValue* style = nullptr; + const CSSValue* color = nullptr; + + if (!CSSPropertyParserHelpers::ConsumeBorderShorthand(range, context, width, + style, color)) { + return false; + }; + + CSSPropertyParserHelpers::AddExpandedPropertyForValue( + CSSPropertyBorderBlockWidth, *width, important, properties); + CSSPropertyParserHelpers::AddExpandedPropertyForValue( + CSSPropertyBorderBlockStyle, *style, important, properties); + CSSPropertyParserHelpers::AddExpandedPropertyForValue( + CSSPropertyBorderBlockColor, *color, important, properties); + + return range.AtEnd(); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/border_block_style_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/border_block_style_custom.cc new file mode 100644 index 0000000..9258ef7 --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/border_block_style_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/border_block_style.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool BorderBlockStyle::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + borderBlockStyleShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/border_block_width_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/border_block_width_custom.cc new file mode 100644 index 0000000..da61939 --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/border_block_width_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/border_block_width.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool BorderBlockWidth::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + borderBlockWidthShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/border_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/border_custom.cc index ba242f9..7dff25ea 100644 --- a/third_party/blink/renderer/core/css/properties/shorthands/border_custom.cc +++ b/third_party/blink/renderer/core/css/properties/shorthands/border_custom.cc
@@ -19,43 +19,14 @@ const CSSParserContext& context, const CSSParserLocalContext&, HeapVector<CSSPropertyValue, 256>& properties) const { - CSSValue* width = nullptr; + const CSSValue* width = nullptr; const CSSValue* style = nullptr; - CSSValue* color = nullptr; + const CSSValue* color = nullptr; - while (!width || !style || !color) { - if (!width) { - width = CSSPropertyParserHelpers::ConsumeLineWidth( - range, context.Mode(), - CSSPropertyParserHelpers::UnitlessQuirk::kForbid); - if (width) - continue; - } - if (!style) { - bool needs_legacy_parsing = false; - style = CSSPropertyParserHelpers::ParseLonghand( - CSSPropertyBorderLeftStyle, CSSPropertyBorder, context, range); - DCHECK(!needs_legacy_parsing); - if (style) - continue; - } - if (!color) { - color = CSSPropertyParserHelpers::ConsumeColor(range, context.Mode()); - if (color) - continue; - } - break; - } - - if (!width && !style && !color) + if (!CSSPropertyParserHelpers::ConsumeBorderShorthand(range, context, width, + style, color)) { return false; - - if (!width) - width = CSSInitialValue::Create(); - if (!style) - style = CSSInitialValue::Create(); - if (!color) - color = CSSInitialValue::Create(); + }; CSSPropertyParserHelpers::AddExpandedPropertyForValue( CSSPropertyBorderWidth, *width, important, properties);
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/border_inline_color_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/border_inline_color_custom.cc new file mode 100644 index 0000000..315135b5 --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/border_inline_color_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/border_inline_color.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool BorderInlineColor::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + borderInlineColorShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/border_inline_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/border_inline_custom.cc new file mode 100644 index 0000000..67d569590 --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/border_inline_custom.cc
@@ -0,0 +1,39 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/border_inline.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool BorderInline::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + const CSSValue* width = nullptr; + const CSSValue* style = nullptr; + const CSSValue* color = nullptr; + + if (!CSSPropertyParserHelpers::ConsumeBorderShorthand(range, context, width, + style, color)) { + return false; + }; + + CSSPropertyParserHelpers::AddExpandedPropertyForValue( + CSSPropertyBorderInlineWidth, *width, important, properties); + CSSPropertyParserHelpers::AddExpandedPropertyForValue( + CSSPropertyBorderInlineStyle, *style, important, properties); + CSSPropertyParserHelpers::AddExpandedPropertyForValue( + CSSPropertyBorderInlineColor, *color, important, properties); + + return range.AtEnd(); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/border_inline_style_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/border_inline_style_custom.cc new file mode 100644 index 0000000..83859ed --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/border_inline_style_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/border_inline_style.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool BorderInlineStyle::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + borderInlineStyleShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/border_inline_width_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/border_inline_width_custom.cc new file mode 100644 index 0000000..1fdacb1 --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/border_inline_width_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/border_inline_width.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool BorderInlineWidth::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + borderInlineWidthShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/margin_block_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/margin_block_custom.cc new file mode 100644 index 0000000..3d91f6d --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/margin_block_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/margin_block.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool MarginBlock::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + marginBlockShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/margin_inline_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/margin_inline_custom.cc new file mode 100644 index 0000000..3dabd4b --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/margin_inline_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/margin_inline.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool MarginInline::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + marginInlineShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/padding_block_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/padding_block_custom.cc new file mode 100644 index 0000000..959c9ed --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/padding_block_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/padding_block.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool PaddingBlock::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + paddingBlockShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/padding_inline_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/padding_inline_custom.cc new file mode 100644 index 0000000..79c0a3b0 --- /dev/null +++ b/third_party/blink/renderer/core/css/properties/shorthands/padding_inline_custom.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/css/properties/shorthands/padding_inline.h" + +#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h" +#include "third_party/blink/renderer/core/style_property_shorthand.h" + +namespace blink { +namespace CSSShorthand { + +bool PaddingInline::ParseShorthand( + bool important, + CSSParserTokenRange& range, + const CSSParserContext& context, + const CSSParserLocalContext&, + HeapVector<CSSPropertyValue, 256>& properties) const { + return CSSPropertyParserHelpers::ConsumeShorthandVia2Longhands( + paddingInlineShorthand(), important, context, range, properties); +} + +} // namespace CSSShorthand +} // namespace blink
diff --git a/third_party/blink/renderer/core/editing/frame_selection.h b/third_party/blink/renderer/core/editing/frame_selection.h index e164ea2..7b1ecd42 100644 --- a/third_party/blink/renderer/core/editing/frame_selection.h +++ b/third_party/blink/renderer/core/editing/frame_selection.h
@@ -64,9 +64,7 @@ enum class CaretVisibility; enum class HandleVisibility { kNotVisible, kVisible }; - -// TODO(yoichio): Rename this to SelectSoftLineBreak -enum class SelectLineBreak { kNotSelected, kSelected }; +enum class SelectSoftLineBreak { kNotSelected, kSelected }; // This is return type of ComputeLayoutSelectionStatus(paintfragment). // This structure represents how the fragment is selected. @@ -81,7 +79,7 @@ LayoutSelectionStatus(unsigned passed_start, unsigned passed_end, - SelectLineBreak passed_line_break) + SelectSoftLineBreak passed_line_break) : start(passed_start), end(passed_end), line_break(passed_line_break) { DCHECK_LE(start, end); } @@ -92,7 +90,7 @@ unsigned start; unsigned end; - SelectLineBreak line_break; + SelectSoftLineBreak line_break; }; class CORE_EXPORT FrameSelection final
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc index f7a30b8..535a184 100644 --- a/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc +++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc
@@ -123,8 +123,7 @@ String text_chunks; for (; !iterator.AtEnd(); iterator.Advance()) { text_chunks.append('['); - text_chunks.append( - iterator.GetText().Substring(0, iterator.GetText().length())); + text_chunks.append(iterator.GetText().GetTextForTesting()); text_chunks.append(']'); } return std::string(text_chunks.Utf8().data());
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc index 66c79b7..7170b04b 100644 --- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc +++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc
@@ -73,18 +73,10 @@ return text_[text_start_offset_ + index]; } -String TextIteratorTextState::Substring(unsigned position, - unsigned length) const { - SECURITY_DCHECK(position <= this->length()); - SECURITY_DCHECK(position + length <= this->length()); - if (!length) - return g_empty_string; - if (single_character_buffer_) { - DCHECK_EQ(position, 0u); - DCHECK_EQ(length, 1u); +String TextIteratorTextState::GetTextForTesting() const { + if (single_character_buffer_) return String(&single_character_buffer_, 1); - } - return text_.Substring(text_start_offset_ + position, length); + return text_.Substring(text_start_offset_, length()); } void TextIteratorTextState::AppendTextToStringBuilder(
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h index e185b2f..9b880d5 100644 --- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h +++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h
@@ -49,7 +49,7 @@ // Return properties of the current text. unsigned length() const { return text_length_; } UChar CharacterAt(unsigned index) const; - String Substring(unsigned position, unsigned length) const; + String GetTextForTesting() const; void AppendTextToStringBuilder(StringBuilder&, unsigned position = 0, unsigned max_length = UINT_MAX) const;
diff --git a/third_party/blink/renderer/core/editing/layout_selection.cc b/third_party/blink/renderer/core/editing/layout_selection.cc index d3aa88b..2286b3e 100644 --- a/third_party/blink/renderer/core/editing/layout_selection.cc +++ b/third_party/blink/renderer/core/editing/layout_selection.cc
@@ -752,7 +752,7 @@ ToNGPhysicalTextFragmentOrDie(fragment.PhysicalFragment()); // We don't paint selection on ellipsis. if (text_fragment.StyleVariant() == NGStyleVariant::kEllipsis) - return {0, 0, SelectLineBreak::kNotSelected}; + return {0, 0, SelectSoftLineBreak::kNotSelected}; switch (text_fragment.GetLayoutObject()->GetSelectionState()) { case SelectionState::kStart: { DCHECK(SelectionStart().has_value()); @@ -761,8 +761,8 @@ return {ClampOffset(start_in_block, text_fragment), text_fragment.EndOffset(), (is_continuous && IsBeforeSoftLineBreak(fragment)) - ? SelectLineBreak::kSelected - : SelectLineBreak::kNotSelected}; + ? SelectSoftLineBreak::kSelected + : SelectSoftLineBreak::kNotSelected}; } case SelectionState::kEnd: { DCHECK(SelectionEnd().has_value()); @@ -772,8 +772,8 @@ const bool is_continuous = text_fragment.EndOffset() < end_in_block; return {text_fragment.StartOffset(), end_in_fragment, (is_continuous && IsBeforeSoftLineBreak(fragment)) - ? SelectLineBreak::kSelected - : SelectLineBreak::kNotSelected}; + ? SelectSoftLineBreak::kSelected + : SelectSoftLineBreak::kNotSelected}; } case SelectionState::kStartAndEnd: { DCHECK(SelectionStart().has_value()); @@ -786,17 +786,18 @@ text_fragment.EndOffset() < end_in_block; return {ClampOffset(start_in_block, text_fragment), end_in_fragment, (is_continuous && IsBeforeSoftLineBreak(fragment)) - ? SelectLineBreak::kSelected - : SelectLineBreak::kNotSelected}; + ? SelectSoftLineBreak::kSelected + : SelectSoftLineBreak::kNotSelected}; } case SelectionState::kInside: { return {text_fragment.StartOffset(), text_fragment.EndOffset(), - IsBeforeSoftLineBreak(fragment) ? SelectLineBreak::kSelected - : SelectLineBreak::kNotSelected}; + IsBeforeSoftLineBreak(fragment) + ? SelectSoftLineBreak::kSelected + : SelectSoftLineBreak::kNotSelected}; } default: // This block is not included in selection. - return {0, 0, SelectLineBreak::kNotSelected}; + return {0, 0, SelectSoftLineBreak::kNotSelected}; } }
diff --git a/third_party/blink/renderer/core/editing/layout_selection_test.cc b/third_party/blink/renderer/core/editing/layout_selection_test.cc index fb1f41b..92e94da 100644 --- a/third_party/blink/renderer/core/editing/layout_selection_test.cc +++ b/third_party/blink/renderer/core/editing/layout_selection_test.cc
@@ -928,7 +928,7 @@ GetNGPaintFragment(first_text->GetLayoutObject()); const LayoutSelectionStatus& status = Selection().ComputeLayoutSelectionStatus(fragment); - return status.line_break == SelectLineBreak::kSelected; + return status.line_break == SelectSoftLineBreak::kSelected; } LayoutSelectionStatus ComputeLayoutSelectionStatus(const Node& node) { @@ -946,9 +946,9 @@ std::ostream& operator<<(std::ostream& ostream, const LayoutSelectionStatus& status) { - const String line_break = (status.line_break == SelectLineBreak::kSelected) - ? "kSelected" - : "kNotSelected"; + const String line_break = + (status.line_break == SelectSoftLineBreak::kSelected) ? "kSelected" + : "kNotSelected"; return ostream << status.start << ", " << status.end << ", " << std::boolalpha << line_break; } @@ -964,7 +964,7 @@ LayoutObject* const foo = GetDocument().body()->firstChild()->GetLayoutObject(); - EXPECT_EQ(LayoutSelectionStatus(0u, 0u, SelectLineBreak::kNotSelected), + EXPECT_EQ(LayoutSelectionStatus(0u, 0u, SelectSoftLineBreak::kNotSelected), Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(foo))); LayoutObject* const bar = GetDocument() .body() @@ -972,7 +972,7 @@ ->nextSibling() ->firstChild() ->GetLayoutObject(); - EXPECT_EQ(LayoutSelectionStatus(4u, 5u, SelectLineBreak::kNotSelected), + EXPECT_EQ(LayoutSelectionStatus(4u, 5u, SelectSoftLineBreak::kNotSelected), Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(bar))); } @@ -988,12 +988,12 @@ const LayoutTextFragment* const foo_f = ToLayoutTextFragment(AssociatedLayoutObjectOf(*foo, 0)); EXPECT_EQ( - LayoutSelectionStatus(0u, 1u, SelectLineBreak::kSelected), + LayoutSelectionStatus(0u, 1u, SelectSoftLineBreak::kSelected), Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(foo_f))); const LayoutTextFragment* const foo_oo = ToLayoutTextFragment(AssociatedLayoutObjectOf(*foo, 1)); EXPECT_EQ( - LayoutSelectionStatus(1u, 2u, SelectLineBreak::kNotSelected), + LayoutSelectionStatus(1u, 2u, SelectSoftLineBreak::kNotSelected), Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(foo_oo))); } @@ -1008,7 +1008,7 @@ TEST_CHECK(); LayoutObject* const foo = GetDocument().body()->firstChild()->firstChild()->GetLayoutObject(); - EXPECT_EQ(LayoutSelectionStatus(1u, 3u, SelectLineBreak::kSelected), + EXPECT_EQ(LayoutSelectionStatus(1u, 3u, SelectSoftLineBreak::kSelected), Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(foo))); LayoutObject* const bar = GetDocument() .body() @@ -1016,7 +1016,7 @@ ->nextSibling() ->firstChild() ->GetLayoutObject(); - EXPECT_EQ(LayoutSelectionStatus(0u, 2u, SelectLineBreak::kNotSelected), + EXPECT_EQ(LayoutSelectionStatus(0u, 2u, SelectSoftLineBreak::kNotSelected), Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(bar))); } @@ -1101,10 +1101,10 @@ "bar<img id=img2 width=10px height=10px>|</div>"); Node* const foo = GetDocument().body()->firstChild()->firstChild()->nextSibling(); - EXPECT_EQ(SelectLineBreak::kNotSelected, + EXPECT_EQ(SelectSoftLineBreak::kNotSelected, ComputeLayoutSelectionStatus(*foo).line_break); Node* const bar = foo->nextSibling()->nextSibling(); - EXPECT_EQ(SelectLineBreak::kNotSelected, + EXPECT_EQ(SelectSoftLineBreak::kNotSelected, ComputeLayoutSelectionStatus(*bar).line_break); } @@ -1117,7 +1117,7 @@ GetDocument().QuerySelector("br")->GetLayoutObject(); CHECK(layout_br->IsBR()); EXPECT_EQ( - LayoutSelectionStatus(3u, 4u, SelectLineBreak::kNotSelected), + LayoutSelectionStatus(3u, 4u, SelectSoftLineBreak::kNotSelected), Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(layout_br))); }
diff --git a/third_party/blink/renderer/core/fetch/body_stream_buffer.cc b/third_party/blink/renderer/core/fetch/body_stream_buffer.cc index 2aae5ac..d5be0e6 100644 --- a/third_party/blink/renderer/core/fetch/body_stream_buffer.cc +++ b/third_party/blink/renderer/core/fetch/body_stream_buffer.cc
@@ -423,6 +423,11 @@ } void BodyStreamBuffer::Abort() { + if (!Controller()) { + DCHECK(!GetExecutionContext()); + DCHECK(!consumer_); + return; + } Controller()->GetError(DOMException::Create(DOMExceptionCode::kAbortError)); CancelConsumer(); }
diff --git a/third_party/blink/renderer/core/frame/report.h b/third_party/blink/renderer/core/frame/report.h index 7391a8e..5163422 100644 --- a/third_party/blink/renderer/core/frame/report.h +++ b/third_party/blink/renderer/core/frame/report.h
@@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REPORT_H_ #include "third_party/blink/renderer/core/frame/report_body.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink {
diff --git a/third_party/blink/renderer/core/frame/use_counter.cc b/third_party/blink/renderer/core/frame/use_counter.cc index 6bf1a6a..de7fd93 100644 --- a/third_party/blink/renderer/core/frame/use_counter.cc +++ b/third_party/blink/renderer/core/frame/use_counter.cc
@@ -1186,6 +1186,30 @@ return 616; case CSSPropertyBorderInlineEnd: return 617; + case CSSPropertyMarginBlock: + return 618; + case CSSPropertyMarginInline: + return 619; + case CSSPropertyPaddingBlock: + return 620; + case CSSPropertyPaddingInline: + return 621; + case CSSPropertyBorderBlockColor: + return 622; + case CSSPropertyBorderBlockStyle: + return 623; + case CSSPropertyBorderBlockWidth: + return 624; + case CSSPropertyBorderInlineColor: + return 625; + case CSSPropertyBorderInlineStyle: + return 626; + case CSSPropertyBorderInlineWidth: + return 627; + case CSSPropertyBorderBlock: + return 628; + case CSSPropertyBorderInline: + return 629; // 1. Add new features above this line (don't change the assigned numbers of // the existing items). // 2. Update kMaximumCSSSampleId (defined in
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl index 4d2516a..e54386b 100644 --- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl +++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -5806,6 +5806,8 @@ command attachToTarget parameters TargetID targetId + # Enables "flat" access to the session via specifying sessionId attribute in the commands. + experimental optional boolean flatten returns # Id assigned to the session. SessionID sessionId
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index dfbec903..7aa3da47 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3985,6 +3985,12 @@ LayoutUnit static_position = child->Layer()->StaticInlinePosition() + container_logical_width + container_block->BorderLogicalLeft(); + if (container_block->IsBox() && + ToLayoutBox(container_block) + ->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) { + static_position += + ToLayoutBox(container_block)->OriginAdjustmentForScrollbars().Width(); + } for (LayoutObject* curr = child->Parent(); curr; curr = curr->Container()) { if (curr->IsBox()) { if (curr == enclosing_box)
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc index e86a0b1..6a26e8b 100644 --- a/third_party/blink/renderer/core/layout/layout_view.cc +++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -148,7 +148,19 @@ hit_layer = true; result = cache_result; } else { - hit_layer = Layer()->HitTest(location, result); + LocalFrameView* frame_view = GetFrameView(); + LayoutRect hit_test_area; + if (frame_view) { + // Start with a rect sized to the frame, to ensure we include the + // scrollbars. + hit_test_area = LayoutRect(LayoutPoint(), LayoutSize(frame_view->Size())); + if (result.GetHitTestRequest().IgnoreClipping()) { + hit_test_area.Unite( + frame_view->DocumentToFrame(LayoutRect(DocumentRect()))); + } + } + + hit_layer = Layer()->HitTest(location, result, hit_test_area); // If hitTestResult include scrollbar, innerNode should be the parent of the // scrollbar.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc index 5309915f..96d18a5 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -1379,8 +1379,12 @@ child_data.margins.block_end, &logical_block_offset, &margin_strut); } else { - margin_strut.Append(child_data.margins.block_end, - child.Style().HasMarginAfterQuirk()); + // An empty block's end margin can "inherit" quirkiness from its start + // margin. E.g. + // <ol style="margin-bottom: 20px"></ol> + bool is_quirky = (is_empty_block && child.Style().HasMarginBeforeQuirk()) || + child.Style().HasMarginAfterQuirk(); + margin_strut.Append(child_data.margins.block_end, is_quirky); } // This flag is subtle, but in order to determine our size correctly we need
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc index 86fbeeaa..3d21038 100644 --- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc +++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
@@ -138,7 +138,8 @@ point_in_foreign_object.MoveBy(-Layer()->LayoutBoxLocation()); HitTestLocation location(point_in_foreign_object); HitTestResult layer_result(result.GetHitTestRequest(), location); - bool retval = Layer()->HitTest(location, layer_result); + bool retval = Layer()->HitTest(location, layer_result, + LayoutRect(LayoutRect::InfiniteIntRect())); // Preserve the "point in inner node frame" from the original request, // since |layer_result| is a hit test rooted at the <foreignObject> element,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc index 04b1417..fe6dd083 100644 --- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc +++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object_test.cc
@@ -286,7 +286,7 @@ HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive); HitTestLocation location((LayoutPoint(206, 206))); HitTestResult result(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(target, result.InnerNode()); EXPECT_EQ(LayoutPoint(206, 206), result.PointInInnerNodeFrame()); } @@ -310,19 +310,57 @@ const auto& svg = *GetDocument().getElementById("svg"); const auto& target = *GetDocument().getElementById("target"); - const auto& foreignObject = *GetDocument().getElementById("foreignObject"); + const auto& foreign_object = *GetDocument().getElementById("foreignObject"); EXPECT_EQ(svg, GetDocument().ElementFromPoint(1, 1)); - EXPECT_EQ(foreignObject, GetDocument().ElementFromPoint(231, 201)); + EXPECT_EQ(foreign_object, GetDocument().ElementFromPoint(231, 201)); EXPECT_EQ(target, GetDocument().ElementFromPoint(236, 206)); - EXPECT_EQ(foreignObject, GetDocument().ElementFromPoint(235, 255)); + EXPECT_EQ(foreign_object, GetDocument().ElementFromPoint(235, 255)); HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive); HitTestLocation location((LayoutPoint(236, 206))); HitTestResult result(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(target, result.InnerNode()); EXPECT_EQ(LayoutPoint(236, 206), result.PointInInnerNodeFrame()); } +TEST_F(LayoutSVGForeignObjectTest, HitTestUnderScrollingAncestor) { + SetBodyInnerHTML(R"HTML( + <style> + * { + margin: 0 + } + </style> + <div id=scroller style="width: 500px; height: 500px; overflow: auto"> + <svg width="3000" height="3000"> + <foreignObject width="3000" height="3000"> + <div id="target" style="width: 3000px; height: 3000px; background: red"> + </div> + </foreignObject> + </svg> + </div> + )HTML"); + + auto& scroller = *GetDocument().getElementById("scroller"); + const auto& target = *GetDocument().getElementById("target"); + + EXPECT_EQ(target, GetDocument().ElementFromPoint(450, 450)); + + HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive); + HitTestLocation location((LayoutPoint(450, 450))); + HitTestResult result(request, location); + GetDocument().GetLayoutView()->HitTest(location, result); + EXPECT_EQ(target, result.InnerNode()); + EXPECT_EQ(LayoutPoint(450, 450), result.PointInInnerNodeFrame()); + + scroller.setScrollTop(3000); + + EXPECT_EQ(target, GetDocument().ElementFromPoint(450, 450)); + + GetDocument().GetLayoutView()->HitTest(location, result); + EXPECT_EQ(target, result.InnerNode()); + EXPECT_EQ(LayoutPoint(450, 450), result.PointInInnerNodeFrame()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc index a51d051..103f20d4 100644 --- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc +++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -574,12 +574,6 @@ document_loader_ == document_loader_->GetFrame() ->Loader() .GetProvisionalDocumentLoader()) { - FrameClientHintsPreferencesContext hints_context(GetFrame()); - document_loader_->GetClientHintsPreferences() - .UpdateFromAcceptClientHintsHeader( - response.HttpHeaderField(HTTPNames::Accept_CH), response.Url(), - &hints_context); - // When response is received with a provisional docloader, the resource // haven't committed yet, and we cannot load resources, only preconnect. resource_loading_policy = LinkLoader::kDoNotLoadResources; @@ -1329,16 +1323,26 @@ void FrameFetchContext::ParseAndPersistClientHints( const ResourceResponse& response) { - ClientHintsPreferences hints_preferences; - WebEnabledClientHints enabled_client_hints; - TimeDelta persist_duration; FrameClientHintsPreferencesContext hints_context(GetFrame()); - hints_preferences.UpdatePersistentHintsFromHeaders( - response, &hints_context, enabled_client_hints, &persist_duration); + document_loader_->GetClientHintsPreferences() + .UpdateFromAcceptClientHintsLifetimeHeader( + response.HttpHeaderField(HTTPNames::Accept_CH_Lifetime), + response.Url(), &hints_context); + + document_loader_->GetClientHintsPreferences() + .UpdateFromAcceptClientHintsHeader( + response.HttpHeaderField(HTTPNames::Accept_CH), response.Url(), + &hints_context); + + // Notify content settings client of persistent client hints. + TimeDelta persist_duration = + document_loader_->GetClientHintsPreferences().GetPersistDuration(); if (persist_duration.InSeconds() <= 0) return; + WebEnabledClientHints enabled_client_hints = + document_loader_->GetClientHintsPreferences().GetWebEnabledClientHints(); if (!AllowScriptFromSourceWithoutNotifying(response.Url())) { // Do not persist client hint preferences if the JavaScript is disabled. return;
diff --git a/third_party/blink/renderer/core/loader/http_equiv.cc b/third_party/blink/renderer/core/loader/http_equiv.cc index 5a936c7..8931a90 100644 --- a/third_party/blink/renderer/core/loader/http_equiv.cc +++ b/third_party/blink/renderer/core/loader/http_equiv.cc
@@ -47,7 +47,7 @@ kSecurityMessageSource, kErrorMessageLevel, "X-Frame-Options may only be set via an HTTP header sent along with a " "document. It may not be set inside <meta>.")); - } else if (EqualIgnoringASCIICase(equiv, "accept-ch")) { + } else if (EqualIgnoringASCIICase(equiv, HTTPNames::Accept_CH)) { ProcessHttpEquivAcceptCH(document, content); } else if (EqualIgnoringASCIICase(equiv, "content-security-policy") || EqualIgnoringASCIICase(equiv,
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc index ae87746f..e1c94ea 100644 --- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc +++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -66,7 +66,7 @@ const LayoutSelectionStatus& selection_status) { // Expand paint rect if selection covers multiple lines and // this fragment is at the end of line. - if (selection_status.line_break == SelectLineBreak::kNotSelected) + if (selection_status.line_break == SelectSoftLineBreak::kNotSelected) return rect; if (paint_fragment.GetLayoutObject() ->EnclosingNGBlockFlow()
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc index fc924df3..3d32c57 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.cc +++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -1783,14 +1783,6 @@ } } -static inline LayoutRect FrameVisibleRect(LayoutObject& layout_object) { - LocalFrameView* frame_view = layout_object.GetDocument().View(); - if (!frame_view) - return LayoutRect(); - - return LayoutRect(LayoutPoint(), LayoutSize(frame_view->Size())); -} - PaintLayer::HitTestRecursionData::HitTestRecursionData( const LayoutRect& rect_arg, const HitTestLocation& location_arg, @@ -1801,7 +1793,8 @@ intersects_location(location_arg.Intersects(rect_arg)) {} bool PaintLayer::HitTest(const HitTestLocation& hit_test_location, - HitTestResult& result) { + HitTestResult& result, + const LayoutRect& hit_test_area) { DCHECK(IsSelfPaintingLayer() || HasSelfPaintingLayerDescendant()); // LayoutView should make sure to update layout before entering hit testing @@ -1810,15 +1803,6 @@ const HitTestRequest& request = result.GetHitTestRequest(); - // Start with frameVisibleRect to ensure we include the scrollbars. - LayoutRect hit_test_area = FrameVisibleRect(GetLayoutObject()); - if (request.IgnoreClipping()) { - if (LocalFrameView* frame_view = GetLayoutObject().GetDocument().View()) { - hit_test_area.Unite(frame_view->DocumentToFrame( - LayoutRect(GetLayoutObject().View()->DocumentRect()))); - } - } - HitTestRecursionData recursion_data(hit_test_area, hit_test_location, hit_test_location); PaintLayer* inside_layer =
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h index f175445..a4a681f 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.h +++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -422,7 +422,11 @@ // The hitTest() method looks for mouse events by walking layers that // intersect the point from front to back. - bool HitTest(const HitTestLocation& location, HitTestResult&); + // |hit_test_area| is the rect in the space of this PaintLayer's + // LayoutObject to consider for hit testing. + bool HitTest(const HitTestLocation& location, + HitTestResult&, + const LayoutRect& hit_test_area); bool IntersectsDamageRect(const LayoutRect& layer_bounds, const LayoutRect& damage_rect,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_test.cc b/third_party/blink/renderer/core/paint/paint_layer_test.cc index d830509..f7bf202 100644 --- a/third_party/blink/renderer/core/paint/paint_layer_test.cc +++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -1375,21 +1375,21 @@ HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive); HitTestLocation location((LayoutPoint(50, 25))); HitTestResult result(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(child, result.InnerNode()); // Same hit test, with stop node. request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive, hit->GetLayoutObject()); result = HitTestResult(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(hit, result.InnerNode()); // Regular hit test over 'overlap' request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive); location = HitTestLocation((LayoutPoint(50, 75))); result = HitTestResult(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(overlap, result.InnerNode()); // Same hit test, with stop node, should still hit 'overlap' because it's not @@ -1397,7 +1397,7 @@ request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive, hit->GetLayoutObject()); result = HitTestResult(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(overlap, result.InnerNode()); // List-based hit test with stop node @@ -1406,7 +1406,7 @@ hit->GetLayoutObject()); location = HitTestLocation((LayoutRect(40, 15, 20, 20))); result = HitTestResult(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(1u, result.ListBasedTestResult().size()); EXPECT_EQ(hit, *result.ListBasedTestResult().begin()); } @@ -1435,13 +1435,13 @@ HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive); HitTestLocation location((LayoutPoint(50, 50))); HitTestResult result(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(cell11, result.InnerNode()); request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive, table->GetLayoutObject()); result = HitTestResult(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(table, result.InnerNode()); } @@ -1456,13 +1456,13 @@ HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive); HitTestLocation location((LayoutPoint(50, 50))); HitTestResult result(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(circle, result.InnerNode()); request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive, svg->GetLayoutObject()); result = HitTestResult(request, location); - GetDocument().GetLayoutView()->Layer()->HitTest(location, result); + GetDocument().GetLayoutView()->HitTest(location, result); EXPECT_EQ(svg, result.InnerNode()); }
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc b/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc index 69e92d99..805d303 100644 --- a/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc +++ b/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
@@ -115,7 +115,7 @@ // true for shouldPauseAnimation, this will result in the timeline being // suspended. test::RunDelayedTasks(TimeDelta::FromMilliseconds(1) + - TimeDelta::FromSecondsD(timer->NextFireInterval())); + timer->NextFireIntervalDelta()); EXPECT_TRUE(chrome_client.IsSuspended()); EXPECT_FALSE(timer->IsActive()); @@ -149,7 +149,7 @@ // Fire the timer/trigger a frame update. The timeline will remain // suspended and no frame will be scheduled. test::RunDelayedTasks(TimeDelta::FromMillisecondsD(1) + - TimeDelta::FromSecondsD(timer->NextFireInterval())); + timer->NextFireIntervalDelta()); EXPECT_TRUE(chrome_client.IsSuspended()); EXPECT_FALSE(timer->IsActive()); @@ -226,7 +226,7 @@ // Wait for the next animation frame to be triggered, and then trigger a new // frame. The image animation timeline should be running. test::RunDelayedTasks(TimeDelta::FromMilliseconds(1) + - TimeDelta::FromSecondsD(timer->NextFireInterval())); + timer->NextFireIntervalDelta()); Compositor().BeginFrame(); EXPECT_FALSE(svg_image_chrome_client.IsSuspended()); @@ -236,7 +236,7 @@ // animation timeline.) WebView().SetVisibilityState(mojom::PageVisibilityState::kHidden, false); test::RunDelayedTasks(TimeDelta::FromMilliseconds(1) + - TimeDelta::FromSecondsD(timer->NextFireInterval())); + timer->NextFireIntervalDelta()); EXPECT_TRUE(svg_image_chrome_client.IsSuspended()); @@ -244,7 +244,7 @@ // frame and resume the image animation. WebView().SetVisibilityState(mojom::PageVisibilityState::kVisible, false); test::RunDelayedTasks(TimeDelta::FromMilliseconds(1) + - TimeDelta::FromSecondsD(timer->NextFireInterval())); + timer->NextFireIntervalDelta()); Compositor().BeginFrame(); EXPECT_FALSE(svg_image_chrome_client.IsSuspended());
diff --git a/third_party/blink/renderer/core/testing/v8/web_core_test_support.cc b/third_party/blink/renderer/core/testing/v8/web_core_test_support.cc index d6d86754..8995082 100644 --- a/third_party/blink/renderer/core/testing/v8/web_core_test_support.cc +++ b/third_party/blink/renderer/core/testing/v8/web_core_test_support.cc
@@ -36,6 +36,7 @@ #include "third_party/blink/renderer/core/testing/worker_internals.h" #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" #include "third_party/blink/renderer/platform/bindings/origin_trial_features.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" namespace WebCoreTestSupport {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc index 61ed37a8..8a8e790d 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -1665,7 +1665,8 @@ HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive); HitTestLocation location(point); HitTestResult hit_test_result = HitTestResult(request, location); - layer->HitTest(location, hit_test_result); + layer->HitTest(location, hit_test_result, + LayoutRect(LayoutRect::InfiniteIntRect())); Node* node = hit_test_result.InnerNode(); if (!node)
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc index 455d69fee..cea538e5 100644 --- a/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc +++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
@@ -4,6 +4,7 @@ #include "third_party/blink/renderer/modules/animationworklet/animation_worklet.h" +#include "base/atomic_sequence_num.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/core/dom/animation_worklet_proxy_client.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -13,9 +14,18 @@ #include "third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.h" #include "third_party/blink/renderer/modules/animationworklet/animation_worklet_thread.h" +base::AtomicSequenceNumber g_next_worklet_id; + +int NextId() { + // Start id from 1. This way it safe to use it as key in hashmap with default + // key traits. + return g_next_worklet_id.GetNext() + 1; +} + namespace blink { -AnimationWorklet::AnimationWorklet(Document* document) : Worklet(document) {} +AnimationWorklet::AnimationWorklet(Document* document) + : Worklet(document), scope_id_(NextId()), last_animation_id_(0) {} AnimationWorklet::~AnimationWorklet() = default; @@ -31,7 +41,7 @@ Document* document = ToDocument(GetExecutionContext()); AnimationWorkletProxyClient* proxy_client = - AnimationWorkletProxyClientImpl::FromDocument(document); + AnimationWorkletProxyClientImpl::FromDocument(document, scope_id_); WorkerClients* worker_clients = WorkerClients::Create(); ProvideAnimationWorkletProxyClientTo(worker_clients, proxy_client); @@ -42,6 +52,12 @@ return proxy; } +WorkletAnimationId AnimationWorklet::NextWorkletAnimationId() { + // Id starts from 1. This way it safe to use it as key in hashmap with default + // key traits. + return {.scope_id = scope_id_, .animation_id = ++last_animation_id_}; +} + void AnimationWorklet::Trace(blink::Visitor* visitor) { Worklet::Trace(visitor); }
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet.h index 7abefd82..fa072689 100644 --- a/third_party/blink/renderer/modules/animationworklet/animation_worklet.h +++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet.h
@@ -7,6 +7,7 @@ #include "third_party/blink/renderer/core/workers/worklet.h" #include "third_party/blink/renderer/modules/modules_export.h" +#include "third_party/blink/renderer/platform/graphics/compositor_animators_state.h" #include "third_party/blink/renderer/platform/heap/handle.h" namespace blink { @@ -25,9 +26,14 @@ explicit AnimationWorklet(Document*); ~AnimationWorklet() override; + WorkletAnimationId NextWorkletAnimationId(); void Trace(blink::Visitor*) override; private: + // Unique id associated with this worklet that is used by cc to identify all + // animations associated it. + int scope_id_; + int last_animation_id_; // Implements Worklet. bool NeedsToCreateGlobalScope() final;
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc index 9c2640c..d6f18952 100644 --- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc +++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
@@ -21,12 +21,12 @@ void UpdateAnimation(Animator* animator, ScriptState* script_state, - int id, + WorkletAnimationId id, double current_time, CompositorMutatorOutputState* result) { CompositorMutatorOutputState::AnimationState animation_output; if (animator->Animate(script_state, current_time, &animation_output)) { - animation_output.animation_id = id; + animation_output.worklet_animation_id = id; result->animations.push_back(std::move(animation_output)); } } @@ -77,23 +77,22 @@ return animator; } -std::unique_ptr<CompositorMutatorOutputState> -AnimationWorkletGlobalScope::Mutate( - const CompositorMutatorInputState& mutator_input) { +std::unique_ptr<AnimationWorkletOutput> AnimationWorkletGlobalScope::Mutate( + const AnimationWorkletInput& mutator_input) { DCHECK(IsContextThread()); ScriptState* script_state = ScriptController()->GetScriptState(); ScriptState::Scope scope(script_state); - std::unique_ptr<CompositorMutatorOutputState> result = - std::make_unique<CompositorMutatorOutputState>(); + std::unique_ptr<AnimationWorkletOutput> result = + std::make_unique<AnimationWorkletOutput>(); - for (const auto& id : mutator_input.removed_animations) - animators_.erase(id); + for (const auto& worklet_animation_id : mutator_input.removed_animations) + animators_.erase(worklet_animation_id.animation_id); for (const auto& animation : mutator_input.added_and_updated_animations) { - int id = animation.animation_id; - DCHECK(!animators_.at(id)); + int id = animation.worklet_animation_id.animation_id; + DCHECK(!animators_.Contains(id)); const String name = String::FromUTF8(animation.name.data(), animation.name.size()); @@ -105,19 +104,19 @@ if (!animator) continue; - UpdateAnimation(animator, script_state, id, animation.current_time, - result.get()); + UpdateAnimation(animator, script_state, animation.worklet_animation_id, + animation.current_time, result.get()); } for (const auto& animation : mutator_input.updated_animations) { - int id = animation.animation_id; + int id = animation.worklet_animation_id.animation_id; Animator* animator = animators_.at(id); // We don't try to create an animator if there isn't any. if (!animator) continue; - UpdateAnimation(animator, script_state, id, animation.current_time, - result.get()); + UpdateAnimation(animator, script_state, animation.worklet_animation_id, + animation.current_time, result.get()); } return result;
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h index fdce086..cfcb8da 100644 --- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h +++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
@@ -42,8 +42,7 @@ bool IsAnimationWorkletGlobalScope() const final { return true; } // Invokes the |animate| function of all of its active animators. - std::unique_ptr<CompositorMutatorOutputState> Mutate( - const CompositorMutatorInputState&); + std::unique_ptr<AnimationWorkletOutput> Mutate(const AnimationWorkletInput&); // Registers a animator definition with the given name and constructor. void registerAnimator(const String& name,
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc index 7839039..461b599 100644 --- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc +++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
@@ -222,10 +222,12 @@ // Passing a new input state with a new animation id should cause the // worklet to create and animate an animator. - CompositorMutatorInputState state; - state.added_and_updated_animations.emplace_back(1, "test", 5000, nullptr); + cc::WorkletAnimationId animation_id = {1, 1}; + AnimationWorkletInput state; + state.added_and_updated_animations.emplace_back(animation_id, "test", 5000, + nullptr); - std::unique_ptr<CompositorMutatorOutputState> output = + std::unique_ptr<AnimationWorkletOutput> output = global_scope->Mutate(state); EXPECT_TRUE(output); @@ -270,10 +272,12 @@ // Passing a new input state with a new animation id should cause the // worklet to create and animate an animator. - CompositorMutatorInputState state; - state.added_and_updated_animations.emplace_back(1, "test", 5000, nullptr); + cc::WorkletAnimationId animation_id = {1, 1}; + AnimationWorkletInput state; + state.added_and_updated_animations.emplace_back(animation_id, "test", 5000, + nullptr); - std::unique_ptr<CompositorMutatorOutputState> output = + std::unique_ptr<AnimationWorkletOutput> output = global_scope->Mutate(state); EXPECT_TRUE(output); @@ -311,8 +315,8 @@ }); )JS")); - int animation_id = 1; - CompositorMutatorInputState state; + cc::WorkletAnimationId animation_id = {1, 1}; + AnimationWorkletInput state; state.updated_animations.push_back({animation_id, 5000}); EXPECT_EQ(state.added_and_updated_animations.size(), 0u); EXPECT_EQ(state.updated_animations.size(), 1u); @@ -359,8 +363,8 @@ }); )JS")); - int animation_id = 1; - CompositorMutatorInputState state; + cc::WorkletAnimationId animation_id = {1, 1}; + AnimationWorkletInput state; state.added_and_updated_animations.push_back( {animation_id, "test", 5000, nullptr}); EXPECT_EQ(state.added_and_updated_animations.size(), 1u);
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.cc index d7288904..6a763ab 100644 --- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.cc +++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.cc
@@ -14,9 +14,11 @@ namespace blink { AnimationWorkletProxyClientImpl::AnimationWorkletProxyClientImpl( + int scope_id, base::WeakPtr<CompositorMutatorImpl> mutator, scoped_refptr<base::SingleThreadTaskRunner> mutator_runner) - : mutator_(std::move(mutator)), + : scope_id_(scope_id), + mutator_(std::move(mutator)), mutator_runner_(std::move(mutator_runner)), state_(RunState::kUninitialized) { DCHECK(IsMainThread()); @@ -71,29 +73,36 @@ state_ = RunState::kDisposed; } -std::unique_ptr<CompositorMutatorOutputState> -AnimationWorkletProxyClientImpl::Mutate( - const CompositorMutatorInputState& input_state) { +std::unique_ptr<AnimationWorkletOutput> AnimationWorkletProxyClientImpl::Mutate( + std::unique_ptr<AnimationWorkletInput> input) { + DCHECK(input); +#if DCHECK_IS_ON() + DCHECK(input->ValidateScope(scope_id_)) + << "Input has state that does not belong to this global scope: " + << scope_id_; +#endif + if (!global_scope_) return nullptr; - auto output_state = global_scope_->Mutate(input_state); + auto output = global_scope_->Mutate(*input); // TODO(petermayo): https://crbug.com/791280 PostCrossThreadTask to supply // this rather than return it. - return output_state; + return output; } // static AnimationWorkletProxyClientImpl* AnimationWorkletProxyClientImpl::FromDocument( - Document* document) { + Document* document, + int scope_id) { WebLocalFrameImpl* local_frame = WebLocalFrameImpl::FromFrame(document->GetFrame()); scoped_refptr<base::SingleThreadTaskRunner> mutator_queue; base::WeakPtr<CompositorMutatorImpl> mutator = local_frame->LocalRootFrameWidget()->EnsureCompositorMutator( &mutator_queue); - return new AnimationWorkletProxyClientImpl(std::move(mutator), + return new AnimationWorkletProxyClientImpl(scope_id, std::move(mutator), std::move(mutator_queue)); }
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.h index 275a2b7c..a63b30e 100644 --- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.h +++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.h
@@ -35,6 +35,7 @@ // This client is hooked to the given |mutatee|, on the given // |mutatee_runner|. explicit AnimationWorkletProxyClientImpl( + int scope_id, base::WeakPtr<CompositorMutatorImpl> mutatee, scoped_refptr<base::SingleThreadTaskRunner> mutatee_runner); void Trace(blink::Visitor*) override; @@ -45,12 +46,14 @@ // CompositorAnimator: // These methods are invoked on the animation worklet thread. - std::unique_ptr<CompositorMutatorOutputState> Mutate( - const CompositorMutatorInputState&) override; + int GetScopeId() const override { return scope_id_; } + std::unique_ptr<AnimationWorkletOutput> Mutate( + std::unique_ptr<AnimationWorkletInput> input) override; - static AnimationWorkletProxyClientImpl* FromDocument(Document*); + static AnimationWorkletProxyClientImpl* FromDocument(Document*, int scope_id); private: + const int scope_id_; base::WeakPtr<CompositorMutatorImpl> mutator_; scoped_refptr<base::SingleThreadTaskRunner> mutator_runner_;
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc index 473186e..b6f11ab 100644 --- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc +++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -14,13 +14,16 @@ #include "third_party/blink/renderer/core/animation/timing.h" #include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/core/dom/node_computed_style.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/layout/layout_box.h" +#include "third_party/blink/renderer/modules/animationworklet/window_animation_worklet.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink { namespace { + bool ConvertAnimationEffects( const AnimationEffectOrAnimationEffectSequence& effects, HeapVector<Member<KeyframeEffect>>& keyframe_effects, @@ -197,21 +200,26 @@ } // namespace WorkletAnimation* WorkletAnimation::Create( + ExecutionContext* context, String animator_name, const AnimationEffectOrAnimationEffectSequence& effects, ExceptionState& exception_state) { - return Create(animator_name, effects, DocumentTimelineOrScrollTimeline(), - nullptr, exception_state); + return Create(context, animator_name, effects, + DocumentTimelineOrScrollTimeline(), nullptr, exception_state); } WorkletAnimation* WorkletAnimation::Create( + ExecutionContext* context, + String animator_name, const AnimationEffectOrAnimationEffectSequence& effects, DocumentTimelineOrScrollTimeline timeline, ExceptionState& exception_state) { - return Create(animator_name, effects, timeline, nullptr, exception_state); + return Create(context, animator_name, effects, timeline, nullptr, + exception_state); } WorkletAnimation* WorkletAnimation::Create( + ExecutionContext* context, String animator_name, const AnimationEffectOrAnimationEffectSequence& effects, DocumentTimelineOrScrollTimeline timeline, @@ -240,24 +248,31 @@ return nullptr; } + AnimationWorklet* worklet = + WindowAnimationWorklet::animationWorklet(*context->ExecutingWindow()); + + WorkletAnimationId id = worklet->NextWorkletAnimationId(); + Document& document = keyframe_effects.at(0)->target()->GetDocument(); AnimationTimeline* animation_timeline = ConvertAnimationTimeline(document, timeline); WorkletAnimation* animation = - new WorkletAnimation(animator_name, document, keyframe_effects, + new WorkletAnimation(id, animator_name, document, keyframe_effects, animation_timeline, std::move(options)); return animation; } WorkletAnimation::WorkletAnimation( + WorkletAnimationId id, const String& animator_name, Document& document, const HeapVector<Member<KeyframeEffect>>& effects, AnimationTimeline* timeline, scoped_refptr<SerializedScriptValue> options) : sequence_number_(NextSequenceNumber()), + id_(id), animator_name_(animator_name), play_state_(Animation::kIdle), document_(document), @@ -402,7 +417,7 @@ if (!compositor_animation_) { compositor_animation_ = CompositorAnimation::CreateWorkletAnimation( - animator_name_, ToCompositorScrollTimeline(timeline_), + id_, animator_name_, ToCompositorScrollTimeline(timeline_), std::move(options_)); compositor_animation_->SetAnimationDelegate(this); }
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h index 66215939..433b9da2 100644 --- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h +++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
@@ -16,6 +16,7 @@ #include "third_party/blink/renderer/platform/animation/compositor_animation.h" #include "third_party/blink/renderer/platform/animation/compositor_animation_client.h" #include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h" +#include "third_party/blink/renderer/platform/graphics/compositor_animators_state.h" namespace blink { @@ -43,15 +44,18 @@ public: static WorkletAnimation* Create( + ExecutionContext*, String animator_name, const AnimationEffectOrAnimationEffectSequence&, ExceptionState&); static WorkletAnimation* Create( + ExecutionContext*, String animator_name, const AnimationEffectOrAnimationEffectSequence&, DocumentTimelineOrScrollTimeline, ExceptionState&); static WorkletAnimation* Create( + ExecutionContext*, String animator_name, const AnimationEffectOrAnimationEffectSequence&, DocumentTimelineOrScrollTimeline, @@ -106,7 +110,8 @@ void Trace(blink::Visitor*) override; private: - WorkletAnimation(const String& animator_name, + WorkletAnimation(WorkletAnimationId id, + const String& animator_name, Document&, const HeapVector<Member<KeyframeEffect>>&, AnimationTimeline*, @@ -123,6 +128,8 @@ unsigned sequence_number_; + WorkletAnimationId id_; + const String animator_name_; Animation::AnimationPlayState play_state_; // Start time in ms.
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.idl b/third_party/blink/renderer/modules/animationworklet/worklet_animation.idl index da6688a..9c8bca9 100644 --- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.idl +++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.idl
@@ -12,6 +12,7 @@ optional (DocumentTimeline or ScrollTimeline) timeline, optional SerializedScriptValue options), RaisesException=Constructor, + ConstructorCallWith=ExecutionContext, MeasureAs=WorkletAnimationConstructor, OriginTrialEnabled=AnimationWorklet ] interface WorkletAnimation {
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc index 58b9a94..5d36d29 100644 --- a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc +++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
@@ -40,15 +40,17 @@ return KeyframeEffect::Create(element, CreateEffectModel(), timing); } -WorkletAnimation* CreateWorkletAnimation(Element* element) { +WorkletAnimation* CreateWorkletAnimation(ExecutionContext* context, + Element* element) { AnimationEffectOrAnimationEffectSequence effects; AnimationEffect* effect = CreateKeyframeEffect(element); effects.SetAnimationEffect(effect); DocumentTimelineOrScrollTimeline timeline; scoped_refptr<SerializedScriptValue> options; DummyExceptionStateForTesting exception_state; - return WorkletAnimation::Create("WorkletAnimation", effects, timeline, - std::move(options), exception_state); + return WorkletAnimation::Create(context, "WorkletAnimation", effects, + timeline, std::move(options), + exception_state); } } // namespace @@ -61,7 +63,7 @@ void SetUp() override { RenderingTest::SetUp(); element_ = GetDocument().CreateElementForBinding("test"); - worklet_animation_ = CreateWorkletAnimation(element_); + worklet_animation_ = CreateWorkletAnimation(&GetDocument(), element_); } Persistent<Element> element_;
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_storage.cc b/third_party/blink/renderer/modules/cache_storage/cache_storage.cc index 0b6f998..6b54aede 100644 --- a/third_party/blink/renderer/modules/cache_storage/cache_storage.cc +++ b/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
@@ -17,6 +17,7 @@ #include "third_party/blink/renderer/core/fetch/response.h" #include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/modules/cache_storage/cache_storage_error.h" +#include "third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/network/http_names.h" @@ -210,6 +211,18 @@ CacheStorage::CacheStorage(ExecutionContext* context, GlobalFetch::ScopedFetcher* fetcher) : scoped_fetcher_(fetcher) { + // Service workers may already have a CacheStoragePtr provided as an + // optimization. + if (context->IsServiceWorkerGlobalScope()) { + auto* service_worker = ToServiceWorkerGlobalScope(context); + mojom::blink::CacheStoragePtrInfo info = service_worker->TakeCacheStorage(); + if (info) { + cache_storage_ptr_ = RevocableInterfacePtr<mojom::blink::CacheStorage>( + std::move(info), context->GetInterfaceInvalidator()); + return; + } + } + context->GetInterfaceProvider()->GetInterface( MakeRequest(&cache_storage_ptr_, context->GetInterfaceInvalidator())); }
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc index 4367b3b2..5f2d75c 100644 --- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc +++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -77,13 +77,17 @@ std::unique_ptr<WebServiceWorkerInstalledScriptsManager> installed_scripts_manager, mojo::ScopedMessagePipeHandle content_settings_handle, + mojo::ScopedMessagePipeHandle cache_storage, mojo::ScopedMessagePipeHandle interface_provider) { return std::make_unique<WebEmbeddedWorkerImpl>( std::move(client), std::move(installed_scripts_manager), std::make_unique<ServiceWorkerContentSettingsProxy>( // Chrome doesn't use interface versioning. + // TODO(falken): Is that comment about versioning correct? mojom::blink::WorkerContentSettingsProxyPtrInfo( std::move(content_settings_handle), 0u)), + mojom::blink::CacheStoragePtrInfo(std::move(cache_storage), + mojom::blink::CacheStorage::Version_), service_manager::mojom::blink::InterfaceProviderPtrInfo( std::move(interface_provider), service_manager::mojom::blink::InterfaceProvider::Version_)); @@ -94,6 +98,7 @@ std::unique_ptr<WebServiceWorkerInstalledScriptsManager> installed_scripts_manager, std::unique_ptr<ServiceWorkerContentSettingsProxy> content_settings_client, + mojom::blink::CacheStoragePtrInfo cache_storage_info, service_manager::mojom::blink::InterfaceProviderPtrInfo interface_provider_info) : worker_context_client_(std::move(client)), @@ -101,6 +106,7 @@ worker_inspector_proxy_(WorkerInspectorProxy::Create()), pause_after_download_state_(kDontPauseAfterDownload), waiting_for_debugger_state_(kNotWaitingForDebugger), + cache_storage_info_(std::move(cache_storage_info)), interface_provider_info_(std::move(interface_provider_info)) { if (installed_scripts_manager) { installed_scripts_manager_ = @@ -408,7 +414,7 @@ worker_thread_ = std::make_unique<ServiceWorkerThread>( ThreadableLoadingContext::Create(*document), ServiceWorkerGlobalScopeProxy::Create(*this, *worker_context_client_), - std::move(installed_scripts_manager_)); + std::move(installed_scripts_manager_), std::move(cache_storage_info_)); // We have a dummy document here for loading but it doesn't really represent // the document/frame of associated document(s) for this worker. Here we
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h index e4ee059..1111abb 100644 --- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h +++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h
@@ -34,6 +34,7 @@ #include <memory> #include "services/service_manager/public/cpp/interface_provider.h" #include "services/service_manager/public/mojom/interface_provider.mojom-blink.h" +#include "third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom-blink.h" #include "third_party/blink/public/web/web_embedded_worker.h" #include "third_party/blink/public/web/web_embedded_worker_start_data.h" #include "third_party/blink/renderer/core/exported/worker_shadow_page.h" @@ -61,6 +62,7 @@ std::unique_ptr<WebServiceWorkerContextClient>, std::unique_ptr<WebServiceWorkerInstalledScriptsManager>, std::unique_ptr<ServiceWorkerContentSettingsProxy>, + mojom::blink::CacheStoragePtrInfo, service_manager::mojom::blink::InterfaceProviderPtrInfo); ~WebEmbeddedWorkerImpl() override; @@ -129,6 +131,8 @@ // to the same worker. base::UnguessableToken devtools_worker_token_; + mojom::blink::CacheStoragePtrInfo cache_storage_info_; + service_manager::mojom::blink::InterfaceProviderPtrInfo interface_provider_info_; };
diff --git a/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope.cc b/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope.cc index 44b0410..faf1659 100644 --- a/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope.cc +++ b/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope.cc
@@ -75,6 +75,7 @@ ServiceWorkerGlobalScope* ServiceWorkerGlobalScope::Create( ServiceWorkerThread* thread, std::unique_ptr<GlobalScopeCreationParams> creation_params, + mojom::blink::CacheStoragePtrInfo cache_storage_info, base::TimeTicks time_origin) { // If the script is being loaded via script streaming, the script is not yet // loaded. @@ -88,14 +89,17 @@ DCHECK(creation_params->origin_trial_tokens->IsEmpty()); } return new ServiceWorkerGlobalScope(std::move(creation_params), thread, + std::move(cache_storage_info), time_origin); } ServiceWorkerGlobalScope::ServiceWorkerGlobalScope( std::unique_ptr<GlobalScopeCreationParams> creation_params, ServiceWorkerThread* thread, + mojom::blink::CacheStoragePtrInfo cache_storage_info, base::TimeTicks time_origin) - : WorkerGlobalScope(std::move(creation_params), thread, time_origin) {} + : WorkerGlobalScope(std::move(creation_params), thread, time_origin), + cache_storage_info_(std::move(cache_storage_info)) {} ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope() = default; @@ -388,4 +392,8 @@ } } +mojom::blink::CacheStoragePtrInfo ServiceWorkerGlobalScope::TakeCacheStorage() { + return std::move(cache_storage_info_); +} + } // namespace blink
diff --git a/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope.h b/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope.h index 4b9e84ad..cde529f 100644 --- a/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope.h +++ b/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope.h
@@ -31,6 +31,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICEWORKERS_SERVICE_WORKER_GLOBAL_SCOPE_H_ #include <memory> +#include "third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom-blink.h" #include "third_party/blink/public/platform/modules/serviceworker/web_service_worker_registration.h" #include "third_party/blink/renderer/bindings/core/v8/request_or_usv_string.h" #include "third_party/blink/renderer/core/workers/worker_global_scope.h" @@ -60,6 +61,7 @@ static ServiceWorkerGlobalScope* Create( ServiceWorkerThread*, std::unique_ptr<GlobalScopeCreationParams>, + mojom::blink::CacheStoragePtrInfo, base::TimeTicks time_origin); ~ServiceWorkerGlobalScope() override; @@ -117,6 +119,8 @@ void CountCacheStorageInstalledScript(uint64_t script_size, uint64_t script_metadata_size); + mojom::blink::CacheStoragePtrInfo TakeCacheStorage(); + DEFINE_ATTRIBUTE_EVENT_LISTENER(install); DEFINE_ATTRIBUTE_EVENT_LISTENER(activate); DEFINE_ATTRIBUTE_EVENT_LISTENER(fetch); @@ -134,6 +138,7 @@ private: ServiceWorkerGlobalScope(std::unique_ptr<GlobalScopeCreationParams>, ServiceWorkerThread*, + mojom::blink::CacheStoragePtrInfo, base::TimeTicks time_origin); void importScripts(const Vector<String>& urls, ExceptionState&) override; SingleCachedMetadataHandler* CreateWorkerScriptCachedMetadataHandler( @@ -158,6 +163,11 @@ bool evaluate_script_ready_ = false; base::OnceClosure evaluate_script_; + + // May be provided in the constructor as an optimization so InterfaceProvider + // doesn't need to be used. Taken at the initial call to + // ServiceWorkerGlobalScope#caches. + mojom::blink::CacheStoragePtrInfo cache_storage_info_; }; DEFINE_TYPE_CASTS(ServiceWorkerGlobalScope,
diff --git a/third_party/blink/renderer/modules/serviceworkers/service_worker_thread.cc b/third_party/blink/renderer/modules/serviceworkers/service_worker_thread.cc index e8b9c738..92444694f 100644 --- a/third_party/blink/renderer/modules/serviceworkers/service_worker_thread.cc +++ b/third_party/blink/renderer/modules/serviceworkers/service_worker_thread.cc
@@ -44,12 +44,14 @@ ThreadableLoadingContext* loading_context, ServiceWorkerGlobalScopeProxy* global_scope_proxy, std::unique_ptr<ServiceWorkerInstalledScriptsManager> - installed_scripts_manager) + installed_scripts_manager, + mojom::blink::CacheStoragePtrInfo cache_storage_info) : WorkerThread(loading_context, *global_scope_proxy), global_scope_proxy_(global_scope_proxy), worker_backing_thread_(WorkerBackingThread::Create( WebThreadCreationParams(GetThreadType()))), - installed_scripts_manager_(std::move(installed_scripts_manager)) {} + installed_scripts_manager_(std::move(installed_scripts_manager)), + cache_storage_info_(std::move(cache_storage_info)) {} ServiceWorkerThread::~ServiceWorkerThread() { global_scope_proxy_->Detach(); @@ -71,6 +73,7 @@ WorkerOrWorkletGlobalScope* ServiceWorkerThread::CreateWorkerGlobalScope( std::unique_ptr<GlobalScopeCreationParams> creation_params) { return ServiceWorkerGlobalScope::Create(this, std::move(creation_params), + std::move(cache_storage_info_), time_origin_); }
diff --git a/third_party/blink/renderer/modules/serviceworkers/service_worker_thread.h b/third_party/blink/renderer/modules/serviceworkers/service_worker_thread.h index 8ac5a94..8838c787 100644 --- a/third_party/blink/renderer/modules/serviceworkers/service_worker_thread.h +++ b/third_party/blink/renderer/modules/serviceworkers/service_worker_thread.h
@@ -31,6 +31,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICEWORKERS_SERVICE_WORKER_THREAD_H_ #include <memory> +#include "third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom-blink.h" #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" #include "third_party/blink/renderer/core/workers/worker_thread.h" #include "third_party/blink/renderer/modules/modules_export.h" @@ -49,7 +50,8 @@ // Persistent. ServiceWorkerThread(ThreadableLoadingContext*, ServiceWorkerGlobalScopeProxy*, - std::unique_ptr<ServiceWorkerInstalledScriptsManager>); + std::unique_ptr<ServiceWorkerInstalledScriptsManager>, + mojom::blink::CacheStoragePtrInfo cache_storage_info); ~ServiceWorkerThread() override; WorkerBackingThread& GetWorkerBackingThread() override { @@ -71,6 +73,7 @@ std::unique_ptr<WorkerBackingThread> worker_backing_thread_; std::unique_ptr<ServiceWorkerInstalledScriptsManager> installed_scripts_manager_; + mojom::blink::CacheStoragePtrInfo cache_storage_info_; }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/serviceworkers/web_embedded_worker_impl_test.cc b/third_party/blink/renderer/modules/serviceworkers/web_embedded_worker_impl_test.cc index 07a62251..bd1a89d 100644 --- a/third_party/blink/renderer/modules/serviceworkers/web_embedded_worker_impl_test.cc +++ b/third_party/blink/renderer/modules/serviceworkers/web_embedded_worker_impl_test.cc
@@ -126,7 +126,8 @@ mock_installed_scripts_manager_ = installed_scripts_manager.get(); worker_ = WebEmbeddedWorker::Create( std::move(client), std::move(installed_scripts_manager), - mojo::ScopedMessagePipeHandle(), mojo::ScopedMessagePipeHandle()); + mojo::ScopedMessagePipeHandle(), mojo::ScopedMessagePipeHandle(), + mojo::ScopedMessagePipeHandle()); WebURL script_url = URLTestHelpers::ToKURL("https://www.example.com/sw.js"); WebURLResponse response;
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index e0a5e5f..4fc58b3 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -2051,6 +2051,15 @@ } } +test("blink_fuzzer_unittests") { + deps = [ + "//third_party/blink/renderer/platform:test_support", + ] + sources = [ + "testing/run_all_tests.cc", + ] +} + # This source set is used for fuzzers that need an environment similar to unit # tests. jumbo_source_set("blink_fuzzer_test_support") { @@ -2200,6 +2209,7 @@ "graphics/canvas_color_params_test.cc", "graphics/canvas_resource_test.cc", "graphics/color_correction_test_utils.cc", + "graphics/compositor_mutator_impl_test.cc", "graphics/deferred_image_decoder_test.cc", "graphics/gpu/drawing_buffer_software_rendering_test.cc", "graphics/graphics_layer_test.cc",
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.cc b/third_party/blink/renderer/platform/animation/compositor_animation.cc index c7e57b9..79a0b4c 100644 --- a/third_party/blink/renderer/platform/animation/compositor_animation.cc +++ b/third_party/blink/renderer/platform/animation/compositor_animation.cc
@@ -20,12 +20,12 @@ std::unique_ptr<CompositorAnimation> CompositorAnimation::CreateWorkletAnimation( + cc::WorkletAnimationId worklet_animation_id, const String& name, std::unique_ptr<CompositorScrollTimeline> scroll_timeline, std::unique_ptr<cc::AnimationOptions> options) { return std::make_unique<CompositorAnimation>(cc::WorkletAnimation::Create( - cc::AnimationIdProvider::NextAnimationId(), - std::string(name.Ascii().data(), name.length()), + worklet_animation_id, std::string(name.Ascii().data(), name.length()), std::move(scroll_timeline), std::move(options))); }
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.h b/third_party/blink/renderer/platform/animation/compositor_animation.h index 779f6dd..1746ef4 100644 --- a/third_party/blink/renderer/platform/animation/compositor_animation.h +++ b/third_party/blink/renderer/platform/animation/compositor_animation.h
@@ -34,6 +34,7 @@ public: static std::unique_ptr<CompositorAnimation> Create(); static std::unique_ptr<CompositorAnimation> CreateWorkletAnimation( + cc::WorkletAnimationId, const String& name, std::unique_ptr<CompositorScrollTimeline>, std::unique_ptr<cc::AnimationOptions>);
diff --git a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h index 81ae140..edb940c3 100644 --- a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h +++ b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
@@ -39,6 +39,7 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" +#include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" #include "v8/include/v8.h" @@ -46,6 +47,7 @@ class DOMDataStore; class DOMObjectHolderBase; +class ScriptWrappable; // This class represent a collection of DOM wrappers for a specific world. This // is identified by a world id that is a per-thread global identifier (see
diff --git a/third_party/blink/renderer/platform/bindings/script_state.cc b/third_party/blink/renderer/platform/bindings/script_state.cc index 9463657..88b2dac 100644 --- a/third_party/blink/renderer/platform/bindings/script_state.cc +++ b/third_party/blink/renderer/platform/bindings/script_state.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_binding.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/instance_counters.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/bindings/script_state.h b/third_party/blink/renderer/platform/bindings/script_state.h index 87187ff..dccda20 100644 --- a/third_party/blink/renderer/platform/bindings/script_state.h +++ b/third_party/blink/renderer/platform/bindings/script_state.h
@@ -7,8 +7,9 @@ #include <memory> +#include "gin/public/context_holder.h" +#include "gin/public/gin_embedders.h" #include "third_party/blink/renderer/platform/bindings/scoped_persistent.h" -#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" #include "v8/include/v8.h" @@ -17,6 +18,7 @@ class DOMWrapperWorld; class ScriptValue; +class V8PerContextData; // ScriptState is an abstraction class that holds all information about script // exectuion (e.g., v8::Isolate, v8::Context, DOMWrapperWorld, ExecutionContext @@ -163,6 +165,10 @@ // disposePerContextData() once you no longer need V8PerContextData. // Otherwise, the v8::Context will leak. std::unique_ptr<V8PerContextData> per_context_data_; + + static constexpr int kV8ContextPerContextDataIndex = static_cast<int>( + gin::kPerContextDataStartIndex + // NOLINT(readability/enum_casing) + gin::kEmbedderBlink); // NOLINT(readability/enum_casing) }; // ScriptStateProtectingContext keeps the context associated with the
diff --git a/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.cc b/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.cc index 39c7b4c..5a2f20c 100644 --- a/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.cc +++ b/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.cc
@@ -6,6 +6,7 @@ #include <memory> #include "third_party/blink/renderer/platform/bindings/v8_binding.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_context_data.h b/third_party/blink/renderer/platform/bindings/v8_per_context_data.h index 9ecf12d..e75d8bf 100644 --- a/third_party/blink/renderer/platform/bindings/v8_per_context_data.h +++ b/third_party/blink/renderer/platform/bindings/v8_per_context_data.h
@@ -53,12 +53,6 @@ class V8DOMActivityLogger; class V8PerContextData; -enum V8ContextEmbedderDataField { - kV8ContextPerContextDataIndex = static_cast<int>( - gin::kPerContextDataStartIndex + // NOLINT(readability/enum_casing) - gin::kEmbedderBlink), // NOLINT(readability/enum_casing) -}; - // Used to hold data that is associated with a single v8::Context object, and // has a 1:1 relationship with v8::Context. class PLATFORM_EXPORT V8PerContextData final {
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h index 5b22a05..6f207a20 100644 --- a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h +++ b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h
@@ -36,6 +36,7 @@ #include "third_party/blink/renderer/platform/bindings/scoped_persistent.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" +#include "third_party/blink/renderer/platform/bindings/v8_global_value_map.h" #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/platform_export.h"
diff --git a/third_party/blink/renderer/platform/graphics/compositor_animator.h b/third_party/blink/renderer/platform/graphics/compositor_animator.h index f23806bf..5c5bbf9 100644 --- a/third_party/blink/renderer/platform/graphics/compositor_animator.h +++ b/third_party/blink/renderer/platform/graphics/compositor_animator.h
@@ -13,9 +13,12 @@ class PLATFORM_EXPORT CompositorAnimator : public GarbageCollectedMixin { public: + virtual ~CompositorAnimator() = default; + + virtual int GetScopeId() const = 0; // Runs the animation frame callback. - virtual std::unique_ptr<CompositorMutatorOutputState> Mutate( - const CompositorMutatorInputState&) = 0; + virtual std::unique_ptr<AnimationWorkletOutput> Mutate( + std::unique_ptr<AnimationWorkletInput>) = 0; void Trace(blink::Visitor* visitor) override {} };
diff --git a/third_party/blink/renderer/platform/graphics/compositor_animators_state.h b/third_party/blink/renderer/platform/graphics/compositor_animators_state.h index 549a1ae..42e1b7f 100644 --- a/third_party/blink/renderer/platform/graphics/compositor_animators_state.h +++ b/third_party/blink/renderer/platform/graphics/compositor_animators_state.h
@@ -9,9 +9,12 @@ namespace blink { +using AnimationWorkletInput = cc::AnimationWorkletInput; +using AnimationWorkletOutput = cc::AnimationWorkletOutput; using CompositorMutatorInputState = cc::MutatorInputState; using CompositorMutatorOutputState = cc::MutatorOutputState; +using WorkletAnimationId = cc::WorkletAnimationId; } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h b/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h index 77e0819d..827aba8 100644 --- a/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h +++ b/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h
@@ -18,7 +18,7 @@ explicit CompositorMutatorClient(std::unique_ptr<CompositorMutatorImpl>); ~CompositorMutatorClient() override; - void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>); + virtual void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>); // cc::LayerTreeMutator void SetClient(cc::LayerTreeMutatorClient*) override;
diff --git a/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.cc b/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.cc index cc73c3c..0609f3d3 100644 --- a/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.cc +++ b/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.cc
@@ -59,8 +59,13 @@ [](const CompositorAnimators* animators, std::unique_ptr<CompositorMutatorInputState> state, std::unique_ptr<AutoSignal> completion, Outputs* output) { - for (CompositorAnimator* animator : *animators) - output->push_back(animator->Mutate(*state)); + + for (CompositorAnimator* animator : *animators) { + std::unique_ptr<AnimationWorkletInput> worklet_input = + state->TakeWorkletState(animator->GetScopeId()); + if (worklet_input) + output->push_back(animator->Mutate(std::move(worklet_input))); + } }, CrossThreadUnretained(&animators_), WTF::Passed(std::move(state)), WTF::Passed(std::make_unique<AutoSignal>(&done)),
diff --git a/third_party/blink/renderer/platform/graphics/compositor_mutator_impl_test.cc b/third_party/blink/renderer/platform/graphics/compositor_mutator_impl_test.cc new file mode 100644 index 0000000..fb1d1d7 --- /dev/null +++ b/third_party/blink/renderer/platform/graphics/compositor_mutator_impl_test.cc
@@ -0,0 +1,152 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h" + +#include "base/single_thread_task_runner.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/platform/web_thread.h" +#include "third_party/blink/public/platform/web_thread_type.h" +#include "third_party/blink/renderer/platform/graphics/compositor_animator.h" +#include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/testing/testing_platform_support.h" + +#include <memory> + +using ::testing::_; +using ::testing::Mock; +using ::testing::StrictMock; +using ::testing::Return; +using ::testing::Truly; + +// This test uses actual threads since mutator logic requires it. This means we +// have dependency on Blink platform to create threads. + +namespace blink { +namespace { + +std::unique_ptr<WebThread> CreateThread(const char* name) { + return Platform::Current()->CreateThread( + WebThreadCreationParams(WebThreadType::kTestThread) + .SetThreadNameForTest(name)); +} + +class MockCompositorAnimator + : public GarbageCollectedFinalized<MockCompositorAnimator>, + public CompositorAnimator { + USING_GARBAGE_COLLECTED_MIXIN(MockCompositorAnimator); + + public: + MockCompositorAnimator( + scoped_refptr<base::SingleThreadTaskRunner> expected_runner) + : expected_runner_(expected_runner) {} + + ~MockCompositorAnimator() override {} + + std::unique_ptr<AnimationWorkletOutput> Mutate( + std::unique_ptr<AnimationWorkletInput> input) override { + return MutateRef(*input); + } + + MOCK_CONST_METHOD0(GetScopeId, int()); + MOCK_METHOD1( + MutateRef, + std::unique_ptr<AnimationWorkletOutput>(const AnimationWorkletInput&)); + + scoped_refptr<base::SingleThreadTaskRunner> expected_runner_; +}; + +class MockCompositorMutatorClient : public CompositorMutatorClient { + public: + MockCompositorMutatorClient(std::unique_ptr<CompositorMutatorImpl> mutator) + : CompositorMutatorClient(std::move(mutator)) {} + ~MockCompositorMutatorClient() override {} + // gmock cannot mock methods with move-only args so we forward it to ourself. + void SetMutationUpdate( + std::unique_ptr<cc::MutatorOutputState> output_state) override { + SetMutationUpdateRef(output_state.get()); + } + + MOCK_METHOD1(SetMutationUpdateRef, + void(cc::MutatorOutputState* output_state)); +}; + +class CompositorMutatorImplTest : public ::testing::Test { + public: + void SetUp() override { + auto mutator = std::make_unique<CompositorMutatorImpl>(); + mutator_ = mutator.get(); + client_ = + std::make_unique<::testing::StrictMock<MockCompositorMutatorClient>>( + std::move(mutator)); + } + + void TearDown() override { mutator_ = nullptr; } + + std::unique_ptr<::testing::StrictMock<MockCompositorMutatorClient>> client_; + CompositorMutatorImpl* mutator_; +}; + +bool OnlyIncludesAnimation1(const AnimationWorkletInput& in) { + return in.added_and_updated_animations.size() == 1 && + in.added_and_updated_animations[0].worklet_animation_id.animation_id == + 1; +} + +TEST_F(CompositorMutatorImplTest, + RegisteredAnimatorShouldOnlyReceiveInputForItself) { + std::unique_ptr<WebThread> first_thread = CreateThread("FirstThread"); + MockCompositorAnimator* first_animator = + new ::testing::StrictMock<MockCompositorAnimator>( + first_thread->GetTaskRunner()); + + mutator_->RegisterCompositorAnimator(first_animator, + first_thread->GetTaskRunner()); + + EXPECT_CALL(*first_animator, GetScopeId()).Times(1).WillOnce(Return(11)); + EXPECT_CALL(*first_animator, MutateRef(Truly(OnlyIncludesAnimation1))) + .Times(1); + EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1); + + AnimationWorkletInput::AddAndUpdateState state1{ + {11, 1}, "test1", 5000, nullptr}; + + AnimationWorkletInput::AddAndUpdateState state2{ + {22, 2}, "test2", 5000, nullptr}; + + auto input = std::make_unique<CompositorMutatorInputState>(); + input->Add(std::move(state1)); + input->Add(std::move(state2)); + + mutator_->Mutate(std::move(input)); +} + +TEST_F(CompositorMutatorImplTest, AnimatorShouldNotBeMutatedWhenNoInput) { + std::unique_ptr<WebThread> first_thread = CreateThread("FirstThread"); + MockCompositorAnimator* first_animator = + new ::testing::StrictMock<MockCompositorAnimator>( + first_thread->GetTaskRunner()); + + mutator_->RegisterCompositorAnimator(first_animator, + first_thread->GetTaskRunner()); + + EXPECT_CALL(*first_animator, GetScopeId()).Times(1).WillOnce(Return(11)); + EXPECT_CALL(*first_animator, MutateRef(_)).Times(0); + EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0); + + AnimationWorkletInput::AddAndUpdateState state2{ + {22, 2}, "test2", 5000, nullptr}; + + auto input = std::make_unique<CompositorMutatorInputState>(); + input->Add(std::move(state2)); + + mutator_->Mutate(std::move(input)); +} + +} // namespace + +} // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc index 2487c4e5..652088b 100644 --- a/third_party/blink/renderer/platform/heap/thread_state.cc +++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -527,9 +527,16 @@ if ((gc_type == BlinkGC::kV8MajorGC && ShouldForceMemoryPressureGC()) || ShouldScheduleV8FollowupGC()) { - VLOG(2) << "[state:" << this << "] " - << "ScheduleV8FollowupGCIfNeeded: Scheduled precise GC"; - SchedulePreciseGC(); + if (RuntimeEnabledFeatures::HeapIncrementalMarkingEnabled()) { + VLOG(2) << "[state:" << this << "] " + << "ScheduleV8FollowupGCIfNeeded: Scheduled incremental v8 " + "followup GC"; + ScheduleIncrementalGC(BlinkGC::GCReason::kIncrementalV8FollowupGC); + } else { + VLOG(2) << "[state:" << this << "] " + << "ScheduleV8FollowupGCIfNeeded: Scheduled precise GC"; + SchedulePreciseGC(); + } return; } if (gc_type == BlinkGC::kV8MajorGC && ShouldScheduleIdleGC()) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc index 1969cff4..961f89d 100644 --- a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc +++ b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
@@ -6,11 +6,11 @@ #include "base/macros.h" #include "third_party/blink/public/common/client_hints/client_hints.h" -#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" #include "third_party/blink/renderer/platform/network/http_names.h" #include "third_party/blink/renderer/platform/network/http_parsers.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" +#include "third_party/blink/renderer/platform/weborigin/security_origin.h" namespace blink { @@ -98,42 +98,25 @@ } } -// static -void ClientHintsPreferences::UpdatePersistentHintsFromHeaders( - const ResourceResponse& response, - Context* context, - WebEnabledClientHints& enabled_hints, - TimeDelta* persist_duration) { - *persist_duration = base::TimeDelta(); - - if (response.WasCached()) +void ClientHintsPreferences::UpdateFromAcceptClientHintsLifetimeHeader( + const String& header_value, + const KURL& url, + Context* context) { + if (header_value.IsEmpty()) return; - String accept_ch_header_value = - response.HttpHeaderField(HTTPNames::Accept_CH); - String accept_ch_lifetime_header_value = - response.HttpHeaderField(HTTPNames::Accept_CH_Lifetime); - - if (accept_ch_header_value.IsEmpty() || - accept_ch_lifetime_header_value.IsEmpty()) { - return; - } - - const KURL url = response.Url(); + // Client hints should be allowed only on secure URLs. if (!IsClientHintsAllowed(url)) return; bool conversion_ok = false; - int64_t persist_duration_seconds = - accept_ch_lifetime_header_value.ToInt64Strict(&conversion_ok); + int64_t persist_duration_seconds = header_value.ToInt64Strict(&conversion_ok); if (!conversion_ok || persist_duration_seconds <= 0) return; - *persist_duration = TimeDelta::FromSeconds(persist_duration_seconds); + persist_duration_ = TimeDelta::FromSeconds(persist_duration_seconds); if (context) context->CountPersistentClientHintHeaders(); - - ParseAcceptChHeader(accept_ch_header_value, enabled_hints); } // static @@ -143,4 +126,12 @@ SecurityOrigin::Create(url)->IsLocalhost()); } +WebEnabledClientHints ClientHintsPreferences::GetWebEnabledClientHints() const { + return enabled_hints_; +} + +base::TimeDelta ClientHintsPreferences::GetPersistDuration() const { + return persist_duration_; +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h index 6900745..270f8bfd 100644 --- a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h +++ b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h
@@ -14,7 +14,6 @@ namespace blink { class KURL; -class ResourceResponse; // TODO (tbansal): Remove PLATFORM_EXPORT, and pass WebClientHintsType // everywhere. @@ -50,26 +49,25 @@ enabled_hints_.SetIsEnabled(type, true); } - // Parses the client hints headers, and populates |enabled_hints| with the - // client hint preferences that should be persisted for |persist_duration|. - // |persist_duration| should be non-null. - // If there are no client hints that need to be persisted, - // |persist_duration| is not set, otherwise it is set to the duration for - // which the client hint preferences should be persisted. - // UpdatePersistentHintsFromHeaders may be called for all responses - // received (including subresources). |context| may be null. - static void UpdatePersistentHintsFromHeaders( - const ResourceResponse&, - Context*, - WebEnabledClientHints& enabled_hints, - TimeDelta* persist_duration); + // Parses the accept-ch-lifetime header, and populates |this| with the client + // hints persistence duration. |url| is the URL of the resource whose response + // included the |header_value|. |context| may be null. If client hints are not + // allowed for |url|, then |this| would not be updated. + void UpdateFromAcceptClientHintsLifetimeHeader(const String& header_value, + const KURL& url, + Context* context); // Returns true if client hints are allowed for the provided KURL. Client // hints are allowed only on HTTP URLs that belong to secure contexts. static bool IsClientHintsAllowed(const KURL&); + WebEnabledClientHints GetWebEnabledClientHints() const; + + base::TimeDelta GetPersistDuration() const; + private: WebEnabledClientHints enabled_hints_; + base::TimeDelta persist_duration_; }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc index 2eccbff..67dc2fa 100644 --- a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc +++ b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc
@@ -82,6 +82,58 @@ } } +// Verify that the set of enabled client hints is updated every time Update*() +// methods are called. +TEST_F(ClientHintsPreferencesTest, SecureEnabledTypesAreUpdated) { + ClientHintsPreferences preferences; + const KURL kurl(String::FromUTF8("https://www.google.com/")); + preferences.UpdateFromAcceptClientHintsHeader("rtt, downlink", kurl, nullptr); + + EXPECT_EQ(base::TimeDelta(), preferences.GetPersistDuration()); + EXPECT_FALSE( + preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth)); + EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kDpr)); + EXPECT_FALSE( + preferences.ShouldSend(mojom::WebClientHintsType::kViewportWidth)); + EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kRtt)); + EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kDownlink)); + EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kEct)); + + // Calling UpdateFromAcceptClientHintsHeader with empty header should have + // no impact on client hint preferences. + preferences.UpdateFromAcceptClientHintsHeader("", kurl, nullptr); + EXPECT_EQ(base::TimeDelta(), preferences.GetPersistDuration()); + EXPECT_FALSE( + preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth)); + EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kRtt)); + EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kDownlink)); + EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kEct)); + + // Calling UpdateFromAcceptClientHintsHeader with an invalid header should + // have no impact on client hint preferences. + preferences.UpdateFromAcceptClientHintsHeader("foobar", kurl, nullptr); + EXPECT_EQ(base::TimeDelta(), preferences.GetPersistDuration()); + EXPECT_FALSE( + preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth)); + EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kRtt)); + EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kDownlink)); + EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kEct)); + + // Calling UpdateFromAcceptClientHintsHeader with "width" header should + // have no impact on already enabled client hint preferences. + preferences.UpdateFromAcceptClientHintsHeader("width", kurl, nullptr); + EXPECT_EQ(base::TimeDelta(), preferences.GetPersistDuration()); + EXPECT_TRUE( + preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth)); + EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kRtt)); + EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kDownlink)); + EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kEct)); + + preferences.UpdateFromAcceptClientHintsLifetimeHeader("1000", kurl, nullptr); + EXPECT_EQ(base::TimeDelta::FromSeconds(1000), + preferences.GetPersistDuration()); +} + TEST_F(ClientHintsPreferencesTest, Insecure) { for (const auto& use_secure_url : {false, true}) { ClientHintsPreferences preferences; @@ -94,60 +146,83 @@ } } -TEST_F(ClientHintsPreferencesTest, PersistentHints) { +// Verify that the client hints header and the lifetime header is parsed +// correctly. +TEST_F(ClientHintsPreferencesTest, ParseHeaders) { struct TestCase { const char* accept_ch_header_value; const char* accept_lifetime_header_value; int64_t expect_persist_duration_seconds; + bool expect_device_memory; + bool expect_width; + bool expect_dpr; + bool expect_viewport_width; + bool expect_rtt; + bool expect_downlink; + bool expect_ect; } test_cases[] = { - {"width, dpr, viewportWidth", "", 0}, - {"width, dpr, viewportWidth", "-1000", 0}, - {"width, dpr, viewportWidth", "1000s", 0}, - {"width, dpr, viewportWidth", "1000.5", 0}, - {"width, dpr, rtt, downlink, ect", "1000", 1000}, + {"width, dpr, viewportWidth", "", 0, false, true, true, false, false, + false, false}, + {"width, dpr, viewportWidth", "-1000", 0, false, true, true, false, false, + false, false}, + {"width, dpr, viewportWidth", "1000s", 0, false, true, true, false, false, + false, false}, + {"width, dpr, viewportWidth", "1000.5", 0, false, true, true, false, + false, false, false}, + {"width, dpr, rtt, downlink, ect", "1000", 1000, false, true, true, false, + true, true, true}, + {"device-memory", "-1000", 0, true, false, false, false, false, false, + false}, + {"dpr rtt", "1000", 1000, false, false, false, false, false, false, + false}, }; for (const auto& test : test_cases) { - WebEnabledClientHints enabled_types; - TimeDelta persist_duration; + ClientHintsPreferences preferences; + WebEnabledClientHints enabled_types = + preferences.GetWebEnabledClientHints(); + EXPECT_FALSE( + enabled_types.IsEnabled(mojom::WebClientHintsType::kDeviceMemory)); + EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kDpr)); + EXPECT_FALSE( + enabled_types.IsEnabled(mojom::WebClientHintsType::kResourceWidth)); + EXPECT_FALSE( + enabled_types.IsEnabled(mojom::WebClientHintsType::kViewportWidth)); + EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kRtt)); + EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink)); + EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kEct)); + TimeDelta persist_duration = preferences.GetPersistDuration(); + EXPECT_EQ(base::TimeDelta(), persist_duration); const KURL kurl(String::FromUTF8("https://www.google.com/")); + preferences.UpdateFromAcceptClientHintsHeader(test.accept_ch_header_value, + kurl, nullptr); + preferences.UpdateFromAcceptClientHintsLifetimeHeader( + test.accept_lifetime_header_value, kurl, nullptr); - ResourceResponse response(kurl); - response.SetHTTPHeaderField(HTTPNames::Accept_CH, - test.accept_ch_header_value); - response.SetHTTPHeaderField(HTTPNames::Accept_CH_Lifetime, - test.accept_lifetime_header_value); + enabled_types = preferences.GetWebEnabledClientHints(); + persist_duration = preferences.GetPersistDuration(); - ClientHintsPreferences::UpdatePersistentHintsFromHeaders( - response, nullptr, enabled_types, &persist_duration); EXPECT_EQ(test.expect_persist_duration_seconds, persist_duration.InSeconds()); - if (test.expect_persist_duration_seconds > 0) { - EXPECT_FALSE( - enabled_types.IsEnabled(mojom::WebClientHintsType::kDeviceMemory)); - EXPECT_TRUE(enabled_types.IsEnabled(mojom::WebClientHintsType::kDpr)); - EXPECT_TRUE( - enabled_types.IsEnabled(mojom::WebClientHintsType::kResourceWidth)); - EXPECT_FALSE( - enabled_types.IsEnabled(mojom::WebClientHintsType::kViewportWidth)); - EXPECT_TRUE(enabled_types.IsEnabled(mojom::WebClientHintsType::kRtt)); - EXPECT_TRUE( - enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink)); - EXPECT_TRUE(enabled_types.IsEnabled(mojom::WebClientHintsType::kEct)); - } else { - EXPECT_FALSE( - enabled_types.IsEnabled(mojom::WebClientHintsType::kDeviceMemory)); - EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kDpr)); - EXPECT_FALSE( - enabled_types.IsEnabled(mojom::WebClientHintsType::kResourceWidth)); - EXPECT_FALSE( - enabled_types.IsEnabled(mojom::WebClientHintsType::kViewportWidth)); - EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kRtt)); - EXPECT_FALSE( - enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink)); - EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kEct)); - } + + EXPECT_EQ( + test.expect_device_memory, + enabled_types.IsEnabled(mojom::WebClientHintsType::kDeviceMemory)); + EXPECT_EQ(test.expect_dpr, + enabled_types.IsEnabled(mojom::WebClientHintsType::kDpr)); + EXPECT_EQ( + test.expect_width, + enabled_types.IsEnabled(mojom::WebClientHintsType::kResourceWidth)); + EXPECT_EQ( + test.expect_viewport_width, + enabled_types.IsEnabled(mojom::WebClientHintsType::kViewportWidth)); + EXPECT_EQ(test.expect_rtt, + enabled_types.IsEnabled(mojom::WebClientHintsType::kRtt)); + EXPECT_EQ(test.expect_downlink, + enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink)); + EXPECT_EQ(test.expect_ect, + enabled_types.IsEnabled(mojom::WebClientHintsType::kEct)); } }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 998e3c6..0c9d89a0 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -286,6 +286,10 @@ status: "experimental", }, { + name: "CSSLogical", + status: "experimental", + }, + { name: "CSSMaskSourceType", status: "experimental", },
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn index 617858dc..6c27d0c 100644 --- a/third_party/blink/renderer/platform/scheduler/BUILD.gn +++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -8,28 +8,6 @@ blink_platform_sources("scheduler") { sources = [ - "base/graceful_queue_shutdown_helper.cc", - "base/graceful_queue_shutdown_helper.h", - "base/real_time_domain.cc", - "base/real_time_domain.h", - "base/sequence_manager_forward.h", - "base/sequence_manager_impl.cc", - "base/sequence_manager_impl.h", - "base/task_queue.cc", - "base/task_queue_forward.h", - "base/task_queue_impl.cc", - "base/task_queue_impl_forward.h", - "base/task_queue_selector.cc", - "base/task_queue_selector.h", - "base/task_queue_selector_logic.h", - "base/thread_controller_impl.cc", - "base/thread_controller_impl.h", - "base/time_domain.cc", - "base/time_domain_forward.h", - "base/work_queue.cc", - "base/work_queue.h", - "base/work_queue_sets.cc", - "base/work_queue_sets.h", "child/features.h", "child/pollable_thread_safe_flag.cc", "child/pollable_thread_safe_flag.h",
diff --git a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h b/third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h deleted file mode 100644 index e7aa21c..0000000 --- a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h +++ /dev/null
@@ -1,32 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCE_MANAGER_FORWARD_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCE_MANAGER_FORWARD_H_ - -// TODO(kraynov): Remove this header after the move. - -// Must be included first. -#include "third_party/blink/renderer/platform/platform_export.h" - -#define HACKDEF_INCLUDED_FROM_BLINK -// It also includes task_queue_impl.h which has PLATFORM_EXPORT macros. -#include "base/task/sequence_manager/sequence_manager.h" -#undef HACKDEF_INCLUDED_FROM_BLINK - -namespace base { -namespace sequence_manager { - -// Create SequenceManager using MessageLoop on the current thread. -// implementation is located in sequence_manager_impl.cc. -// TODO(kraynov): Move to SequenceManager class. -// TODO(scheduler-dev): Rename to TakeOverCurrentThread when we'll stop using -// MessageLoop and will actually take over a thread. -PLATFORM_EXPORT std::unique_ptr<SequenceManager> -CreateSequenceManagerOnCurrentThread(); - -} // namespace sequence_manager -} // namespace base - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCE_MANAGER_FORWARD_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl_unittest.cc index b160bf8..a39e6d77 100644 --- a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h" +#include "base/task/sequence_manager/sequence_manager_impl.h" #include <stddef.h> #include <memory> @@ -16,6 +16,11 @@ #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" +#include "base/task/sequence_manager/real_time_domain.h" +#include "base/task/sequence_manager/task_queue_impl.h" +#include "base/task/sequence_manager/task_queue_selector.h" +#include "base/task/sequence_manager/work_queue.h" +#include "base/task/sequence_manager/work_queue_sets.h" #include "base/test/simple_test_tick_clock.h" #include "base/test/test_mock_time_task_runner.h" #include "base/test/test_simple_task_runner.h" @@ -24,17 +29,11 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/blame_context.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h" #include "third_party/blink/renderer/platform/scheduler/base/test/mock_time_domain.h" #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" #include "third_party/blink/renderer/platform/scheduler/base/test/test_count_uses_time_source.h" #include "third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.h" #include "third_party/blink/renderer/platform/scheduler/base/test/test_task_time_observer.h" -#include "third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h" using testing::AnyNumber; using testing::Contains;
diff --git a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_perftest.cc b/third_party/blink/renderer/platform/scheduler/base/sequence_manager_perftest.cc index 70bd824..ed72105 100644 --- a/third_party/blink/renderer/platform/scheduler/base/sequence_manager_perftest.cc +++ b/third_party/blink/renderer/platform/scheduler/base/sequence_manager_perftest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" +#include "base/task/sequence_manager/sequence_manager.h" #include <stddef.h> #include <memory> @@ -12,12 +12,12 @@ #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/strings/stringprintf.h" +#include "base/task/sequence_manager/task_queue_impl.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_tick_clock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/perf/perf_test.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" #include "third_party/blink/renderer/platform/scheduler/base/test/mock_time_domain.h" #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" #include "third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.h"
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h b/third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h deleted file mode 100644 index a37f961..0000000 --- a/third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_FORWARD_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_FORWARD_H_ - -// Currently the implementation is located in Blink because scheduler/base move -// is in progress https://crbug.com/783309. -// TODO(kraynov): Remove this header after the move. - -// Must be included first. -#include "third_party/blink/renderer/platform/platform_export.h" - -#define HACKDEF_INCLUDED_FROM_BLINK -#include "base/task/sequence_manager/task_queue.h" -#undef HACKDEF_INCLUDED_FROM_BLINK - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_FORWARD_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h b/third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h deleted file mode 100644 index 11b975f..0000000 --- a/third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_IMPL_FORWARD_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_IMPL_FORWARD_H_ - -// Currently the implementation is located in Blink because scheduler/base move -// is in progress https://crbug.com/783309. -// TODO(kraynov): Remove this header after the move. - -// Must be included first. -#include "third_party/blink/renderer/platform/platform_export.h" - -#define HACKDEF_INCLUDED_FROM_BLINK -#include "base/task/sequence_manager/task_queue_impl.h" -#undef HACKDEF_INCLUDED_FROM_BLINK - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_IMPL_FORWARD_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc b/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc index b7cb6f1..4b9ccd1 100644 --- a/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h" +#include "base/task/sequence_manager/task_queue_selector.h" #include <stddef.h> @@ -12,13 +12,13 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/pending_task.h" +#include "base/task/sequence_manager/task_queue_impl.h" +#include "base/task/sequence_manager/work_queue.h" +#include "base/task/sequence_manager/work_queue_sets.h" #include "base/test/metrics/histogram_tester.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" #include "third_party/blink/renderer/platform/scheduler/base/test/mock_time_domain.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h" using testing::_;
diff --git a/third_party/blink/renderer/platform/scheduler/base/test/mock_time_domain.h b/third_party/blink/renderer/platform/scheduler/base/test/mock_time_domain.h index c395b6c3a..3d3faf48 100644 --- a/third_party/blink/renderer/platform/scheduler/base/test/mock_time_domain.h +++ b/third_party/blink/renderer/platform/scheduler/base/test/mock_time_domain.h
@@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_MOCK_TIME_DOMAIN_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_MOCK_TIME_DOMAIN_H_ -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" +#include "base/task/sequence_manager/time_domain.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.cc b/third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.cc index 832a950..e34c27e 100644 --- a/third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.cc +++ b/third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.cc
@@ -4,7 +4,7 @@ #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" -#include "third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h" +#include "base/task/sequence_manager/thread_controller_impl.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h b/third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h index 460911a..4befcca 100644 --- a/third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h +++ b/third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h
@@ -6,8 +6,8 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_SEQUENCE_MANAGER_FOR_TEST_H_ #include "base/single_thread_task_runner.h" +#include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/time/tick_clock.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h" namespace base { class MessageLoop;
diff --git a/third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.cc b/third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.cc index 5a02791..2db5f5b8 100644 --- a/third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.cc +++ b/third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.cc
@@ -4,7 +4,7 @@ #include "third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" +#include "base/task/sequence_manager/task_queue_impl.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.h b/third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.h index e8c05d6..706372a 100644 --- a/third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.h +++ b/third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.h
@@ -6,7 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_TEST_TASK_QUEUE_H_ #include "base/memory/weak_ptr.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" +#include "base/task/sequence_manager/task_queue.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h b/third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h deleted file mode 100644 index 6b012ab6..0000000 --- a/third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TIME_DOMAIN_FORWARD_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TIME_DOMAIN_FORWARD_H_ - -// Currently the implementation is located in Blink because scheduler/base move -// is in progress https://crbug.com/783309. -// TODO(kraynov): Remove this header after the move. - -// Must be included first. -#include "third_party/blink/renderer/platform/platform_export.h" - -#define HACKDEF_INCLUDED_FROM_BLINK -#include "base/task/sequence_manager/time_domain.h" -#undef HACKDEF_INCLUDED_FROM_BLINK - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TIME_DOMAIN_FORWARD_H_
diff --git a/third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc b/third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc index 476485e..951314f 100644 --- a/third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" +#include "base/task/sequence_manager/time_domain.h" #include <memory> #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/task/sequence_manager/sequence_manager_impl.h" +#include "base/task/sequence_manager/task_queue_impl.h" +#include "base/task/sequence_manager/work_queue.h" #include "base/test/simple_test_tick_clock.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_impl.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" using testing::_; using testing::AnyNumber;
diff --git a/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc b/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc index 62e321a1..d2dc9a1 100644 --- a/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc
@@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h" +#include "base/task/sequence_manager/work_queue_sets.h" #include <stddef.h> #include "base/memory/ptr_util.h" +#include "base/task/sequence_manager/work_queue.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc b/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc index aba858de..b984f84 100644 --- a/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h" +#include "base/task/sequence_manager/work_queue.h" #include <stddef.h> #include <memory> #include "base/bind.h" +#include "base/task/sequence_manager/real_time_domain.h" +#include "base/task/sequence_manager/task_queue_impl.h" +#include "base/task/sequence_manager/work_queue_sets.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h" namespace base { namespace sequence_manager {
diff --git a/third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.cc b/third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.cc index 391c521..8e672dc 100644 --- a/third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.cc +++ b/third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.cc
@@ -8,7 +8,7 @@ #include "base/bind.h" #include "base/location.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" +#include "base/task/sequence_manager/task_queue.h" namespace blink { namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc index 9619174f..7c9a23d 100644 --- a/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc +++ b/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc
@@ -9,9 +9,9 @@ #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/time/default_tick_clock.h" #include "third_party/blink/public/platform/task_type.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.h" #include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy.h" #include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
diff --git a/third_party/blink/renderer/platform/scheduler/common/idle_canceled_delayed_task_sweeper_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/idle_canceled_delayed_task_sweeper_unittest.cc index 611efb0..fbcd2e0 100644 --- a/third_party/blink/renderer/platform/scheduler/common/idle_canceled_delayed_task_sweeper_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/common/idle_canceled_delayed_task_sweeper_unittest.cc
@@ -5,10 +5,10 @@ #include "third_party/blink/renderer/platform/scheduler/common/idle_canceled_delayed_task_sweeper.h" #include "base/task/sequence_manager/lazy_now.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/test/simple_test_tick_clock.h" #include "components/viz/test/ordered_simple_task_runner.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" #include "third_party/blink/renderer/platform/scheduler/common/idle_helper.h" #include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
diff --git a/third_party/blink/renderer/platform/scheduler/common/idle_helper.cc b/third_party/blink/renderer/platform/scheduler/common/idle_helper.cc index eb7d203..6929547 100644 --- a/third_party/blink/renderer/platform/scheduler/common/idle_helper.cc +++ b/third_party/blink/renderer/platform/scheduler/common/idle_helper.cc
@@ -4,12 +4,12 @@ #include "third_party/blink/renderer/platform/scheduler/common/idle_helper.h" +#include "base/task/sequence_manager/sequence_manager.h" +#include "base/task/sequence_manager/task_queue.h" +#include "base/task/sequence_manager/time_domain.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" #include "third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.h" #include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
diff --git a/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc index f8310c7..f2034b0 100644 --- a/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc
@@ -11,14 +11,14 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/task/sequence_manager/sequence_manager.h" +#include "base/task/sequence_manager/task_queue.h" +#include "base/task/sequence_manager/time_domain.h" #include "base/test/simple_test_tick_clock.h" #include "components/viz/test/ordered_simple_task_runner.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" #include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h" #include "third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_helper.h"
diff --git a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h index 42e7ca8..13485052 100644 --- a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h +++ b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h
@@ -6,10 +6,10 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_METRICS_HELPER_H_ #include "base/optional.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/time/time.h" #include "third_party/blink/public/platform/web_thread_type.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h" namespace base {
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc index cef362a9..b1d90e7 100644 --- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc +++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
@@ -6,10 +6,10 @@ #include <utility> +#include "base/task/sequence_manager/task_queue_impl.h" #include "base/time/default_tick_clock.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" #include "third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h index 81963783..29a46162 100644 --- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h +++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
@@ -10,11 +10,10 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "base/task/sequence_manager/sequence_manager.h" #include "base/time/tick_clock.h" #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h" namespace blink { namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc index b029f55..c510e3e7 100644 --- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc
@@ -9,10 +9,10 @@ #include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" #include "base/task/sequence_manager/lazy_now.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/test/scoped_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" #include "third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_helper.h"
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc index 66706e84..75aee0e 100644 --- a/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc
@@ -10,11 +10,11 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/task/sequence_manager/task_queue_impl.h" #include "base/test/simple_test_tick_clock.h" #include "components/viz/test/ordered_simple_task_runner.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h"
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h index 8498c2f25..42269fa 100644 --- a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h +++ b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h
@@ -12,10 +12,10 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" +#include "base/task/sequence_manager/task_queue.h" +#include "base/task/sequence_manager/time_domain.h" #include "base/threading/thread_checker.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" #include "third_party/blink/renderer/platform/scheduler/common/cancelable_closure_holder.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h"
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc index 037ea85..f19fe359 100644 --- a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
@@ -11,12 +11,12 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/task/sequence_manager/sequence_manager.h" +#include "base/task/sequence_manager/task_queue_impl.h" #include "base/test/simple_test_tick_clock.h" #include "components/viz/test/ordered_simple_task_runner.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h b/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h index 8d7bb3e..889a491 100644 --- a/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h +++ b/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h
@@ -6,8 +6,8 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_THROTTLED_TIME_DOMAIN_H_ #include "base/macros.h" +#include "base/task/sequence_manager/time_domain.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" namespace blink { namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h index 09526dc..3a3dbec 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h
@@ -7,9 +7,9 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "base/task/sequence_manager/time_domain.h" #include "base/time/time_override.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/time_domain_forward.h" namespace blink { namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc index ba2c84e2..47efdd6 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc
@@ -6,10 +6,10 @@ #include <memory> #include "base/run_loop.h" +#include "base/task/sequence_manager/sequence_manager.h" #include "base/test/test_mock_time_task_runner.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" #include "third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.h" #include "third_party/blink/renderer/platform/scheduler/base/test/test_task_time_observer.h"
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h index 4afc6ef..5252738 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -11,9 +11,9 @@ #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/trace_event/trace_event.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h index 46768b9b..1f72d60 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h
@@ -7,10 +7,10 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/time/tick_clock.h" #include "cc/base/rolling_time_delta_history.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" namespace blink { namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc index af16e93..a978f394 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc
@@ -7,11 +7,11 @@ #include <memory> #include "base/memory/scoped_refptr.h" +#include "base/task/sequence_manager/sequence_manager.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/test/scoped_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/base/test/sequence_manager_for_test.h" #include "third_party/blink/renderer/platform/scheduler/base/test/test_task_queue.h" #include "third_party/blink/renderer/platform/scheduler/base/test/test_task_time_observer.h"
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc index 9cf061d..28e6c37 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -16,6 +16,7 @@ #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" +#include "base/task/sequence_manager/task_queue_impl.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" @@ -29,7 +30,6 @@ #include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h" #include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" #include "third_party/blink/renderer/platform/scheduler/child/features.h" #include "third_party/blink/renderer/platform/scheduler/child/process_state.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h index 4dc01318..58a5430 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -19,12 +19,12 @@ #include "base/metrics/single_sample_metrics.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/task/sequence_manager/task_time_observer.h" #include "base/trace_event/trace_log.h" #include "build/build_config.h" #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.h" #include "third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.h" #include "third_party/blink/renderer/platform/scheduler/common/idle_canceled_delayed_task_sweeper.h"
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc index 8791700..7bec9d7 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
@@ -4,7 +4,7 @@ #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" +#include "base/task/sequence_manager/task_queue_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h index 50cb991..1bf14a0 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -5,8 +5,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_TASK_QUEUE_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_TASK_QUEUE_H_ -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" +#include "base/task/sequence_manager/task_queue.h" +#include "base/task/sequence_manager/task_queue_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h" @@ -196,6 +196,7 @@ protected: void SetFrameSchedulerForTest(FrameSchedulerImpl* frame_scheduler); + // TODO(kraynov): Consider options to remove TaskQueueImpl reference here. MainThreadTaskQueue( std::unique_ptr<base::sequence_manager::internal::TaskQueueImpl> impl, const Spec& spec,
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h index 7b6f8703..8357b92 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -13,9 +13,9 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/optional.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/time/time.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h" #include "third_party/blink/renderer/platform/scheduler/public/page_lifecycle_state.h"
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h b/third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h index 903d7eb..6caf61e594 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h
@@ -5,8 +5,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PRIORITIZE_COMPOSITING_AFTER_INPUT_EXPERIMENT_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PRIORITIZE_COMPOSITING_AFTER_INPUT_EXPERIMENT_H_ +#include "base/task/sequence_manager/task_queue.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" namespace blink { namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h index 634099d..90a1affe 100644 --- a/third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h +++ b/third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h
@@ -7,8 +7,8 @@ #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" +#include "base/task/sequence_manager/task_queue.h" #include "third_party/blink/public/platform/task_type.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc b/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc index 6cdfe33..09dfa78 100644 --- a/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc +++ b/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc
@@ -5,7 +5,7 @@ #include "third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h" #include "base/location.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" +#include "base/task/sequence_manager/task_queue.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/scheduler/test/lazy_thread_controller_for_test.h b/third_party/blink/renderer/platform/scheduler/test/lazy_thread_controller_for_test.h index 325baf6..0ff6b6d 100644 --- a/third_party/blink/renderer/platform/scheduler/test/lazy_thread_controller_for_test.h +++ b/third_party/blink/renderer/platform/scheduler/test/lazy_thread_controller_for_test.h
@@ -7,8 +7,8 @@ #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/task/sequence_manager/thread_controller_impl.h" #include "base/threading/platform_thread.h" -#include "third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h" // TODO(kraynov): Move to //base/task/sequence_manager/test to avoid // cross-component exposure of internal ThreadControllerImpl.
diff --git a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc index fc4855e..456bd41c 100644 --- a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc +++ b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc
@@ -9,10 +9,10 @@ #include "base/callback.h" #include "base/message_loop/message_loop.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "third_party/blink/public/platform/task_type.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/child/features.h" #include "third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.h" #include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
diff --git a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h index 2c88e6f..dcc5a4e1 100644 --- a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h
@@ -8,11 +8,11 @@ #include <memory> #include "base/macros.h" +#include "base/task/sequence_manager/task_queue.h" #include "third_party/blink/public/platform/scheduler/single_thread_idle_task_runner.h" #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h" #include "third_party/blink/public/platform/web_thread_type.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
diff --git a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_task_queue.cc index 99a9524..8cf71824 100644 --- a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_task_queue.cc +++ b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_task_queue.cc
@@ -4,7 +4,7 @@ #include "third_party/blink/renderer/platform/scheduler/worker/non_main_thread_task_queue.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" +#include "base/task/sequence_manager/task_queue_impl.h" #include "third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_task_queue.h index b247336c..1ca0468 100644 --- a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_task_queue.h +++ b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_task_queue.h
@@ -5,8 +5,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_NON_MAIN_THREAD_TASK_QUEUE_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_NON_MAIN_THREAD_TASK_QUEUE_H_ +#include "base/task/sequence_manager/task_queue.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" namespace blink { namespace scheduler { @@ -16,6 +16,7 @@ class PLATFORM_EXPORT NonMainThreadTaskQueue : public base::sequence_manager::TaskQueue { public: + // TODO(kraynov): Consider options to remove TaskQueueImpl reference here. NonMainThreadTaskQueue( std::unique_ptr<base::sequence_manager::internal::TaskQueueImpl> impl, const Spec& spec,
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc index 927b590..d9625367 100644 --- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc +++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
@@ -10,12 +10,12 @@ #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" +#include "base/task/sequence_manager/sequence_manager.h" +#include "base/task/sequence_manager/task_queue.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "third_party/blink/renderer/platform/histogram.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_forward.h" #include "third_party/blink/renderer/platform/scheduler/child/features.h" #include "third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.cc b/third_party/blink/renderer/platform/testing/testing_platform_support.cc index 9e98ff0..5fac72f6 100644 --- a/third_party/blink/renderer/platform/testing/testing_platform_support.cc +++ b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
@@ -49,7 +49,6 @@ #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h" #include "third_party/blink/renderer/platform/network/http_names.h" #include "third_party/blink/renderer/platform/network/mime/mock_mime_registry.h" -#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_forward.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h" #include "third_party/blink/renderer/platform/wtf/wtf.h"
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc b/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc index a2b3d722..809330e 100644 --- a/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc +++ b/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc
@@ -870,30 +870,6 @@ return rule_status != UBRK_WORD_NONE; } -static TextBreakIterator* SetUpIteratorWithRules(const char* break_rules, - const UChar* string, - int length) { - if (!string) - return nullptr; - - static TextBreakIterator* iterator = nullptr; - if (!iterator) { - UParseError parse_status; - UErrorCode open_status = U_ZERO_ERROR; - // break_rules is ASCII. Pick the most efficient UnicodeString ctor. - iterator = new icu::RuleBasedBreakIterator( - icu::UnicodeString(break_rules, -1, US_INV), parse_status, open_status); - DCHECK(U_SUCCESS(open_status)) - << "ICU could not open a break iterator: " << u_errorName(open_status) - << " (" << open_status << ")"; - if (!iterator) - return nullptr; - } - - SetText16(iterator, string, length); - return iterator; -} - TextBreakIterator* CursorMovementIterator(const UChar* string, int length) { // This rule set is based on character-break iterator rules of ICU 4.0 // <http://source.icu-project.org/repos/icu/icu/tags/release-4-0/source/data/brkitr/char.txt>. @@ -984,7 +960,28 @@ "!!safe_reverse;" "!!safe_forward;"; - return SetUpIteratorWithRules(kRules, string, length); + if (!string) + return nullptr; + + DEFINE_THREAD_SAFE_STATIC_LOCAL( + ThreadSpecific<std::unique_ptr<icu::RuleBasedBreakIterator>>, + thread_specific, ()); + + std::unique_ptr<icu::RuleBasedBreakIterator>& iterator = *thread_specific; + + if (!iterator) { + UParseError parse_status; + UErrorCode open_status = U_ZERO_ERROR; + // break_rules is ASCII. Pick the most efficient UnicodeString ctor. + iterator = std::make_unique<icu::RuleBasedBreakIterator>( + icu::UnicodeString(kRules, -1, US_INV), parse_status, open_status); + DCHECK(U_SUCCESS(open_status)) + << "ICU could not open a break iterator: " << u_errorName(open_status) + << " (" << open_status << ")"; + } + + SetText16(iterator.get(), string, length); + return iterator.get(); } } // namespace blink
diff --git a/third_party/blink/renderer/platform/timer_test.cc b/third_party/blink/renderer/platform/timer_test.cc index 7ff47af..8ede271 100644 --- a/third_party/blink/renderer/platform/timer_test.cc +++ b/third_party/blink/renderer/platform/timer_test.cc
@@ -12,7 +12,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_thread.h" -#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl_forward.h" #include "third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
diff --git a/chrome/third_party/chromevox/BUILD.gn b/third_party/chromevox/BUILD.gn similarity index 100% rename from chrome/third_party/chromevox/BUILD.gn rename to third_party/chromevox/BUILD.gn
diff --git a/chrome/third_party/chromevox/LICENSE b/third_party/chromevox/LICENSE similarity index 100% rename from chrome/third_party/chromevox/LICENSE rename to third_party/chromevox/LICENSE
diff --git a/chrome/third_party/chromevox/OWNERS b/third_party/chromevox/OWNERS similarity index 100% rename from chrome/third_party/chromevox/OWNERS rename to third_party/chromevox/OWNERS
diff --git a/chrome/third_party/chromevox/README.chromium b/third_party/chromevox/README.chromium similarity index 95% rename from chrome/third_party/chromevox/README.chromium rename to third_party/chromevox/README.chromium index dc97bcb..8702dc1 100644 --- a/chrome/third_party/chromevox/README.chromium +++ b/third_party/chromevox/README.chromium
@@ -3,6 +3,7 @@ InfoURL: http://www.chromevox.com/ Version: 1.31.4 License: Apache 2.0 +Security Critical: Yes Description: ChromeVox is the screen reader for Chrome OS. This directory contains pars of
diff --git a/chrome/third_party/chromevox/chromevox/background/chrome_shared2.css b/third_party/chromevox/chromevox/background/chrome_shared2.css similarity index 100% rename from chrome/third_party/chromevox/chromevox/background/chrome_shared2.css rename to third_party/chromevox/chromevox/background/chrome_shared2.css
diff --git a/chrome/third_party/chromevox/chromevox/background/options.css b/third_party/chromevox/chromevox/background/options.css similarity index 100% rename from chrome/third_party/chromevox/chromevox/background/options.css rename to third_party/chromevox/chromevox/background/options.css
diff --git a/chrome/third_party/chromevox/chromevox/background/options_widgets.css b/third_party/chromevox/chromevox/background/options_widgets.css similarity index 100% rename from chrome/third_party/chromevox/chromevox/background/options_widgets.css rename to third_party/chromevox/chromevox/background/options_widgets.css
diff --git a/chrome/third_party/chromevox/chromevox/injected/mathjax.js b/third_party/chromevox/chromevox/injected/mathjax.js similarity index 100% rename from chrome/third_party/chromevox/chromevox/injected/mathjax.js rename to third_party/chromevox/chromevox/injected/mathjax.js
diff --git a/chrome/third_party/chromevox/chromevox/injected/mathjax_external_util.js b/third_party/chromevox/chromevox/injected/mathjax_external_util.js similarity index 100% rename from chrome/third_party/chromevox/chromevox/injected/mathjax_external_util.js rename to third_party/chromevox/chromevox/injected/mathjax_external_util.js
diff --git a/chrome/third_party/chromevox/third_party/closure-library/LICENSE b/third_party/chromevox/third_party/closure-library/LICENSE similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/LICENSE rename to third_party/chromevox/third_party/closure-library/LICENSE
diff --git a/chrome/third_party/chromevox/third_party/closure-library/README.chromium b/third_party/chromevox/third_party/closure-library/README.chromium similarity index 97% rename from chrome/third_party/chromevox/third_party/closure-library/README.chromium rename to third_party/chromevox/third_party/closure-library/README.chromium index f1fb9e0..aa37cc52 100644 --- a/chrome/third_party/chromevox/third_party/closure-library/README.chromium +++ b/third_party/chromevox/third_party/closure-library/README.chromium
@@ -3,6 +3,7 @@ Version: 5a4878ece3dd35230a21d745411ab0985cf99e15 InfoURL: http://developers.google.com/closure/library License: Apache 2.0 +Security Critical: Yes Description: Closure Library is a powerful, low level JavaScript library designed
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/bin/build/depstree.py b/third_party/chromevox/third_party/closure-library/closure/bin/build/depstree.py similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/bin/build/depstree.py rename to third_party/chromevox/third_party/closure-library/closure/bin/build/depstree.py
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/bin/build/source.py b/third_party/chromevox/third_party/closure-library/closure/bin/build/source.py similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/bin/build/source.py rename to third_party/chromevox/third_party/closure-library/closure/bin/build/source.py
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/bin/build/treescan.py b/third_party/chromevox/third_party/closure-library/closure/bin/build/treescan.py similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/bin/build/treescan.py rename to third_party/chromevox/third_party/closure-library/closure/bin/build/treescan.py
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/goog/asserts/asserts.js b/third_party/chromevox/third_party/closure-library/closure/goog/asserts/asserts.js similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/goog/asserts/asserts.js rename to third_party/chromevox/third_party/closure-library/closure/goog/asserts/asserts.js
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/goog/base.js b/third_party/chromevox/third_party/closure-library/closure/goog/base.js similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/goog/base.js rename to third_party/chromevox/third_party/closure-library/closure/goog/base.js
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/goog/debug/error.js b/third_party/chromevox/third_party/closure-library/closure/goog/debug/error.js similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/goog/debug/error.js rename to third_party/chromevox/third_party/closure-library/closure/goog/debug/error.js
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/goog/dom/nodetype.js b/third_party/chromevox/third_party/closure-library/closure/goog/dom/nodetype.js similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/goog/dom/nodetype.js rename to third_party/chromevox/third_party/closure-library/closure/goog/dom/nodetype.js
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js b/third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js rename to third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/ordinalrules.js b/third_party/chromevox/third_party/closure-library/closure/goog/i18n/ordinalrules.js similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/ordinalrules.js rename to third_party/chromevox/third_party/closure-library/closure/goog/i18n/ordinalrules.js
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/pluralrules.js b/third_party/chromevox/third_party/closure-library/closure/goog/i18n/pluralrules.js similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/pluralrules.js rename to third_party/chromevox/third_party/closure-library/closure/goog/i18n/pluralrules.js
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/goog/object/object.js b/third_party/chromevox/third_party/closure-library/closure/goog/object/object.js similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/goog/object/object.js rename to third_party/chromevox/third_party/closure-library/closure/goog/object/object.js
diff --git a/chrome/third_party/chromevox/third_party/closure-library/closure/goog/string/string.js b/third_party/chromevox/third_party/closure-library/closure/goog/string/string.js similarity index 100% rename from chrome/third_party/chromevox/third_party/closure-library/closure/goog/string/string.js rename to third_party/chromevox/third_party/closure-library/closure/goog/string/string.js
diff --git a/chrome/third_party/chromevox/third_party/sre/LICENSE b/third_party/chromevox/third_party/sre/LICENSE similarity index 100% rename from chrome/third_party/chromevox/third_party/sre/LICENSE rename to third_party/chromevox/third_party/sre/LICENSE
diff --git a/chrome/third_party/chromevox/third_party/sre/METADATA b/third_party/chromevox/third_party/sre/METADATA similarity index 100% rename from chrome/third_party/chromevox/third_party/sre/METADATA rename to third_party/chromevox/third_party/sre/METADATA
diff --git a/chrome/third_party/chromevox/third_party/sre/OWNERS b/third_party/chromevox/third_party/sre/OWNERS similarity index 100% rename from chrome/third_party/chromevox/third_party/sre/OWNERS rename to third_party/chromevox/third_party/sre/OWNERS
diff --git a/chrome/third_party/chromevox/third_party/sre/sre_browser.js b/third_party/chromevox/third_party/sre/sre_browser.js similarity index 100% rename from chrome/third_party/chromevox/third_party/sre/sre_browser.js rename to third_party/chromevox/third_party/sre/sre_browser.js
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn index 89491023..c8bd9c6 100644 --- a/third_party/harfbuzz-ng/BUILD.gn +++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -46,7 +46,6 @@ sources = [ "src/src/hb-atomic-private.hh", - "src/src/hb-blob-private.hh", "src/src/hb-blob.cc", "src/src/hb-blob.h", "src/src/hb-buffer-deserialize-json.hh", @@ -158,7 +157,6 @@ "src/src/hb-shaper-list.hh", "src/src/hb-shaper-private.hh", "src/src/hb-shaper.cc", - "src/src/hb-static.cc", "src/src/hb-string-array.hh", "src/src/hb-subset-glyf.hh", "src/src/hb-subset-plan.h", @@ -178,7 +176,6 @@ "HAVE_ICU", "HAVE_ICU_BUILTIN", "HAVE_INTEL_ATOMIC_PRIMITIVES", - "HB_NO_MMAP", ] if (is_component_build) {
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium index 777219af..98165ba 100644 --- a/third_party/harfbuzz-ng/README.chromium +++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,9 +1,9 @@ Name: harfbuzz-ng Short Name: harfbuzz-ng URL: http://harfbuzz.org -Version: 1.8.2 -Date: 20180703 -Revision: 2cb075fe26201f3e370fccfff6c1bc242b5acc79 +Version: 1.7.6 +Date: 20180319 +Revision: 957e7756634a4fdf1654041e20e883cf964ecac9 Security Critical: yes License: MIT License File: src/COPYING @@ -31,26 +31,16 @@ dump-khmer-data.cc dump-myanmar-data.cc dump-use-data.cc - hb-aat-fmtx-table.hh - hb-aat-gcid-table.hh hb-aat-layout-ankr-table.hh - hb-aat-layout-bsln-table.hh hb-aat-layout-common-private.hh - hb-aat-layout-feat-table.hh hb-aat-layout-kerx-table.hh hb-aat-layout-morx-table.hh hb-aat-layout-private.hh hb-aat-layout-trak-table.hh hb-aat-layout.cc - hb-aat-ltag-table.hh hb-directwrite.cc hb-directwrite.h hb-fallback-shape.cc - hb-map-private.hh - hb-map.cc - hb-map.h - hb-ot-color-sbix-table.hh - hb-ot-color-svg-table.hh hb-ot-color.cc hb-subset-glyf.cc hb-subset-input.cc
diff --git a/tools/binary_size/libsupersize/html_report.py b/tools/binary_size/libsupersize/html_report.py index f8d61c7d..076a9639 100644 --- a/tools/binary_size/libsupersize/html_report.py +++ b/tools/binary_size/libsupersize/html_report.py
@@ -241,9 +241,10 @@ small_symbols = {} small_file_node = None if min_symbol_size > 0: + component_index = components.GetOrAdd('') small_file_node = { _COMPACT_FILE_PATH_KEY: _NAME_SMALL_SYMBOL_BUCKET, - _COMPACT_FILE_COMPONENT_INDEX_KEY: components.GetOrAdd(''), + _COMPACT_FILE_COMPONENT_INDEX_KEY: component_index, _COMPACT_FILE_SYMBOLS_KEY: [], } file_nodes[_NAME_SMALL_SYMBOL_BUCKET] = small_file_node
diff --git a/tools/binary_size/libsupersize/template_tree_view/index.html b/tools/binary_size/libsupersize/template_tree_view/index.html index 60cb0d5..48479243 100644 --- a/tools/binary_size/libsupersize/template_tree_view/index.html +++ b/tools/binary_size/libsupersize/template_tree_view/index.html
@@ -1,5 +1,5 @@ <!DOCTYPE html> -<html lang='en'> +<html lang="en"> <!-- Copyright 2018 The Chromium Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be @@ -17,9 +17,9 @@ display: grid; grid-template-columns: auto 0; grid-template-rows: 64px 1fr; - grid-template-areas: 'appbar options' 'symbols options'; + grid-template-areas: "appbar options" "symbols options"; color: #3c4043; - font-family: 'Roboto', sans-serif; + font-family: "Roboto", sans-serif; } .appbar { @@ -46,7 +46,7 @@ .headline { margin: 0; - font-family: 'Google Sans', Arial, sans-serif; + font-family: "Google Sans", Arial, sans-serif; font-weight: normal; color: #202124; font-size: 22px; @@ -59,7 +59,7 @@ } .subhead { - font-family: 'Google Sans', Arial, sans-serif; + font-family: "Google Sans", Arial, sans-serif; font-weight: 500; font-size: 14px; color: #3c4043; @@ -83,7 +83,7 @@ border-bottom: 1px solid #dadce0; } - [role='group'] { + [role="group"] { padding-left: 13px; border-left: 1px solid #dadce0; margin-left: 10px; @@ -107,7 +107,7 @@ background: #f1f3f4; } .node::before { - content: ''; + content: ""; display: inline-block; margin: 10px; width: 0; @@ -122,7 +122,7 @@ border-color: transparent transparent transparent currentColor; transition: transform .1s ease-out; } - [aria-expanded='true']>.node::before { + [aria-expanded="true"]>.node::before { transform: rotate(90deg); } @@ -202,17 +202,35 @@ <label class="checkbox-label" for="methodcount">Show dex method count rather than size</label> </p> + <h2 class="subhead">Group symbols by</h2> + <p class="radio-wrapper"> + <input type="radio" id="sourcepath" name="group_by" value="source_path" checked> + <label class="radio-label" for="sourcepath">Source path</label> + <input type="radio" id="component" name="group_by" value="component"> + <label class="radio-label" for="component">Component</label> + </p> + <p style="text-align:right"> <button class="filled-button" type="submit">Update</button> </p> </form> - <div class='symbols'> - <div hidden id='icons'> - <svg class='icon foldericon' height='24' width='24' fill='#5f6368'> + <div class="symbols"> + <div hidden id="icons"> + <svg class="icon foldericon" height="24" width="24" fill="#5f6368"> <title>Directory</title> <path d="M9.17,6l2,2H20v10L4,18V6H9.17 M10,4H4C2.9,4,2.01,4.9,2.01,6L2,18c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8c0-1.1-0.9-2-2-2 h-8L10,4L10,4z" /> </svg> + <svg class='icon componenticon' height='24' width='24' fill='#5f6368'> + <title>Component</title> + <path d="M9,13.75c-2.34,0-7,1.17-7,3.5V19h14v-1.75C16,14.92,11.34,13.75,9,13.75z M4.34,17c0.84-0.58,2.87-1.25,4.66-1.25 + s3.82,0.67,4.66,1.25H4.34z"/> + <path d="M9,12c1.93,0,3.5-1.57,3.5-3.5C12.5,6.57,10.93,5,9,5S5.5,6.57,5.5,8.5C5.5,10.43,7.07,12,9,12z M9,7 + c0.83,0,1.5,0.67,1.5,1.5S9.83,10,9,10S7.5,9.33,7.5,8.5S8.17,7,9,7z"/> + <path d="M16.04,13.81C17.2,14.65,18,15.77,18,17.25V19h4v-1.75C22,15.23,18.5,14.08,16.04,13.81z"/> + <path d="M15,12c1.93,0,3.5-1.57,3.5-3.5C18.5,6.57,16.93,5,15,5c-0.54,0-1.04,0.13-1.5,0.35c0.63,0.89,1,1.98,1,3.15 + s-0.37,2.26-1,3.15C13.96,11.87,14.46,12,15,12z"/> + </svg> <svg class="icon fileicon" height="24" width="24" fill="#5f6368"> <title>File</title> <path d="M14,2H6C4.9,2,4.01,2.9,4.01,4L4,20c0,1.1,0.89,2,1.99,2H18c1.1,0,2-0.9,2-2V8L14,2z M6,20V4h7v5h5v11 @@ -272,30 +290,29 @@ /> </svg> </div> - <template id="treefolder"> <li role="treeitem" aria-expanded="false"> - <a class="node" href="#"> + <a class="node" href="#" tabindex="-1"> <span class="symbol-name"></span> <span class="size"></span> </a> - <ul role="group" hidden></ul> + <ul role="group"></ul> </li> </template> <template id="treeitem"> <li role="treeitem"> - <span class="node"> + <span class="node" tabindex="-1"> <span class="symbol-name"></span> <span class="size"></span> </span> </li> </template> - <main class='tree-container'> - <header class='tree-header'> - <span class='subtitle'>Name</span> - <span class='subtitle'>{{size_header}}</span> + <main class="tree-container"> + <header class="tree-header"> + <span class="subtitle">Name</span> + <span class="subtitle" id="size-header" data-value="{{size_header}}">{{size_header}}</span> </header> - <ul id='symboltree' class='tree' role='tree' aria-labelledby='headline'></ul> + <ul id="symboltree" class="tree" role="tree" aria-labelledby="headline"></ul> </main> </div> </body>
diff --git a/tools/binary_size/libsupersize/template_tree_view/options.css b/tools/binary_size/libsupersize/template_tree_view/options.css index 4b9af281..6bbfc0b0 100644 --- a/tools/binary_size/libsupersize/template_tree_view/options.css +++ b/tools/binary_size/libsupersize/template_tree_view/options.css
@@ -101,6 +101,7 @@ background: rgba(0, 0, 0, 0.04); } select { + -webkit-appearance: none; font: inherit; background: transparent; border: 0; @@ -120,17 +121,31 @@ color: #5f6368; border-bottom: 1px solid currentColor; } -select:focus+.select-label { +.select-label::after { + content: ''; + position: absolute; + top: calc(50% - 5px); + right: 4px; + margin: 5px 7px 5px 8px; + border-style: solid; + border-width: 5px 5px 0 5px; + border-color: currentColor transparent transparent transparent; + transition: transform 0.2s ease-out; +} +select:focus + .select-label { color: #1a73e8; bottom: -2px; border-width: 2px; } +select:focus + .select-label::after { + transform: rotate(180deg); +} -/** <input type='checkbox'> elements */ -input[type='checkbox'] { +/** <input type='checkbox' or 'radio'> elements */ +input[type='checkbox'], input[type='radio'] { display: none; } -.checkbox-label { +.checkbox-label, .radio-label { display: block; position: relative; padding-left: 34px; @@ -138,19 +153,20 @@ cursor: pointer; font-size: 14px; } -.checkbox-label::before, .checkbox-label::after { +.checkbox-label::before, .checkbox-label::after, +.radio-label::before, .radio-label::after { position: absolute; content: ''; border: 2px solid currentColor; } -.checkbox-label::before { +.checkbox-label::before, .radio-label::before { width: 14px; height: 14px; border-radius: 2px; left: 0; } .checkbox-label::after { - display: none; + opacity: 0; width: 4px; height: 9px; left: 6px; @@ -159,26 +175,41 @@ border-left-width: 0; transform: rotate(45deg); } -input[type='checkbox']:checked + .checkbox-label { +.radio-label::before { + border-radius: 50%; +} +.radio-label::after { + opacity: 0; + width: 4px; + height: 4px; + left: 5px; + top: 5px; + background: currentColor; + border-radius: 50%; +} +input[type='checkbox']:checked + .checkbox-label, +input[type='radio']:checked + .radio-label { color: #1a73e8; } -input[type='checkbox']:checked + .checkbox-label::after { - display: block; +input[type='checkbox']:checked + .checkbox-label::after, +input[type='radio']:checked + .radio-label::after { + opacity: 1; } -input[type='checkbox']:disabled + .checkbox-label { +input[type='checkbox']:disabled + .checkbox-label, +input[type='radio']:disabled + .radio-label { color: #80868b; } /** Tweaks for smaller screen sizes */ @media (max-width: 700px) { .show-options { - grid-template-columns: auto 0; + grid-template-columns: auto 0; } .show-options .scrim { - display: block; + display: block; } .appbar, .symbols { - padding: 0 16px; + padding: 0 16px; } }
diff --git a/tools/binary_size/libsupersize/template_tree_view/state.js b/tools/binary_size/libsupersize/template_tree_view/state.js index 6c29c655..0de25a62 100644 --- a/tools/binary_size/libsupersize/template_tree_view/state.js +++ b/tools/binary_size/libsupersize/template_tree_view/state.js
@@ -28,11 +28,11 @@ * Removes all the existing children of `parent` and inserts * `newChild` in their place * @param {Node} parent - * @param {Node} newChild + * @param {Node | null} newChild */ replace(parent, newChild) { while (parent.firstChild) parent.removeChild(parent.firstChild); - parent.appendChild(newChild); + if (newChild != null) parent.appendChild(newChild); }, }; @@ -42,7 +42,7 @@ * can be manipulated by this object. Keys in the query match with * input names. */ - const _filterParams = new URLSearchParams(location.search.slice(1)); + let _filterParams = new URLSearchParams(location.search.slice(1)); const state = { /** @@ -102,6 +102,15 @@ */ set(name, value) { _filterParams.set(name, value); + state.setAll(_filterParams); + }, + /** + * Replaces the current state with a new list of values. Afterwards + * display the new state in the URL by replacing the current history entry. + * @param {Iterable<[string, string]>} entries Iterator of key-value pairs + */ + setAll(entries) { + _filterParams = new URLSearchParams(entries); history.replaceState(null, null, '?' + _filterParams.toString()); }, }; @@ -111,10 +120,14 @@ if (input.name) { const value = _filterParams.get(input.name); if (value) { - if (input.type === 'checkbox') { - input.checked = value === input.value; - } else { - input.value = value; + switch (input.type) { + case 'checkbox': + case 'radio': + input.checked = value === input.value; + break; + default: + input.value = value; + break; } } } @@ -153,11 +166,21 @@ document.body.classList.add('show-options'); } - // Disable some fields when method_count is set - if (state.has('method_count')) { - document.getElementById('size-header').textContent = 'Methods'; - form.byteunit.setAttribute('disabled', ''); + /** + * Disable some fields when method_count is set + */ + function setMethodCountModeUI() { + const sizeHeader = document.getElementById('size-header'); + if (form.method_count.checked) { + sizeHeader.textContent = 'Methods'; + form.byteunit.setAttribute('disabled', ''); + } else { + sizeHeader.textContent = sizeHeader.dataset.value; + form.byteunit.removeAttribute('disabled', ''); + } } + setMethodCountModeUI(); + form.method_count.addEventListener('change', setMethodCountModeUI); } /** Utilities for working with the state */
diff --git a/tools/binary_size/libsupersize/template_tree_view/tree-worker.js b/tools/binary_size/libsupersize/template_tree_view/tree-worker.js index 9ebe2ce..94bc4986 100644 --- a/tools/binary_size/libsupersize/template_tree_view/tree-worker.js +++ b/tools/binary_size/libsupersize/template_tree_view/tree-worker.js
@@ -61,6 +61,7 @@ const _NO_NAME = '(No path)'; const _DIRECTORY_TYPE = 'D'; +const _COMPONENT_TYPE = 'C'; const _FILE_TYPE = 'F'; /** @@ -71,8 +72,9 @@ * @param {string} sep Path seperator, such as '/'. */ function basename(path, sep) { - const idx = path.lastIndexOf(sep); - return path.substring(idx + 1); + const sepIndex = path.lastIndexOf(sep); + const pathIndex = path.lastIndexOf('/'); + return path.substring(Math.max(sepIndex, pathIndex) + 1); } /** @@ -82,33 +84,9 @@ * @param {string} sep Path seperator, such as '/'. */ function dirname(path, sep) { - const idx = path.lastIndexOf(sep); - return path.substring(0, idx); -} - -/** - * Collapse "java"->"com"->"google" into "java/com/google". Nodes will only be - * collapsed if they are the same type, most commonly by merging directories. - * @param {TreeNode} node Node to potentially collapse. Will be modified by - * this function. - * @param {string} sep Path seperator, such as '/'. - */ -function combineSingleChildNodes(node, sep) { - if (node.children.length > 0) { - const [child] = node.children; - // If there is only 1 child and its the same type, merge it in. - if (node.children.length === 1 && node.type === child.type) { - // size & type should be the same, so don't bother copying them. - node.shortName += sep + '\u200b' + child.shortName; - node.idPath = child.idPath; - node.children = child.children; - // Search children of this node. - combineSingleChildNodes(node, sep); - } else { - // Search children of this node. - node.children.forEach(child => combineSingleChildNodes(child, sep)); - } - } + const sepIndex = path.lastIndexOf(sep); + const pathIndex = path.lastIndexOf('/'); + return path.substring(0, Math.max(sepIndex, pathIndex)); } /** @@ -178,15 +156,23 @@ * @param {(symbol: TreeNode) => boolean} options.filterTest Called to see if * a symbol should be included. If a symbol fails the test, it will not be * attached to the tree. - * @param {string} options.sep Path seperator used to find parent names. - * @param {boolean} options.methodCountMode If true, return number of dex + * @param {string} [options.sep] Path seperator used to find parent names. + * @param {boolean} [options.methodCountMode] If true, return number of dex * methods instead of size. * @returns {TreeNode} Root node of the new tree */ function makeTree(options) { - const {symbols, sep, methodCountMode, getPath, filterTest} = options; + const { + symbols, + getPath, + filterTest, + sep = '/', + methodCountMode = false, + } = options; + if (!getPath) throw new TypeError('Missing getPath'); + const rootNode = createNode( - {idPath: '/', shortName: '/', type: _DIRECTORY_TYPE}, + {idPath: sep, shortName: sep, type: _DIRECTORY_TYPE}, sep ); @@ -213,8 +199,13 @@ // get parent from cache if it exists, otherwise create it parentNode = parents.get(parentPath); if (parentNode == null) { + const useAltType = + node.idPath.lastIndexOf(sep) > node.idPath.lastIndexOf('/'); parentNode = createNode( - {idPath: parentPath, type: _DIRECTORY_TYPE}, + { + idPath: parentPath, + type: useAltType ? _COMPONENT_TYPE : _DIRECTORY_TYPE, + }, sep ); parents.set(parentPath, parentNode); @@ -231,9 +222,13 @@ // its parent directory node, or create it if missing. for (const fileNode of symbols) { // make path for this + const filePath = fileNode[_KEYS.SOURCE_PATH]; const idPath = getPath(fileNode); // make node for this - const node = createNode({idPath, type: _FILE_TYPE}, sep); + const node = createNode( + {idPath, shortName: basename(filePath, sep), type: _FILE_TYPE}, + sep + ); // build child nodes for this file's symbols and attach to self for (const symbol of fileNode[_KEYS.FILE_SYMBOLS]) { const size = methodCountMode ? 1 : symbol[_KEYS.SIZE]; @@ -258,8 +253,6 @@ getOrMakeParentNode(directory); } - // Collapse nodes such as "java"->"com"->"google" into "java/com/google". - combineSingleChildNodes(rootNode, sep); // Sort the tree so that larger items are higher. sortTree(rootNode); @@ -279,7 +272,7 @@ const {tree, options} = JSON.parse(event.data); const params = new URLSearchParams(options); - const sep = params.get('sep') || '/'; + const groupBy = params.get('group_by') || 'source_path'; const methodCountMode = params.has('method_count'); let typeFilter; if (methodCountMode) typeFilter = new Set('m'); @@ -288,11 +281,25 @@ typeFilter = new Set(types.length === 0 ? 'bdrtv*xmpPo' : types); } + const getPathMap = { + component(fileEntry) { + const component = tree.components[fileEntry[_KEYS.COMPONENT_INDEX]]; + const path = getPathMap.source_path(fileEntry); + return (component || '(No component)') + '>' + path; + }, + source_path(fileEntry) { + return fileEntry[_KEYS.SOURCE_PATH]; + }, + }; + const sepMap = { + component: '>', + }; + const rootNode = makeTree({ symbols: tree.file_nodes, - sep, + sep: sepMap[groupBy], methodCountMode, - getPath: s => s[_KEYS.SOURCE_PATH], + getPath: getPathMap[groupBy], filterTest: s => typeFilter.has(s.type), });
diff --git a/tools/binary_size/libsupersize/template_tree_view/ui.js b/tools/binary_size/libsupersize/template_tree_view/ui.js index bbf82ea..ca3a122d 100644 --- a/tools/binary_size/libsupersize/template_tree_view/ui.js +++ b/tools/binary_size/libsupersize/template_tree_view/ui.js
@@ -30,6 +30,7 @@ */ const _SYMBOL_ICONS = { D: _icons.querySelector('.foldericon'), + C: _icons.querySelector('.componenticon'), F: _icons.querySelector('.fileicon'), b: _icons.querySelector('.bssicon'), d: _icons.querySelector('.dataicon'), @@ -48,6 +49,11 @@ const _leafTemplate = document.getElementById('treeitem'); const _treeTemplate = document.getElementById('treefolder'); + const _symbolTree = document.getElementById('symboltree'); + + /** HTMLCollection of all nodes. Updates itself automatically. */ + const _liveNodeList = document.getElementsByClassName('node'); + /** * @type {WeakMap<HTMLElement, Readonly<TreeNode>>} * Associates UI nodes with the corresponding tree data object @@ -113,6 +119,24 @@ } /** + * Sets focus to a new tree element while updating the element that last had + * focus. The tabindex property is used to avoid needing to tab through every + * single tree item in the page to reach other areas. + * @param {number | HTMLElement} el Index of tree node in `_liveNodeList` + */ + function _focusTreeElement(el) { + const lastFocused = document.activeElement; + if (_uiNodeData.has(lastFocused)) { + lastFocused.tabIndex = -1; + } + const element = typeof el === 'number' ? _liveNodeList[el] : el; + if (element != null) { + element.tabIndex = 0; + element.focus(); + } + } + + /** * Click event handler to expand or close the child group of a tree. * @param {Event} event */ @@ -126,17 +150,120 @@ const isExpanded = element.getAttribute('aria-expanded') === 'true'; if (isExpanded) { element.setAttribute('aria-expanded', 'false'); - group.setAttribute('hidden', ''); + dom.replace(group, null); } else { - if (group.children.length === 0) { - const data = _uiNodeData.get(link); - group.appendChild( - dom.createFragment(data.children.map(child => newTreeElement(child))) - ); + const data = _uiNodeData.get(link); + const newElements = data.children.map(child => newTreeElement(child)); + if (newElements.length === 1) { + // Open the inner element if it only has a single child. + // Ensures nodes like "java"->"com"->"google" are opened all at once. + newElements[0].querySelector('.node').click(); } + group.appendChild(dom.createFragment(newElements)); element.setAttribute('aria-expanded', 'true'); - group.removeAttribute('hidden'); + } + } + + /** + * Keydown event handler to move focus for the given element + * @param {KeyboardEvent} event + */ + function _handleKeyNavigation(event) { + const link = event.target; + const focusIndex = Array.prototype.indexOf.call(_liveNodeList, link); + + /** Focus the tree element immediately following this one */ + function _focusNext() { + if (focusIndex > -1 && focusIndex < _liveNodeList.length - 1) { + event.preventDefault(); + _focusTreeElement(focusIndex + 1); + } + } + + /** Open or close the tree element */ + function _toggle() { + event.preventDefault(); + link.click(); + } + + /** Focus the tree element at `index` if it starts with `char` */ + function _focusIfStartsWith(char, index) { + const data = _uiNodeData.get(_liveNodeList[index]); + if (data.shortName.startsWith(char)) { + event.preventDefault(); + _focusTreeElement(index); + return true; + } else { + return false; + } + } + + switch (event.key) { + // Space should act like clicking or pressing enter & toggle the tree. + case ' ': + _toggle(); + break; + // Move to previous focusable node + case 'ArrowUp': + if (focusIndex > 0) { + event.preventDefault(); + _focusTreeElement(focusIndex - 1); + } + break; + // Move to next focusable node + case 'ArrowDown': + _focusNext(); + break; + // If closed tree, open tree. Otherwise, move to first child. + case 'ArrowRight': + if (_uiNodeData.get(link).children.length !== 0) { + const isExpanded = + link.parentElement.getAttribute('aria-expanded') === 'true'; + if (isExpanded) { + _focusNext(); + } else { + _toggle(); + } + } + break; + // If opened tree, close tree. Otherwise, move to parent. + case 'ArrowLeft': + { + const isExpanded = + link.parentElement.getAttribute('aria-expanded') === 'true'; + if (isExpanded) { + _toggle(); + } else { + const groupList = link.parentElement.parentElement; + if (groupList.getAttribute('role') === 'group') { + event.preventDefault(); + _focusTreeElement(groupList.previousElementSibling); + } + } + } + break; + // Focus first node + case 'Home': + event.preventDefault(); + _focusTreeElement(0); + break; + // Focus last node on screen + case 'End': + event.preventDefault(); + _focusTreeElement(_liveNodeList.length - 1); + break; + // If a letter was pressed, find a node starting with that character. + default: + if (event.key.length === 1 && event.key.match(/\S/)) { + for (let i = focusIndex + 1; i < _liveNodeList.length; i++) { + if (_focusIfStartsWith(event.key, i)) return; + } + for (let i = 0; i < focusIndex; i++) { + if (_focusIfStartsWith(event.key, i)) return; + } + } + break; } } @@ -190,7 +317,10 @@ } }); + _symbolTree.addEventListener('keydown', _handleKeyNavigation); + self.newTreeElement = newTreeElement; + self._symbolTree = _symbolTree; } { @@ -208,10 +338,12 @@ */ worker.onmessage = ({data}) => { const root = newTreeElement(data); + const node = root.querySelector('.node'); // Expand the root UI node - root.querySelector('.node').click(); + node.click(); + node.tabIndex = 0; - dom.replace(document.getElementById('symboltree'), root); + dom.replace(_symbolTree, root); }; /** @@ -226,5 +358,11 @@ ); } + form.addEventListener('submit', event => { + event.preventDefault(); + state.setAll(new FormData(event.currentTarget)); + loadTree(tree_data); + }) + self.loadTree = loadTree; }
diff --git a/tools/clang/scripts/generate_compdb.py b/tools/clang/scripts/generate_compdb.py index 146916a..7378093 100755 --- a/tools/clang/scripts/generate_compdb.py +++ b/tools/clang/scripts/generate_compdb.py
@@ -40,7 +40,7 @@ compdb_text = json.dumps( compile_db.ProcessCompileDatabaseIfNeeded( - compile_db.GenerateWithNinja(args.p, args.targets)) + compile_db.GenerateWithNinja(args.p, args.targets))) if args.o is None: print(compdb_text) else:
diff --git a/tools/luci-go/.gitignore b/tools/luci-go/.gitignore index 99085c5..06e95105 100644 --- a/tools/luci-go/.gitignore +++ b/tools/luci-go/.gitignore
@@ -1,5 +1,3 @@ -/isolate -/isolate.exe /linux64/isolate /mac64/isolate /win64/isolate.exe
diff --git a/tools/luci-go/OWNERS b/tools/luci-go/OWNERS index 8b03e48b..3bcc77d 100644 --- a/tools/luci-go/OWNERS +++ b/tools/luci-go/OWNERS
@@ -1,3 +1,5 @@ +djd@chromium.org maruel@chromium.org tandrii@chromium.org +tansell@chromium.org vadimsh@chromium.org
diff --git a/tools/luci-go/README.md b/tools/luci-go/README.md index 5cb0ddf..f53bec17 100644 --- a/tools/luci-go/README.md +++ b/tools/luci-go/README.md
@@ -2,4 +2,9 @@ Contains executable built out of https://chromium.googlesource.com/infra/luci/luci-go/+/master/client/cmd -Mapped through CIPD. + +The binaries are retrieved from the following builders: + +- mac64: http://build.chromium.org/p/chromium.infra/builders/infra-continuous-mac-10.10-64/ +- linux64: http://build.chromium.org/p/chromium.infra/builders/infra-continuous-precise-64/ +- win64: http://build.chromium.org/p/chromium.infra/builders/infra-continuous-win-64/
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 5f0b7f8..1e4b3b2 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -27851,6 +27851,7 @@ <int value="-973509424" label="NewTabPageIcons:disabled"/> <int value="-972737445" label="ArcUseAuthEndpoint:disabled"/> <int value="-972425050" label="gesture-editing"/> + <int value="-970067535" label="BackgroundTaskComponentUpdate:disabled"/> <int value="-969332901" label="stop-non-timers-in-background:disabled"/> <int value="-968010468" label="SharedArrayBuffer:disabled"/> <int value="-966290456" label="WebAuthenticationCtap2:enabled"/> @@ -28389,6 +28390,7 @@ <int value="255375615" label="stop-non-timers-in-background:enabled"/> <int value="259021228" label="OffMainThreadFetch:disabled"/> <int value="262382944" label="GuestViewCrossProcessFrames:disabled"/> + <int value="265830810" label="BackgroundTaskComponentUpdate:enabled"/> <int value="266322815" label="ChromeModernDesign:disabled"/> <int value="266702296" label="disable-plugin-power-saver"/> <int value="268535107" @@ -30249,6 +30251,18 @@ <int value="615" label="border-block-end"/> <int value="616" label="border-inline-start"/> <int value="617" label="border-inline-end"/> + <int value="618" label="margin-block"/> + <int value="619" label="margin-inline"/> + <int value="620" label="padding-block"/> + <int value="621" label="padding-inline"/> + <int value="622" label="border-block-color"/> + <int value="623" label="border-block-style"/> + <int value="624" label="border-block-width"/> + <int value="625" label="border-inline-color"/> + <int value="626" label="border-inline-style"/> + <int value="627" label="border-inline-width"/> + <int value="628" label="border-block"/> + <int value="629" label="border-inline"/> </enum> <enum name="MappedEditingCommands"> @@ -47541,6 +47555,7 @@ <int value="-400619253" label="mus_demo"/> <int value="-254080081" label="cdm"/> <int value="-2816355" label="profile_import"/> + <int value="69152809" label="font_service"/> <int value="357224138" label="user_id"/> <int value="498331100" label="preferences"/> <int value="573935755" label="removable_storage_writer"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 1514768..cd62164 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -85501,26 +85501,6 @@ </summary> </histogram> -<histogram name="Scheduling.BeginMainFrameIntervalCritical2" - units="microseconds"> - <owner>brianderson@chromium.org</owner> - <summary> - This is the time delta between back-to-back BeginMainFrames completions on - the compositor side when the on_critical_path flag is set, regardless of - whether they abort (have no updates) or commit (have updates). - - The interval is only recorded when the BeginMainFrames are running - continuously; sepcifically when another BeginMainFrame is requested by the - next BeginImplFrame after a) an abort or b) activation. - - Warning: This metric may include reports from clients with low-resolution - clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports - will cause this metric to have an abnormal distribution. When considering - revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the - solution. - </summary> -</histogram> - <histogram name="Scheduling.BeginMainFrameIntervalNotCritical" units="microseconds"> <obsolete> @@ -85661,6 +85641,29 @@ </summary> </histogram> +<histogram name="Scheduling.Browser.BeginMainFrameIntervalCritical2" + units="microseconds"> + <obsolete> + Deprecated in 06/2018, M69, due to too much noise in the collected data. + </obsolete> + <owner>brianderson@chromium.org</owner> + <summary> + This is the time delta between back-to-back BeginMainFrames completions on + the compositor side when the on_critical_path flag is set, regardless of + whether they abort (have no updates) or commit (have updates). + + The interval is only recorded when the BeginMainFrames are running + continuously; sepcifically when another BeginMainFrame is requested by the + next BeginImplFrame after a) an abort or b) activation. + + Warning: This metric may include reports from clients with low-resolution + clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports + will cause this metric to have an abnormal distribution. When considering + revising this histogram, see UMA_HISTOGRAM_CUSTOM_HIGH_RESOLUTION_TIMES for + the solution. + </summary> +</histogram> + <histogram name="Scheduling.Browser.BeginMainFrameStartToCommit" units="microseconds"> <obsolete> @@ -85993,6 +85996,26 @@ </summary> </histogram> +<histogram name="Scheduling.Renderer.BeginMainFrameIntervalCritical2" + units="microseconds"> + <owner>brianderson@chromium.org</owner> + <summary> + This is the time delta between back-to-back BeginMainFrames completions on + the compositor side when the on_critical_path flag is set, regardless of + whether they abort (have no updates) or commit (have updates). + + The interval is only recorded when the BeginMainFrames are running + continuously; sepcifically when another BeginMainFrame is requested by the + next BeginImplFrame after a) an abort or b) activation. + + Warning: This metric may include reports from clients with low-resolution + clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports + will cause this metric to have an abnormal distribution. When considering + revising this histogram, see UMA_HISTOGRAM_CUSTOM_HIGH_RESOLUTION_TIMES for + the solution. + </summary> +</histogram> + <histogram name="Scheduling.Renderer.DrawInterval2" units="microseconds"> <owner>brianderson@chromium.org</owner> <summary> @@ -114857,7 +114880,6 @@ <suffix name="Renderer"/> <affected-histogram name="Scheduling.ActivateDuration2"/> <affected-histogram name="Scheduling.BeginImplFrameLatency2"/> - <affected-histogram name="Scheduling.BeginMainFrameIntervalCritical2"/> <affected-histogram name="Scheduling.BeginMainFrameIntervalNotCritical2"/> <affected-histogram name="Scheduling.BeginMainFrameQueueDurationCritical2"/> <affected-histogram
diff --git a/tools/perf/benchmarks/startup_mobile.py b/tools/perf/benchmarks/startup_mobile.py index 2912cf1..ea41431 100644 --- a/tools/perf/benchmarks/startup_mobile.py +++ b/tools/perf/benchmarks/startup_mobile.py
@@ -62,7 +62,12 @@ assert isinstance(self.platform, android_platform.AndroidPlatform) self._finder_options.browser_options.browser_user_agent_type = 'mobile' self.platform.Initialize() - self.platform.network_controller.Open(wpr_modes.WPR_REPLAY) + assert finder_options.browser_options.wpr_mode != wpr_modes.WPR_RECORD, ( + 'Recording WPR archives is not supported for this benchmark.') + wpr_mode = wpr_modes.WPR_REPLAY + if finder_options.use_live_sites: + wpr_mode = wpr_modes.WPR_OFF + self.platform.network_controller.Open(wpr_mode) self._story_set = story_set @property
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config index c2e2f82..0d39a20 100644 --- a/tools/perf/expectations.config +++ b/tools/perf/expectations.config
@@ -42,6 +42,7 @@ crbug.com/574483 [ Android_Svelte ] blink_perf.paint/* [ Skip ] crbug.com/799540 [ Nexus_5 ] blink_perf.paint/* [ Skip ] crbug.com/799540 [ Nexus_7 ] blink_perf.paint/* [ Skip ] +crbug.com/859979 [ Android_Webview ] blink_perf.paint/paint-offset-changes.html [ Skip ] # Benchmark: blink_perf.parser crbug.com/796115 [ Android_One ] blink_perf.parser/html5-full-render.html [ Skip ] @@ -85,6 +86,19 @@ [ Nexus_5X ] loading.mobile/Hongkiat [ Skip ] [ Nexus_5X ] loading.mobile/Dramaq [ Skip ] [ Nexus_7 ] loading.mobile/Facebook [ Skip ] +[ All ] loading.mobile/Bradesco_3g [ Skip ] +[ All ] loading.mobile/Dailymotion_3g [ Skip ] +[ All ] loading.mobile/Dawn_3g [ Skip ] +[ All ] loading.mobile/FlipKart_cold_3g [ Skip ] +[ All ] loading.mobile/G1_3g [ Skip ] +[ All ] loading.mobile/GSShop_3g [ Skip ] +[ All ] loading.mobile/GoogleIndia_3g [ Skip ] +[ All ] loading.mobile/KapanLagi_3g [ Skip ] +[ All ] loading.mobile/Kaskus_3g [ Skip ] +[ All ] loading.mobile/LocalMoxie_3g [ Skip ] +[ All ] loading.mobile/Thairath_3g [ Skip ] +[ All ] loading.mobile/TheStar_3g [ Skip ] +[ All ] loading.mobile/YahooNews_3g [ Skip ] # Benchmark: memory.long_running_idle_gmail_tbmv2 crbug.com/611167 [ Android_Svelte ] memory.long_running_idle_gmail_tbmv2/* [ Skip ] @@ -306,6 +320,9 @@ # Benchmark: media.desktop crbug.com/859660 [ All ] media.desktop/video.html?src=tulip2.vp9.webm_Regular-3G [ Skip ] +# Benchmark: media.mobile +crbug.com/859175 [ Android ] media.mobile/video.html?src=tulip2.vp9.webm_Regular-3G [ Skip ] + # Benchmark: v8.browsing_desktop crbug.com/773084 [ Mac ] v8.browsing_desktop/browse:tools:maps [ Skip ] crbug.com/788796 [ Linux ] v8.browsing_desktop/browse:media:imgur [ Skip ]
diff --git a/tools/perf/page_sets/system_health/system_health_stories.py b/tools/perf/page_sets/system_health/system_health_stories.py index b1eea779..1d087fb 100644 --- a/tools/perf/page_sets/system_health/system_health_stories.py +++ b/tools/perf/page_sets/system_health/system_health_stories.py
@@ -25,7 +25,7 @@ assert platform in platforms.ALL_PLATFORMS - for story_class in _IterAllSystemHealthStoryClasses(): + for story_class in IterAllSystemHealthStoryClasses(): if (story_class.ABSTRACT_STORY or platform not in story_class.SUPPORTED_PLATFORMS or case and not story_class.NAME.startswith(case + ':')): @@ -67,7 +67,12 @@ 'mobile', take_memory_measurement=False) -def _IterAllSystemHealthStoryClasses(): +def IterAllSystemHealthStoryClasses(): + """Generator for system health stories. + + Yields: + All appropriate SystemHealthStory subclasses defining stories. + """ start_dir = os.path.dirname(os.path.abspath(__file__)) # Sort the classes by their names so that their order is stable and # deterministic.
diff --git a/tools/traffic_annotation/scripts/traffic_annotation_auditor_tests.py b/tools/traffic_annotation/scripts/traffic_annotation_auditor_tests.py index 2901c94b..c1345d5a 100755 --- a/tools/traffic_annotation/scripts/traffic_annotation_auditor_tests.py +++ b/tools/traffic_annotation/scripts/traffic_annotation_auditor_tests.py
@@ -91,10 +91,13 @@ _, stderr_text, return_code = self.tools.RunAuditor( args + ["--annotations-file=%s" % temp_filename]) - # TODO(https://crbug.com/844014): Remove after debugging. - print("Return Code: %i" % return_code) - print("File Exists: %i" % os.path.exists(temp_filename)) if os.path.exists(temp_filename): + # When tests are run on all files (without filtering), there might be some + # compile errors in irrelevant files on Windows that can be ignored. + if (return_code and "--no-filtering" in args and + sys.platform.startswith(('win', 'cygwin'))): + print("Ignoring return code: %i" % return_code) + return_code = 0 annotations = None if return_code else open(temp_filename).read() os.remove(temp_filename) else:
diff --git a/ui/file_manager/externs/command_handler_deps.js b/ui/file_manager/externs/command_handler_deps.js index e8342c1..a3647b4 100644 --- a/ui/file_manager/externs/command_handler_deps.js +++ b/ui/file_manager/externs/command_handler_deps.js
@@ -6,7 +6,7 @@ * Interface on which |CommandHandler| depends. * @interface */ -function CommandHandlerDeps(){}; +function CommandHandlerDeps() {} /** * @type {ActionsController} @@ -94,7 +94,7 @@ CommandHandlerDeps.prototype.volumeManager; /** - * @return {DirectoryEntry|FakeEntry} + * @return {DirectoryEntry|FakeEntry|FilesAppEntry} */ CommandHandlerDeps.prototype.getCurrentDirectoryEntry = function() {};
diff --git a/ui/file_manager/externs/volume_info_list.js b/ui/file_manager/externs/volume_info_list.js index d8d25d7..da5715f 100644 --- a/ui/file_manager/externs/volume_info_list.js +++ b/ui/file_manager/externs/volume_info_list.js
@@ -47,7 +47,8 @@ /** * Searches the information of the volume that contains the passed entry. - * @param {!Entry|!FakeEntry} entry Entry on the volume to be found. + * @param {!Entry|!FakeEntry|!FilesAppEntry} entry Entry on the volume to be + * found. * @return {VolumeInfo} The volume's information, or null if not found. */ VolumeInfoList.prototype.findByEntry = function(entry) {};
diff --git a/ui/file_manager/externs/volume_manager.js b/ui/file_manager/externs/volume_manager.js index b0fda5d..ba92628 100644 --- a/ui/file_manager/externs/volume_manager.js +++ b/ui/file_manager/externs/volume_manager.js
@@ -7,7 +7,7 @@ * @interface * @extends {VolumeManagerCommon.VolumeInfoProvider} */ -function VolumeManager() {}; +function VolumeManager() {} /** * The list of VolumeInfo instances for each mounted volume. @@ -59,8 +59,8 @@ /** * Obtains location information from an entry. * - * @param {!Entry|!FakeEntry} entry File or directory entry. It can be a fake - * entry. + * @param {!Entry|!FakeEntry|!FilesAppEntry} entry File or directory entry. It + * can be a fake entry. * @return {EntryLocation} Location information. */ VolumeManager.prototype.getLocationInfo = function(entry) {};
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn index 0e09ce54..6b2d8b3 100644 --- a/ui/file_manager/file_manager/background/js/BUILD.gn +++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -87,6 +87,7 @@ ":launcher_search", ":media_import_handler", ":progress_center", + "../../common/js:files_app_entry_types", "../../common/js:metrics", "../../common/js:util", "../../common/js:volume_manager_common",
diff --git a/ui/file_manager/file_manager/background/js/background_common_scripts.js b/ui/file_manager/file_manager/background/js/background_common_scripts.js index 3a14a116..f791184 100644 --- a/ui/file_manager/file_manager/background/js/background_common_scripts.js +++ b/ui/file_manager/file_manager/background/js/background_common_scripts.js
@@ -15,6 +15,7 @@ // <include src="../../common/js/metrics_base.js"> // <include src="../../common/js/metrics_events.js"> // <include src="../../common/js/metrics.js"> +// <include src="../../common/js/files_app_entry_types.js"> // <include src="../../common/js/util.js"> // <include src="../../common/js/volume_manager_common.js"> // <include src="app_window_wrapper.js">
diff --git a/ui/file_manager/file_manager/background/js/mock_volume_manager.js b/ui/file_manager/file_manager/background/js/mock_volume_manager.js index e757e7a..f4283bd 100644 --- a/ui/file_manager/file_manager/background/js/mock_volume_manager.js +++ b/ui/file_manager/file_manager/background/js/mock_volume_manager.js
@@ -59,7 +59,8 @@ /** * Returns the corresponding VolumeInfo. * - * @param {!Entry|!FakeEntry} entry FileEntry pointing anywhere on a volume. + * @param {!Entry|!FakeEntry|!FilesAppEntry} entry FileEntry pointing anywhere + * on a volume. * @return {VolumeInfo} Corresponding VolumeInfo. */ MockVolumeManager.prototype.getVolumeInfo = function(entry) { @@ -70,7 +71,7 @@ * Obtains location information from an entry. * Current implementation can handle only fake entries. * - * @param {!Entry|!FakeEntry} entry A fake entry. + * @param {!Entry|!FakeEntry|!FilesAppEntry} entry A fake entry. * @return {EntryLocation} Location information. */ MockVolumeManager.prototype.getLocationInfo = function(entry) {
diff --git a/ui/file_manager/file_manager/common/js/BUILD.gn b/ui/file_manager/file_manager/common/js/BUILD.gn index d696ced..72d68243 100644 --- a/ui/file_manager/file_manager/common/js/BUILD.gn +++ b/ui/file_manager/file_manager/common/js/BUILD.gn
@@ -10,6 +10,7 @@ ":closure_compile_externs", ":error_util", ":file_type", + ":files_app_entry_types", ":importer_common", ":lru_cache", ":metrics", @@ -46,6 +47,9 @@ js_library("error_util") { } +js_library("files_app_entry_types") { +} + js_library("file_type") { } @@ -87,6 +91,7 @@ js_library("util") { deps = [ + ":files_app_entry_types", ":volume_manager_common", "//ui/webui/resources/js:load_time_data", "//ui/webui/resources/js:util",
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types.js b/ui/file_manager/file_manager/common/js/files_app_entry_types.js new file mode 100644 index 0000000..8785ceb0 --- /dev/null +++ b/ui/file_manager/file_manager/common/js/files_app_entry_types.js
@@ -0,0 +1,395 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Entry-like types for Files app UI. + * This file defines the interface |FilesAppEntry| and some specialized + * implementations of it. + * + * These entries are intended to behave like the browser native FileSystemEntry + * (aka Entry) and FileSystemDirectoryEntry (aka DirectoryEntry), providing an + * unified API for Files app UI components. UI components should be able to + * display any implementation of FilesAppEntry. + * The main intention of those types is to be able to provide alternative + * implementations and from other sources for "entries", as well as be able to + * extend the native "entry" types. + * + * Native Entry: + * https://developer.mozilla.org/en-US/docs/Web/API/FileSystemEntry + * Native DirectoryEntry: + * https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryReader + */ + +/** + * FilesAppEntry represents a single Entry (file, folder or root) in the Files + * app. Previously, we used the Entry type directly, but this limits the code to + * only work with native Entry type which can't be instantiated in JS. + * For now, Entry and FilesAppEntry should be used interchangeably. + * See also FilesAppDirEntry for a folder-like interface. + * + * TODO(lucmult): Replace uses of Entry with FilesAppEntry implementations. + * + * @interface + */ +class FilesAppEntry { + constructor() { + /** + * @public {!boolean} true if this entry represents a Directory-like entry, + * as in have sub-entries and implements {createReader} method. + * This attribute is defined on Entry. + */ + this.isDirectory = false; + + /** + * @public {!boolean} true if this entry represents a File-like entry. + * Implementations of FilesAppEntry are expected to have this as |true|. + * Whereas implementations of FilesAppDirEntry are expected to have this as + * |false|. + * This attribute is defined on Entry. + */ + this.isFile = true; + + /** + * @public {string} absolute path from the file system's root to the entry. + * It can also be thought of as a path which is relative to the root + * directory, prepended with a "/" character. + * This attribute is defined on Entry. + */ + this.fullPath = ''; + + /** + * @public {string} the name of the entry (the final part of the path, + * after the last. + * This attribute is defined on Entry. + */ + this.name = ''; + + /** + * @public {!string} the class name for this class. It's workaround for the + * fact that an instance created on foreground page and sent to background + * page can't be checked with "instanceof". + */ + this.type_name = 'FilesAppEntry'; + } + + /** + * @param {function(Entry)|function(FilesAppEntry)} success callback. + * @param {function(Entry)|function(FilesAppEntry)} error callback. + * This method is defined on Entry. + */ + getParent(success, error) {} + + /** + * @return {!string} used to compare entries. It should return an unique + * identifier for such entry, usually prefixed with it's root type like: + * "fake-entry://unique/path/to/entry". + * This method is defined on Entry. + */ + toURL() {} + + /** + * Return metadata via |success| callback. Relevant metadata are + * "modificationTime" and "contentMimeType". + * @param {function(Object)} success callback to be called with the result + * metadata. + * @param {function(Object)} error callback to be called in case of error or + * ignored if no error happened. + */ + getMetadata(success, error) {} + + /** + * Returns true if this entry object has a native representation such as Entry + * or DirectoryEntry, this means it can interact with VolumeManager. + * @return {!boolean} + */ + get isNativeType() {} +} + +/** + * A reader compatible with DirectoryEntry.createReader (from Web Standards) + * that reads a static list of entries, provided at construction time. + * https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryReader + * It can be used by DirectoryEntry-like such as EntryList to return its + * children entries. + */ +class StaticReader { + /** + * @param {Array<Entry|FakeEntry|FilesAppEntry>} children: Array of Entry-like + * instances that will be returned/read by this reader. + */ + constructor(children) { + this.children_ = children; + } + + /** + * Reads array of entries via |success| callback. + * + * @param {function(Array<Entry|FilesAppEntry>)} success: A callback that will + * be called multiple times with the entries, last call will be called with an + * empty array indicating that no more entries available. + * @param {function(Array<Entry|FilesAppEntry>)} error: A callback that's + * never called, it's here to match the signature from the Web Standards. + */ + readEntries(success, error) { + let children = this.children_; + // readEntries is suppose to return empty result when there are no more + // files to return, so we clear the children_ attribute for next call. + this.children_ = []; + // Triggers callback asynchronously. + setTimeout(children => success(children), 0, children); + } +} + +/** + * Interface with minimal API shared among different types of FilesAppDirEntry + * and native DirectoryEntry. UI components should be able to display any + * implementation of FilesAppEntry. + * + * FilesAppDirEntry represents a DirectoryEntry-like (folder or root) in the + * Files app. It's a specialization of FilesAppEntry extending the behavior for + * folder, which is basically the method createReader. + * As in FilesAppEntry, FilesAppDirEntry should be interchangeable with Entry + * and DirectoryEntry. + * + * @interface + */ +class FilesAppDirEntry extends FilesAppEntry { + constructor() { + super(); + /** + * @public {!boolean} true if this entry represents a Directory-like entry, + * as in have sub-entries and implements {createReader} method. + * Implementations of FilesAppEntry are expected to have this as |true|. + * This attribute is defined on Entry. + */ + this.isDirectory = true; + this.type_name = 'FilesAppDirEntry'; + } + + /** + * @return {!StaticReader|!DirectoryReader} Returns a reader compatible with + * DirectoryEntry.createReader (from Web Standards) that reads the children of + * this instance. + * This method is defined on DirectoryEntry. + */ + createReader() {} +} + +/** + * EntryList, a DirectoryEntry-like object that contains entries. Initially used + * to implement "My Files" containing VolumeEntry for "Downloads", "Linux + * Files" and "Play Files". + * + * @implements FilesAppDirEntry + */ +class EntryList { + /** + * @param {string} label: Label to be used when displaying to user, it should + * already translated. + * @param {VolumeManagerCommon.RootType} rootType root type. + * + */ + constructor(label, rootType) { + /** + * @private {string} label: Label to be used when displaying to user, it + * should be already translated. */ + this.label_ = label; + + /** @private {VolumeManagerCommon.RootType} rootType root type. */ + this.rootType_ = rootType; + + /** + * @private {!Array<!Entry|!FilesAppEntry|!FakeEntry>} children entries of + * this EntryList instance. + */ + this.children_ = []; + + this.isDirectory = true; + this.isFile = false; + this.type_name = 'EntryList'; + } + + get children() { + return this.children_; + } + + get label() { + return this.label_; + } + + get rootType() { + return this.rootType_; + } + + get name() { + return this.label_; + } + + /** @override */ + get isNativeType() { + return false; + } + + /** @override */ + getMetadata(success, error) { + // Defaults modificationTime to current time just to have a valid value. + setTimeout(() => success({modificationTime: new Date()})); + } + + /** + * @return {!string} used to compare entries. + * @override + */ + toURL() { + return 'entry-list://' + this.rootType; + } + + /** + * @param {function(Entry)|function(FilesAppEntry)} success callback, it + * returns itself since EntryList is intended to be used as root node and the + * Web Standard says to do so. + * @param {function(Entry)|function(FilesAppEntry)} error callback, not used + * for this implementation. + * + * @override + */ + getParent(success, error) { + setTimeout(success, 0, this); + } + + /** + * @param {!Entry|!FakeEntry|!FilesAppEntry} entry that should be added as + * child of this EntryList. + * This method is specific to EntryList instance. + */ + addEntry(entry) { + this.children_.push(entry); + } + + /** + * @return {!StaticReader} Returns a reader compatible with + * DirectoryEntry.createReader (from Web Standards) that reads the children of + * this EntryList instance. + * This method is defined on DirectoryEntry. + * @override + */ + createReader() { + return new StaticReader(this.children_); + } +} + +/** + * A DirectoryEntry-like which represents a Volume, based on VolumeInfo. + * + * It uses composition to behave like a DirectoryEntry and proxies some calls + * to its VolumeInfo instance. + * + * It's used to be able to add a volume as child of |EntryList| and make volume + * displayable on file list. + * + * @implements FilesAppDirEntry + */ +class VolumeEntry { + /** + * @param {!VolumeInfo} volumeInfo: VolumeInfo for this entry. + */ + constructor(volumeInfo) { + /** + * @private {!VolumeInfo} holds a reference to VolumeInfo to delegate some + * method calls to it. + */ + this.volumeInfo_ = volumeInfo; + + /** @type {DirectoryEntry} from Volume's root. */ + this.rootEntry_ = volumeInfo.displayRoot; + this.type_name = 'VolumeEntry'; + } + + /** + * @return {!VolumeInfo} for this entry. This method is only valid for + * VolumeEntry instances. + */ + get volumeInfo() { + return this.volumeInfo_; + } + /** + * @return {DirectoryEntry} for this volume. This method is only valid for + * VolumeEntry instances. + */ + get rootEntry() { + return this.rootEntry_; + } + + /** + * @return {!FileSystem} FileSystem for this volume. + * This method is defined on Entry. + */ + get filesystem() { + return this.rootEntry_.filesystem; + } + + /** + * @return {!string} Full path for this volume. + * This method is defined on Entry. + * @override. + */ + get fullPath() { + return this.rootEntry_.fullPath; + } + get isDirectory() { + return this.rootEntry_.isDirectory; + } + get isFile() { + return this.rootEntry_.isFile; + } + + /** + * @return {!string} Name for this volume. + * @override. + */ + get name() { + return this.volumeInfo_.label; + } + + /** + * @return {string} + * @override + */ + toURL() { + return this.rootEntry_.toURL(); + } + + /** + * @param {function(Entry)|function(FilesAppEntry)} success callback, it + * returns itself since EntryList is intended to be used as root node and the + * Web Standard says to do so. + * @param {function(Entry)|function(FilesAppEntry)} error callback, not used + * for this implementation. + * + * @override + */ + getParent(success, error) { + setTimeout(success, 0, this); + } + + /** @override */ + getMetadata(success, error) { + this.rootEntry_.getMetadata(success, error); + } + + /** @override */ + get isNativeType() { + return true; + } + + /** + * @return {!StaticReader|!DirectoryReader} Returns a reader from root entry, + * which is compatible with DirectoryEntry.createReader (from Web Standards). + * This method is defined on DirectoryEntry. + * @override + */ + createReader() { + return this.rootEntry_.createReader(); + } +}
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.html b/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.html new file mode 100644 index 0000000..fe96bde5 --- /dev/null +++ b/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.html
@@ -0,0 +1,13 @@ +<!DOCTYPE html> +<!-- Copyright 2018 The Chromium Authors. All rights reserved. + -- Use of this source code is governed by a BSD-style license that can be + -- found in the LICENSE file. + --> +<html> + <body> + <script src="../../common/js/unittest_util.js"></script> + + <script src="files_app_entry_types.js"></script> + <script src="files_app_entry_types_unittest.js"></script> + </body> +</html>
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js b/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js new file mode 100644 index 0000000..24ab21f --- /dev/null +++ b/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js
@@ -0,0 +1,223 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +'use strict'; + +/** Test constructor and default public attributes. */ +function testEntryList(testReportCallback) { + const entryList = new EntryList('My Files', 'my_files'); + assertEquals('My Files', entryList.label); + assertEquals('entry-list://my_files', entryList.toURL()); + assertEquals('my_files', entryList.rootType); + assertFalse(entryList.isNativeType); + assertEquals(0, entryList.children.length); + assertTrue(entryList.isDirectory); + assertFalse(entryList.isFile); + + entryList.addEntry(new EntryList('Child Entry', 'child_entry')); + assertEquals(1, entryList.children.length); + + const reader = entryList.createReader(); + // How many times the reader callback |accumulateResults| has been called? + let callCounter = 0; + // How many times it was called with results? + let resultCouter = 0; + const accumulateResults = (readerResult) => { + // It's called with readerResult==[] a last time to indicate no more files. + callCounter++; + if (readerResult.length > 0) { + resultCouter++; + reader.readEntries(accumulateResults); + } + }; + + reader.readEntries(accumulateResults); + // readEntries runs asynchronously, so let's wait it to be called. + reportPromise( + waitUntil(() => { + // accumulateResults should be called 2x in normal conditions; + return callCounter >= 2; + }).then(() => { + // Now we can check the final result. + assertEquals(2, callCounter); + assertEquals(1, resultCouter); + }), + testReportCallback); +} + +/** Tests method EntryList.getParent. */ +function testEntryListGetParent(testReportCallback) { + const entryList = new EntryList('My Files', 'my_files'); + let callbackTriggered = false; + entryList.getParent(parentEntry => { + // EntryList should return itself since it's a root and that's what the web + // spec says. + callbackTriggered = true; + assertEquals(parentEntry, entryList); + }); + reportPromise(waitUntil(() => callbackTriggered), testReportCallback); +} + +/** Tests method EntryList.addEntry. */ +function testEntryListAddEntry() { + const entryList = new EntryList('My Files'); + assertEquals(0, entryList.children.length); + + const fakeRootEntry = createFakeDisplayRoot(); + const fakeVolumeInfo = { + displayRoot: fakeRootEntry, + label: 'Fake Filesystem', + }; + const childEntry = new VolumeEntry(fakeVolumeInfo); + entryList.addEntry(childEntry); + assertEquals(1, entryList.children.length); + assertEquals(childEntry, entryList.children[0]); +} + +/** Tests method EntryList.getMetadata. */ +function testEntryListAddVolume(testReportCallback) { + const entryList = new EntryList('My Files'); + + let modificationTime = null; + entryList.getMetadata(metadata => { + modificationTime = metadata.modificationTime; + }); + + // getMetadata runs asynchronously, so let's wait it to be called. + reportPromise( + waitUntil(() => { + return modificationTime !== null; + }).then(() => { + // Now we can check the final result, it returns "now", so let's just + // check the type and 1 attribute here. + assertTrue(modificationTime instanceof Date); + assertTrue(!!modificationTime.getUTCFullYear()); + }), + testReportCallback); +} + +/** Tests StaticReader.readEntries. */ +function testStaticReader(testReportCallback) { + const reader = new StaticReader(['file1', 'file2']); + const testResults = []; + // How many times the reader callback |accumulateResults| has been called? + let callCounter = 0; + const accumulateResults = (readerResult) => { + callCounter++; + // merge on testResults. + readerResult.map(f => testResults.push(f)); + if (readerResult.length > 0) + reader.readEntries(accumulateResults); + }; + + reader.readEntries(accumulateResults); + // readEntries runs asynchronously, so let's wait it to be called. + reportPromise( + waitUntil(() => { + // accumulateResults should be called 2x in normal conditions; + return callCounter >= 2; + }).then(() => { + // Now we can check the final result. + assertEquals(2, callCounter); + assertEquals(2, testResults.length); + assertEquals('file1', testResults[0]); + assertEquals('file2', testResults[1]); + }), + testReportCallback); +} + +/** + * Returns an object that can be used as displayRoot on a FakeVolumeInfo. + * VolumeEntry delegates many attributes and methods to displayRoot. + */ +function createFakeDisplayRoot() { + const fakeRootEntry = { + filesystem: 'fake-filesystem://', + fullPath: '/fake/full/path', + isDirectory: true, + isFile: false, + name: 'fs-name', + toURL: () => { + return 'fake-filesystem://fake/full/path'; + }, + createReader: () => { + return 'FAKE READER'; + }, + getMetadata: (success, error) => { + // Returns static date as modificationTime for testing. + setTimeout( + () => success({modificationTime: new Date(Date.UTC(2018, 6, 27))})); + }, + }; + return fakeRootEntry; +} + +/** + * Tests VolumeEntry constructor and default public attributes/getter/methods. + */ +function testVolumeEntry() { + const fakeRootEntry = createFakeDisplayRoot(); + const fakeVolumeInfo = { + displayRoot: fakeRootEntry, + label: 'Fake Filesystem', + }; + + const volumeEntry = new VolumeEntry(fakeVolumeInfo); + assertEquals(fakeRootEntry, volumeEntry.rootEntry); + assertEquals('fake-filesystem://', volumeEntry.filesystem); + assertEquals('/fake/full/path', volumeEntry.fullPath); + assertEquals('fake-filesystem://fake/full/path', volumeEntry.toURL()); + assertEquals('Fake Filesystem', volumeEntry.name); + assertEquals('FAKE READER', volumeEntry.createReader()); + assertTrue(volumeEntry.isNativeType); + assertTrue(volumeEntry.isDirectory); + assertFalse(volumeEntry.isFile); +} + +/** Tests VolumeEntry.getParent */ +function testVolumeEntryGetParent(testReportCallback) { + const fakeRootEntry = createFakeDisplayRoot(); + const fakeVolumeInfo = { + displayRoot: fakeRootEntry, + label: 'Fake Filesystem', + }; + + const volumeEntry = new VolumeEntry(fakeVolumeInfo); + let callbackTriggered = false; + volumeEntry.getParent(parentEntry => { + callbackTriggered = true; + // VolumeEntry should return itself since it's a root and that's what the + // web spec says. + assertEquals(parentEntry, volumeEntry); + }); + reportPromise(waitUntil(() => callbackTriggered), testReportCallback); +} + +/** Tests VolumeEntry.getMetadata */ +function testVolumeEntryGetMetadata(testReportCallback) { + const fakeRootEntry = createFakeDisplayRoot(); + const fakeVolumeInfo = { + displayRoot: fakeRootEntry, + label: 'Fake Filesystem', + }; + const volumeEntry = new VolumeEntry(fakeVolumeInfo); + + let modificationTime = null; + volumeEntry.getMetadata(metadata => { + modificationTime = metadata.modificationTime; + }); + + // getMetadata runs asynchronously, so let's wait it to be called. + reportPromise( + waitUntil(() => { + return modificationTime !== null; + }).then(() => { + // Now we can check the final result. + assertEquals(2018, modificationTime.getUTCFullYear()); + // Date() month is 0-based, so 6 == July. :-( + assertEquals(6, modificationTime.getUTCMonth()); + assertEquals(27, modificationTime.getUTCDate()); + }), + testReportCallback); +}
diff --git a/ui/file_manager/file_manager/common/js/importer_common.js b/ui/file_manager/file_manager/common/js/importer_common.js index 3fa33d10..bca92c9 100644 --- a/ui/file_manager/file_manager/common/js/importer_common.js +++ b/ui/file_manager/file_manager/common/js/importer_common.js
@@ -183,7 +183,7 @@ * Returns true if the entry represents a media directory for the purposes * of Cloud Import. * - * @param {Entry|FakeEntry} entry + * @param {Entry|FakeEntry|FilesAppEntry} entry * @param {VolumeManagerCommon.VolumeInfoProvider} volumeInfoProvider * @return {boolean} */
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js index 926915e3..1c22f899 100644 --- a/ui/file_manager/file_manager/common/js/util.js +++ b/ui/file_manager/file_manager/common/js/util.js
@@ -649,16 +649,23 @@ /** * Obtains whether an entry is fake or not. - * @param {(!Entry|!FakeEntry)} entry Entry or a fake entry. + * @param {(!Entry|!FakeEntry|!FilesAppEntry)} entry Entry or a fake entry. * @return {boolean} True if the given entry is fake. + * @suppress {missingProperties} Closure compiler doesn't allow to call isNative + * on Entry which is native and thus doesn't define this property, however we + * handle undefined accordingly. + * TODO(lucmult): Remove @suppress once all entries are sub-type of + * FilesAppEntry. */ util.isFakeEntry = function(entry) { - return !('getParent' in entry); + return ( + entry.getParent === undefined || + (entry.isNativeType !== undefined && !entry.isNativeType)); }; /** * Obtains whether an entry is the root directory of a Team Drive. - * @param {(!Entry|!FakeEntry)|null} entry Entry or a fake entry. + * @param {(!Entry|!FakeEntry|!FilesAppEntry)|null} entry Entry or a fake entry. * @return {boolean} True if the given entry is root of a Team Drive. */ util.isTeamDriveRoot = function(entry) { @@ -684,7 +691,7 @@ /** * Obtains whether an entry is descendant of the Team Drives directory. - * @param {(!Entry|!FakeEntry)} entry Entry or a fake entry. + * @param {(!Entry|!FakeEntry|!FilesAppEntry)} entry Entry or a fake entry. * @return {boolean} True if the given entry is under Team Drives. */ util.isTeamDriveEntry = function(entry) { @@ -712,7 +719,7 @@ /** * Returns true if the given entry is the root folder of recent files. - * @param {(!Entry|!FakeEntry)} entry Entry or a fake entry. + * @param {(!Entry|!FakeEntry|!FilesAppEntry)} entry Entry or a fake entry. * @returns {boolean} */ util.isRecentRoot = function(entry) { @@ -772,8 +779,10 @@ /** * Compares two entries. - * @param {Entry|FakeEntry} entry1 The entry to be compared. Can be a fake. - * @param {Entry|FakeEntry} entry2 The entry to be compared. Can be a fake. + * @param {Entry|FakeEntry|FilesAppEntry} entry1 The entry to be compared. Can + * be a fake. + * @param {Entry|FakeEntry|FilesAppEntry} entry2 The entry to be compared. Can + * be a fake. * @return {boolean} True if the both entry represents a same file or * directory. Returns true if both entries are null. */ @@ -871,7 +880,8 @@ * Checks if {@code entry} is an immediate child of {@code directory}. * * @param {Entry} entry The presumptive child. - * @param {DirectoryEntry|FakeEntry} directory The presumptive parent. + * @param {DirectoryEntry|FakeEntry|FilesAppEntry} directory The presumptive + * parent. * @return {!Promise<boolean>} Resolves with true if {@code directory} is * parent of {@code entry}. */ @@ -902,6 +912,14 @@ util.isDescendantEntry = function(ancestorEntry, childEntry) { if (!ancestorEntry.isDirectory) return false; + if (ancestorEntry instanceof EntryList) { + let entryList = /** @type {EntryList} */ (ancestorEntry); + return entryList.children.some(ancestorChild => { + let volumeEntry = ancestorChild.rootEntry; + return util.isSameEntry(volumeEntry, childEntry) || + util.isDescendantEntry(volumeEntry, childEntry); + }); + } if (!util.isSameFileSystem(ancestorEntry.filesystem, childEntry.filesystem)) return false; if (util.isSameEntry(ancestorEntry, childEntry)) @@ -1118,6 +1136,8 @@ return str('RECENT_ROOT_LABEL'); case VolumeManagerCommon.RootType.CROSTINI: return str('LINUX_FILES_ROOT_LABEL'); + case VolumeManagerCommon.RootType.MY_FILES: + return str('MY_FILES_ROOT_LABEL'); case VolumeManagerCommon.RootType.MEDIA_VIEW: var mediaViewRootType = VolumeManagerCommon.getMediaViewRootTypeFromVolumeId(
diff --git a/ui/file_manager/file_manager/common/js/volume_manager_common.js b/ui/file_manager/file_manager/common/js/volume_manager_common.js index b30563d3..1c32499 100644 --- a/ui/file_manager/file_manager/common/js/volume_manager_common.js +++ b/ui/file_manager/file_manager/common/js/volume_manager_common.js
@@ -106,8 +106,11 @@ // Root for crostini 'Linux Files'. CROSTINI: 'crostini', - // Root for android files, + // Root for android files. ANDROID_FILES: 'android_files', + + // My Files root, which aggregates DOWNLOADS, ANDROID_FILES and CROSTINI. + MY_FILES: 'my_files', }; Object.freeze(VolumeManagerCommon.RootType); @@ -139,6 +142,7 @@ VolumeManagerCommon.RootType.ADD_NEW_SERVICES_MENU, VolumeManagerCommon.RootType.CROSTINI, VolumeManagerCommon.RootType.ANDROID_FILES, + VolumeManagerCommon.RootType.MY_FILES, ]; console.assert( Object.keys(VolumeManagerCommon.RootType).length === @@ -223,6 +227,7 @@ MEDIA_VIEW: 'media_view', CROSTINI: 'crostini', ANDROID_FILES: 'android_files', + MY_FILES: 'my_files', }; /** @@ -284,6 +289,8 @@ return VolumeManagerCommon.VolumeType.CROSTINI; case VolumeManagerCommon.RootType.ANDROID_FILES: return VolumeManagerCommon.VolumeType.ANDROID_FILES; + case VolumeManagerCommon.RootType.MY_FILES: + return VolumeManagerCommon.VolumeType.MY_FILES; } assertNotReached('Unknown root type: ' + rootType); }; @@ -306,8 +313,8 @@ /** * Obtains a volume info containing the passed entry. - * @param {!Entry|!FakeEntry} entry Entry on the volume to be returned. - * Can be fake. + * @param {!Entry|!FakeEntry|!FilesAppEntry} entry Entry on the volume to be + * returned. Can be fake. * @return {?VolumeInfo} The VolumeInfo instance or null if not found. */ VolumeManagerCommon.VolumeInfoProvider.prototype.getVolumeInfo;
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn index a5df428..77f86318 100644 --- a/ui/file_manager/file_manager/foreground/js/BUILD.gn +++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -407,6 +407,7 @@ js_library("main") { deps = [ ":file_manager", + "../../common/js:files_app_entry_types", "../../common/js:metrics", "../../common/js:util", ]
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model.js b/ui/file_manager/file_manager/foreground/js/actions_model.js index 2abae1f..632241f 100644 --- a/ui/file_manager/file_manager/foreground/js/actions_model.js +++ b/ui/file_manager/file_manager/foreground/js/actions_model.js
@@ -41,13 +41,14 @@ /** * @param {!Entry} entry + * @param {!MetadataModel} metadataModel * @param {!ActionModelUI} ui * @param {!VolumeManagerWrapper} volumeManager * @implements {Action} * @constructor * @struct */ -function DriveShareAction(entry, volumeManager, ui) { +function DriveShareAction(entry, metadataModel, volumeManager, ui) { /** * @private {!Entry} * @const @@ -55,6 +56,12 @@ this.entry_ = entry; /** + * @private {!MetadataModel} + * @const + */ + this.metadataModel_ = metadataModel; + + /** * @private {!VolumeManagerWrapper} * @const */ @@ -69,14 +76,15 @@ /** * @param {!Array<!Entry>} entries + * @param {!MetadataModel} metadataModel * @param {!ActionModelUI} ui * @param {!VolumeManagerWrapper} volumeManager * @return {DriveShareAction} */ -DriveShareAction.create = function(entries, volumeManager, ui) { +DriveShareAction.create = function(entries, metadataModel, volumeManager, ui) { if (entries.length !== 1) return null; - return new DriveShareAction(entries[0], volumeManager, ui); + return new DriveShareAction(entries[0], metadataModel, volumeManager, ui); }; /** @@ -115,9 +123,12 @@ * @override */ DriveShareAction.prototype.canExecute = function() { + const metadata = this.metadataModel_.getCache([this.entry_], ['canShare']); + assert(metadata.length === 1); + const canShareItem = metadata[0].canShare !== false; return this.volumeManager_.getDriveConnectionState().type !== VolumeManagerCommon.DriveConnectionType.OFFLINE && - !util.isTeamDriveRoot(this.entry_); + !util.isTeamDriveRoot(this.entry_) && canShareItem; }; /** @@ -720,7 +731,7 @@ // For Drive, actions are constructed directly in the Files app code. case VolumeManagerCommon.VolumeType.DRIVE: var shareAction = DriveShareAction.create( - this.entries_, this.volumeManager_, this.ui_); + this.entries_, this.metadataModel_, this.volumeManager_, this.ui_); if (shareAction) actions[ActionsModel.CommonActionId.SHARE] = shareAction;
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js index f5b2126..9e2b64c 100644 --- a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js +++ b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
@@ -108,6 +108,9 @@ var model = new ActionsModel(volumeManager, metadataModel, shortcutsModel, driveSyncHandler, ui, [driveFileSystem.entries['/test']]); + metadataModel.properties = { + canShare: true, + }; var invalidated = 0; model.addEventListener('invalidated', function() { invalidated++; @@ -266,6 +269,9 @@ var model = new ActionsModel( volumeManager, metadataModel, shortcutsModel, driveSyncHandler, ui, [driveFileSystem.entries['/team_drives/ABC Team']]); + metadataModel.properties = { + canShare: true, + }; return reportPromise( model.initialize().then(function() { var actions = model.getActions(); @@ -296,6 +302,9 @@ var model = new ActionsModel( volumeManager, metadataModel, shortcutsModel, driveSyncHandler, ui, [driveFileSystem.entries['/team_drives/ABC Team/Folder 1']]); + metadataModel.properties = { + canShare: true, + }; return reportPromise( model.initialize().then(function() { var actions = model.getActions();
diff --git a/ui/file_manager/file_manager/foreground/js/directory_contents.js b/ui/file_manager/file_manager/foreground/js/directory_contents.js index ffe6e11..25f2175b 100644 --- a/ui/file_manager/file_manager/foreground/js/directory_contents.js +++ b/ui/file_manager/file_manager/foreground/js/directory_contents.js
@@ -36,7 +36,7 @@ /** * Scanner of the entries in a directory. - * @param {DirectoryEntry} entry The directory to be read. + * @param {DirectoryEntry|FilesAppDirEntry} entry The directory to be read. * @constructor * @extends {ContentScanner} */ @@ -592,8 +592,8 @@ * @param {FileListContext} context The file list context. * @param {boolean} isSearch True for search directory contents, otherwise * false. - * @param {DirectoryEntry|FakeEntry} directoryEntry The entry of the current - * directory. + * @param {DirectoryEntry|FakeEntry|FilesAppDirEntry} directoryEntry The entry + * of the current directory. * @param {function():ContentScanner} scannerFactory The factory to create * ContentScanner instance. * @constructor @@ -731,7 +731,8 @@ }; /** - * @return {DirectoryEntry|FakeEntry} A DirectoryEntry for current directory. + * @return {DirectoryEntry|FakeEntry|FilesAppDirEntry} A DirectoryEntry for + * current directory. * In case of search -- the top directory from which search is run. */ DirectoryContents.prototype.getDirectoryEntry = function() { @@ -989,7 +990,8 @@ * Creates a DirectoryContents instance to show entries in a directory. * * @param {FileListContext} context File list context. - * @param {DirectoryEntry} directoryEntry The current directory entry. + * @param {DirectoryEntry|FilesAppDirEntry} directoryEntry The current directory + * entry. * @return {DirectoryContents} Created DirectoryContents instance. */ DirectoryContents.createForDirectory = function(context, directoryEntry) {
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js index 7c0dd4e..4bacce0 100644 --- a/ui/file_manager/file_manager/foreground/js/directory_model.js +++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -327,7 +327,7 @@ }; /** - * @return {DirectoryEntry|FakeEntry} Current directory. + * @return {DirectoryEntry|FakeEntry|FilesAppDirEntry} Current directory. */ DirectoryModel.prototype.getCurrentDirEntry = function() { return this.currentDirContents_.getDirectoryEntry(); @@ -1205,7 +1205,7 @@ * Creates directory contents for the entry and query. * * @param {FileListContext} context File list context. - * @param {!DirectoryEntry|!FakeEntry} entry Current directory. + * @param {!DirectoryEntry|!FakeEntry|!FilesAppEntry} entry Current directory. * @param {string=} opt_query Search query string. * @return {DirectoryContents} Directory contents. * @private @@ -1226,6 +1226,10 @@ return DirectoryContents.createForCrostiniMounter( context, /** @type {!FakeEntry} */ (entry)); } + if (entry.rootType == VolumeManagerCommon.RootType.MY_FILES) { + return DirectoryContents.createForDirectory( + context, /** @type {!FilesAppDirEntry} */ (entry)); + } if (query && canUseDriveSearch) { // Drive search. return DirectoryContents.createForDriveSearch(
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js index d31c0f8c..14ee2f2 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -351,6 +351,14 @@ * @private */ this.initBackgroundPagePromise_ = null; + + /** + * Flags async retrieved once at startup and can be used to switch behaviour + * on sync functions. + * @dict + * @private + */ + this.commandLineFlags_ = {}; } FileManager.prototype = /** @struct */ { @@ -506,12 +514,16 @@ FileManager.prototype.startInitSettings_ = function() { metrics.startInterval('Load.InitSettings'); this.appStateController_ = new AppStateController(this.dialogType); - return new Promise(function(resolve) { - this.appStateController_.loadInitialViewOptions().then(function() { - metrics.recordInterval('Load.InitSettings'); - resolve(); - }); - }.bind(this)); + return Promise + .all([ + this.appStateController_.loadInitialViewOptions(), + util.isNewNavigationEnabled(), + ]) + .then(values => { + this.commandLineFlags_['new-files-app-navigation'] = + /** @type {boolean} */ (values[1]); + metrics.recordInterval('Load.InitSettings'); + }); }; /** @@ -797,20 +809,20 @@ this.document_ = this.dialogDom_.ownerDocument; metrics.startInterval('Load.InitDocuments'); - return Promise.all([ - this.initBackgroundPagePromise_, - window.importElementsPromise - ]).then(function() { - metrics.recordInterval('Load.InitDocuments'); - metrics.startInterval('Load.InitUI'); - this.initEssentialUI_(); - this.initAdditionalUI_(); - return this.initSettingsPromise_; - }.bind(this)).then(function() { - this.initFileSystemUI_(); - this.initUIFocus_(); - metrics.recordInterval('Load.InitUI'); - }.bind(this)); + return Promise + .all([this.initBackgroundPagePromise_, window.importElementsPromise]) + .then(function() { + metrics.recordInterval('Load.InitDocuments'); + metrics.startInterval('Load.InitUI'); + this.initEssentialUI_(); + this.initAdditionalUI_(); + return this.initSettingsPromise_; + }.bind(this)) + .then(function() { + this.initFileSystemUI_(); + this.initUIFocus_(); + metrics.recordInterval('Load.InitUI'); + }.bind(this)); }; /** @@ -1207,7 +1219,8 @@ new NavigationModelMenuItem( str('ADD_NEW_SERVICES_BUTTON_LABEL'), '#add-new-services-menu', 'add-new-services') : - null); + null, + this.commandLineFlags_['new-files-app-navigation']); this.setupCrostini_(); this.ui_.initDirectoryTree(directoryTree); }; @@ -1474,7 +1487,8 @@ /** * Return DirectoryEntry of the current directory or null. - * @return {DirectoryEntry|FakeEntry} DirectoryEntry of the current directory. + * @return {DirectoryEntry|FakeEntry|FilesAppDirEntry} DirectoryEntry of the + * current directory. * Returns null if the directory model is not ready or the current * directory is not set. */
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js index 5cc497fc..e93b872f 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -69,7 +69,7 @@ * * @param {EventTarget} element Element which is the command event's target. * @param {DirectoryModel} directoryModel - * @return {DirectoryEntry|FakeEntry} The extracted parent entry. + * @return {DirectoryEntry|FakeEntry|FilesAppEntry} The extracted parent entry. */ CommandUtil.getParentEntry = function(element, directoryModel) { if (element instanceof DirectoryTree) {
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js index b408ff45..e4f8f74 100644 --- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js +++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -1469,7 +1469,8 @@ /** * @param {!ClipboardData} clipboardData Clipboard data object. - * @param {DirectoryEntry|FakeEntry} destinationEntry Destination entry. + * @param {DirectoryEntry|FakeEntry|FilesAppEntry} destinationEntry Destination + * entry. * @return {boolean} Returns true if items stored in {@code clipboardData} can * be pasted to {@code destinationEntry}. Otherwise, returns false. * @private @@ -1508,7 +1509,7 @@ /** * Execute paste command. * - * @param {DirectoryEntry|FakeEntry} destinationEntry + * @param {DirectoryEntry|FakeEntry|FilesAppEntry} destinationEntry * @return {boolean} Returns true, the paste is success. Otherwise, returns * false. */ @@ -1616,7 +1617,8 @@ * @param {!Event} event Drag event. * @param {Object<string>} dragAndDropData drag & drop data from * getDragAndDropGlobalData_(). - * @param {DirectoryEntry|FakeEntry} destinationEntry Destination entry. + * @param {DirectoryEntry|FakeEntry|FilesAppEntry} destinationEntry Destination + * entry. * @return {DropEffectAndLabel} Returns the appropriate drop query type * ('none', 'move' or copy') to the current modifiers status and the * destination, as well as label message to describe why the operation is
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js index bbdb42e..c935ab6 100644 --- a/ui/file_manager/file_manager/foreground/js/import_controller.js +++ b/ui/file_manager/file_manager/foreground/js/import_controller.js
@@ -1071,7 +1071,7 @@ /** * Returns the directory entry for the current directory. - * @return {DirectoryEntry|FakeEntry} + * @return {DirectoryEntry|FakeEntry|FilesAppEntry} */ importer.ControllerEnvironment.prototype.getCurrentDirectory;
diff --git a/ui/file_manager/file_manager/foreground/js/main_scripts.js b/ui/file_manager/file_manager/foreground/js/main_scripts.js index c0d05ed..6b99219 100644 --- a/ui/file_manager/file_manager/foreground/js/main_scripts.js +++ b/ui/file_manager/file_manager/foreground/js/main_scripts.js
@@ -80,6 +80,7 @@ // // <include src="../../common/js/async_util.js"> // <include src="../../common/js/file_type.js"> +// <include src="../../common/js/files_app_entry_types.js"> // <include src="../../common/js/volume_manager_common.js"> // <include src="../../common/js/util.js"> // <include src="../../common/js/progress_center_common.js">
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js index f0373a5..2b6ded53 100644 --- a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js +++ b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js
@@ -136,6 +136,8 @@ item.canRename = prop.canRename; if (prop.canAddChildren !== undefined || nameMap['canAddChildren']) item.canAddChildren = prop.canAddChildren; + if (prop.canShare !== undefined || nameMap['canShare']) + item.canShare = prop.canShare; results.push(item); } return results;
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js index 1530fdf..e8abfba8 100644 --- a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js +++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
@@ -11,6 +11,7 @@ MENU: 'menu', RECENT: 'recent', CROSTINI: 'crostini', + ENTRY_LIST: 'entry-list', }; /** @@ -139,11 +140,14 @@ * The list of folder shortcut. * @param {NavigationModelFakeItem} recentModelItem Recent folder. * @param {NavigationModelMenuItem} addNewServicesItem Add new services item. + * @param {boolean=} opt_useNewNavigation true if should use the new navigation + * style, value should come from flag new-files-app-navigation. * @constructor * @extends {cr.EventTarget} */ function NavigationListModel( - volumeManager, shortcutListModel, recentModelItem, addNewServicesItem) { + volumeManager, shortcutListModel, recentModelItem, addNewServicesItem, + opt_useNewNavigation) { cr.EventTarget.call(this); /** @@ -219,6 +223,9 @@ this.shortcutList_.push(entryToModelItem(shortcutEntry)); } + // True if the flag new-files-app-navigation is enabled. + this.useNewNavigation_ = !!opt_useNewNavigation; + // Reorder volumes, shortcuts, and optional items for initial display. this.reorderNavigationItems_(); @@ -352,6 +359,19 @@ }; /** + * Reorder navigation items when command line flag new-files-app-navigation is + * enabled it nests Downloads, Linux and Android files under "My Files"; when + * it's disabled it has a flat structure with Linux Files after Recent menu. + */ +NavigationListModel.prototype.reorderNavigationItems_ = function() { + if (this.useNewNavigation_) { + return this.orderAndNestItems_(); + } else { + return this.flatNavigationItems_(); + } +}; + +/** * Reorder navigation items in the following order: * 1. Volumes. * 2. If Downloads exists, then immediately after Downloads should be: @@ -362,7 +382,7 @@ * 4. Add new services if it exists. * @private */ -NavigationListModel.prototype.reorderNavigationItems_ = function() { +NavigationListModel.prototype.flatNavigationItems_ = function() { // Check if Linux files already mounted. let linuxFilesMounted = false; for (let i = 0; i < this.volumeList_.length; i++) { @@ -388,6 +408,128 @@ }; /** + * Reorder navigation items and nest some within "Downloads" + * which will be displayed as "My-Files". Desired order: + * 1. Recents. + * 2. Shortcuts. + * 3. "My-Files" (grouping), actually Downloads volume. + * 3.1. Downloads + * 3.2. Play files (android volume) (if enabled). + * 3.3. Linux files (crostini volume or fake item) (if enabled). + * 4. Other volumes (MTP, ARCHIVE, REMOVABLE). + * 5. Drive volumes. + * 6. Other FSP (File System Provider) (when mounted). + * 7. Add new services if (it exists). + * @private + */ +NavigationListModel.prototype.orderAndNestItems_ = function() { + const volumeIndexes = {}; + const volumeList = this.volumeList_; + + // Find the index of each volumeType from the array volumeList_, + // for volumes that can have multiple entries it saves as list + // of indexes, otherwise saves the index as int directly. + for (let i = 0; i < volumeList.length; i++) { + const volumeType = volumeList[i].volumeInfo.volumeType; + switch (volumeType) { + case VolumeManagerCommon.VolumeType.CROSTINI: + case VolumeManagerCommon.VolumeType.DOWNLOADS: + case VolumeManagerCommon.VolumeType.ANDROID_FILES: + volumeIndexes[volumeType] = i; + break; + case VolumeManagerCommon.VolumeType.REMOVABLE: + case VolumeManagerCommon.VolumeType.ARCHIVE: + case VolumeManagerCommon.VolumeType.MTP: + case VolumeManagerCommon.VolumeType.DRIVE: + case VolumeManagerCommon.VolumeType.PROVIDED: + case VolumeManagerCommon.VolumeType.MEDIA_VIEW: + if (!volumeIndexes[volumeType]) { + volumeIndexes[volumeType] = [i]; + } else { + volumeIndexes[volumeType].push(i); + } + break; + default: + assertNotReached(`No explict order for VolumeType: "${volumeType}"`); + break; + } + } + + /** + * @param {!VolumeManagerCommon.VolumeType} volumeType the desired volume type + * to be filtered from volumeList. + * @return {NavigationModelVolumeItem} + */ + const getSingleVolume = function(volumeType) { + return volumeList[volumeIndexes[volumeType]]; + }; + + /** + * @param {!VolumeManagerCommon.VolumeType} volumeType the desired volume type + * to be filtered from volumeList. + * @return Array<!NavigationModelVolumeItem> + */ + const getVolumes = function(volumeType) { + const indexes = volumeIndexes[volumeType] || []; + return indexes.map(idx => volumeList[idx]); + }; + + // Items as per required order. + this.navigationItems_ = []; + + if (this.recentModelItem_) + this.navigationItems_.push(this.recentModelItem_); + for (const shortcut of this.shortcutList_) + this.navigationItems_.push(shortcut); + + const myFilesEntry = new EntryList( + str('MY_FILES_ROOT_LABEL'), VolumeManagerCommon.RootType.MY_FILES); + const myFilesModel = new NavigationModelFakeItem( + myFilesEntry.label, NavigationModelItemType.ENTRY_LIST, myFilesEntry); + const downloadsVolume = + getSingleVolume(VolumeManagerCommon.VolumeType.DOWNLOADS); + if (downloadsVolume) + myFilesEntry.addEntry(new VolumeEntry(downloadsVolume.volumeInfo)); + + this.navigationItems_.push(myFilesModel); + + const androidVolume = + getSingleVolume(VolumeManagerCommon.VolumeType.ANDROID_FILES); + if (androidVolume) + myFilesEntry.addEntry(new VolumeEntry(androidVolume.volumeInfo)); + + const crostiniVolume = + getSingleVolume(VolumeManagerCommon.VolumeType.CROSTINI); + if (crostiniVolume) { + // Crostini is mounted so add it. + myFilesEntry.addEntry(new crostiniVolume.volumeInfo); + } else if (this.linuxFilesItem_) { + // Here it's just a fake item. + myFilesEntry.addEntry(this.linuxFilesItem_.entry); + } + + // Join MEDIA_VIEW, MTP, ARCHIVE and REMOVABLE. + // TODO(lucmult) sort based on the index number to preserve original order. + const otherVolumes = [].concat( + getVolumes(VolumeManagerCommon.VolumeType.MEDIA_VIEW), + getVolumes(VolumeManagerCommon.VolumeType.REMOVABLE), + getVolumes(VolumeManagerCommon.VolumeType.ARCHIVE), + getVolumes(VolumeManagerCommon.VolumeType.MTP)); + + for (const volume of otherVolumes) + this.navigationItems_.push(volume); + + for (const driveItem of getVolumes(VolumeManagerCommon.VolumeType.DRIVE)) + this.navigationItems_.push(driveItem); + + for (const provided of getVolumes(VolumeManagerCommon.VolumeType.PROVIDED)) + this.navigationItems_.push(provided); + + if (this.addNewServicesItem_) + this.navigationItems_.push(this.addNewServicesItem_); +}; + +/** * Returns the item at the given index. * @param {number} index The index of the entry to get. * @return {NavigationModelItem|undefined} The item at the given index.
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js index b570a2bf8..647f7cf 100644 --- a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js +++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
@@ -11,8 +11,18 @@ // Set up string assets. loadTimeData.data = { DRIVE_DIRECTORY_LABEL: 'My Drive', + DRIVE_MY_DRIVE_LABEL: 'My Drive', + DRIVE_TEAM_DRIVES_LABEL: 'Team Drives', + DRIVE_OFFLINE_COLLECTION_LABEL: 'Offline', + DRIVE_SHARED_WITH_ME_COLLECTION_LABEL: 'Shared with me', + DRIVE_RECENT_COLLECTION_LABEL: 'Recents', DOWNLOADS_DIRECTORY_LABEL: 'Downloads', LINUX_FILES_ROOT_LABEL: 'Linux Files', + MY_FILES_ROOT_LABEL: 'My Files', + RECENT_ROOT_LABEL: 'Recent', + MEDIA_VIEW_IMAGES_ROOT_LABEL: 'Images', + MEDIA_VIEW_VIDEOS_ROOT_LABEL: 'Videos', + MEDIA_VIEW_AUDIO_ROOT_LABEL: 'Audio', }; function setUp() {
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn index 45ed768..058fa4b 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn +++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -302,6 +302,7 @@ js_library("location_line") { deps = [ "..:volume_manager_wrapper", + "../../../common/js:files_app_entry_types", "../../../common/js:metrics", "../../../common/js:util", "../../../common/js:volume_manager_common",
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js index 29d57bf..0f77164 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js +++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -145,6 +145,10 @@ // prototype. var labelId = item.labelElement.id; item.__proto__ = DirectoryItem.prototype; + if (window.IN_TEST) { + item.setAttribute('dir-type', 'DirectoryItem'); + item.setAttribute('entry-label', label); + } item.parentTree_ = tree; item.directoryModel_ = tree.directoryModel; item.fileFilter_ = tree.directoryModel.getFileFilter(); @@ -536,6 +540,8 @@ var item = new DirectoryItem(label, tree); item.__proto__ = SubDirectoryItem.prototype; + if (window.IN_TEST) + item.setAttribute('dir-type', 'SubDirectoryItem'); item.entry = dirEntry; item.delayExpansion = parentDirItem.delayExpansion; @@ -595,6 +601,96 @@ }); }; +/** + * A directory of entries. Each element represents an entry. + * + * @param {VolumeManagerCommon.RootType} rootType The root type to record. + * @param {!NavigationModelFakeItem} modelItem NavigationModelItem of this + * volume. + * @param {DirectoryTree} tree Current tree, which contains this item. + * @extends {DirectoryItem} + * @constructor + */ +function EntryListItem(rootType, modelItem, tree) { + var item = new DirectoryItem(modelItem.label, tree); + // Get the original label id defined by TreeItem, before overwriting + // prototype. + item.__proto__ = EntryListItem.prototype; + if (window.IN_TEST) + item.setAttribute('dir-type', 'EntryListItem'); + item.entries_ = []; + + item.rootType_ = rootType; + item.modelItem_ = modelItem; + item.dirEntry_ = modelItem.entry; + item.parentTree_ = tree; + + var icon = queryRequiredElement('.icon', item); + icon.classList.add('item-icon'); + item.setAttribute('root-type-icon', rootType); + return item; +} + +EntryListItem.prototype = { + __proto__: DirectoryItem.prototype, + + /** + * The DirectoryEntry corresponding to this DirectoryItem. This may be + * a dummy DirectoryEntry. + * @type {DirectoryEntry|Object} + */ + get entry() { + return this.dirEntry_; + }, + + /** + * The element containing the label text and the icon. + * @type {!HTMLElement} + * @override + */ + get labelElement() { + return this.firstElementChild.querySelector('.label'); + }, + + /** + * @type {!NavigationModelVolumeItem} + */ + get modelItem() { + return this.modelItem_; + } +}; + +/** + * Retrieves the subdirectories and update them on the tree. Runs synchronously, + * since EntryList has its subdirectories already in memory. + * @param {boolean} recursive True if the update is recursively. + * @param {function()=} opt_successCallback Callback called on success. + * @param {function()=} opt_errorCallback Callback called on error. + */ +EntryListItem.prototype.updateSubDirectories = function( + recursive, opt_successCallback, opt_errorCallback) { + if (!this.entry) { + opt_errorCallback && opt_errorCallback(); + return; + } + this.entries_ = []; + if (this.entry && this.entry.children) { + for (let childEntry of this.entry.children) { + if (childEntry instanceof VolumeEntry) { + // For VolumeEntry we wan't to display its root. + this.entries_.push(childEntry.rootEntry); + } else { + this.entries_.push(childEntry); + } + } + } + if (this.entries_.length > 0) { + this.expanded = true; + } + this.updateSubElementsFromList(recursive); + opt_successCallback && opt_successCallback(); +}; + //////////////////////////////////////////////////////////////////////////////// // VolumeItem @@ -621,8 +717,10 @@ item.delayExpansion = (item.volumeInfo.volumeType === 'provided'); // Set helper attribute for testing. - if (window.IN_TEST) + if (window.IN_TEST) { item.setAttribute('volume-type-for-testing', item.volumeInfo_.volumeType); + item.setAttribute('dir-type', 'VolumeItem'); + } item.setupIcon_(item.querySelector('.icon'), item.volumeInfo_); @@ -797,6 +895,8 @@ var item = new VolumeItem(modelItem, tree); item.__proto__ = DriveVolumeItem.prototype; item.classList.add('drive-volume'); + if (window.IN_TEST) + item.setAttribute('dir-type', 'DriveVolumeItem'); return item; } @@ -960,6 +1060,8 @@ var labelId = item.labelElement.id; item.__proto__ = ShortcutItem.prototype; + if (window.IN_TEST) + item.setAttribute('dir-type', 'ShortcutItem'); item.parentTree_ = tree; item.dirEntry_ = modelItem.entry; item.modelItem_ = modelItem; @@ -1093,6 +1195,10 @@ // prototype. var labelId = item.labelElement.id; item.__proto__ = MenuItem.prototype; + if (window.IN_TEST) { + item.setAttribute('dir-type', 'MenuItem'); + item.setAttribute('entry-label', modelItem.label); + } item.parentTree_ = tree; item.modelItem_ = modelItem; @@ -1177,6 +1283,10 @@ // prototype. var labelId = item.labelElement.id; item.__proto__ = FakeItem.prototype; + if (window.IN_TEST) { + item.setAttribute('dir-type', 'FakeItem'); + item.setAttribute('entry-label', modelItem.label); + } item.rootType_ = rootType; item.parentTree_ = tree; @@ -1185,6 +1295,7 @@ item.innerHTML = TREE_ITEM_INNER_HTML; item.labelElement.id = labelId; item.label = modelItem.label; + item.directoryModel_ = tree.directoryModel; var icon = queryRequiredElement('.icon', item); icon.classList.add('item-icon'); @@ -1239,6 +1350,15 @@ this.parentTree_.directoryModel.activateDirectoryEntry(this.entry); }; +/** + * FakeItem doesn't really have sub-directories, it's defined here only to have + * the same API of other Items on this file. + */ +FakeItem.prototype.updateSubDirectories = function( + recursive, opt_successCallback, opt_errorCallback) { + return opt_successCallback && opt_successCallback(); +}; + //////////////////////////////////////////////////////////////////////////////// // DirectoryTree @@ -1336,6 +1456,53 @@ cr.defineProperty(DirectoryTree, 'contextMenuForRootItems', cr.PropertyKind.JS); /** + * Creates a new DirectoryItem based on |modelItem|. + * @param {NavigationModelItem} modelItem, model that will determine the type of + * DirectoryItem to be created. + * @param {!DirectoryTree} tree The tree to add the new DirectoryItem to. + * @return {!cr.ui.TreeItem} a newly created instance of a + * DirectoryItem type. + */ +DirectoryTree.createDirectoryItem = function(modelItem, tree) { + switch (modelItem.type) { + case NavigationModelItemType.VOLUME: + const volumeModelItem = + /** @type {NavigationModelVolumeItem} */ (modelItem); + if (volumeModelItem.volumeInfo.volumeType === + VolumeManagerCommon.VolumeType.DRIVE) { + return new DriveVolumeItem(volumeModelItem, tree); + } else { + return new VolumeItem(volumeModelItem, tree); + } + break; + case NavigationModelItemType.SHORTCUT: + return new ShortcutItem( + /** @type {!NavigationModelShortcutItem} */ (modelItem), tree); + break; + case NavigationModelItemType.MENU: + return new MenuItem( + /** @type {!NavigationModelMenuItem} */ (modelItem), tree); + break; + case NavigationModelItemType.RECENT: + return new FakeItem( + VolumeManagerCommon.RootType.RECENT, + /** @type {!NavigationModelFakeItem} */ (modelItem), tree); + break; + case NavigationModelItemType.CROSTINI: + return new FakeItem( + VolumeManagerCommon.RootType.CROSTINI, + /** @type {!NavigationModelFakeItem} */ (modelItem), tree); + break; + case NavigationModelItemType.ENTRY_LIST: + return new EntryListItem( + VolumeManagerCommon.RootType.MY_FILES, + /** @type {!NavigationModelFakeItem} */ (modelItem), tree); + break; + } + assertNotReached(`No DirectoryItem model: "${modelItem.type}"`); +}; + +/** * Updates and selects new directory. * @param {!DirectoryEntry} parentDirectory Parent directory of new directory. * @param {!DirectoryEntry} newDirectory @@ -1409,33 +1576,10 @@ this.items[itemIndex].updateSubDirectories(true); } else { var modelItem = this.dataModel.item(modelIndex); - switch (modelItem.type) { - case NavigationModelItemType.VOLUME: - if (modelItem.volumeInfo.volumeType === - VolumeManagerCommon.VolumeType.DRIVE) { - this.addAt(new DriveVolumeItem(modelItem, this), itemIndex); - } else { - this.addAt(new VolumeItem(modelItem, this), itemIndex); - } - break; - case NavigationModelItemType.SHORTCUT: - this.addAt(new ShortcutItem(modelItem, this), itemIndex); - break; - case NavigationModelItemType.MENU: - this.addAt(new MenuItem(modelItem, this), itemIndex); - break; - case NavigationModelItemType.RECENT: - this.addAt( - new FakeItem( - VolumeManagerCommon.RootType.RECENT, modelItem, this), - itemIndex); - break; - case NavigationModelItemType.CROSTINI: - this.addAt( - new FakeItem( - VolumeManagerCommon.RootType.CROSTINI, modelItem, this), - itemIndex); - break; + if (modelItem) { + var item = DirectoryTree.createDirectoryItem(modelItem, this); + if (item) + this.addAt(item, itemIndex); } } itemIndex++;
diff --git a/ui/file_manager/file_manager/foreground/js/ui/location_line.js b/ui/file_manager/file_manager/foreground/js/ui/location_line.js index 9ca44ca..030562f 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/location_line.js +++ b/ui/file_manager/file_manager/foreground/js/ui/location_line.js
@@ -60,7 +60,7 @@ /** * Get components for the path of entry. - * @param {!Entry|!FakeEntry} entry An entry. + * @param {!Entry|!FakeEntry|!FilesAppEntry} entry An entry. * @return {!Array<!LocationLine.PathComponent>} Components. * @private */
diff --git a/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js b/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js index c93f184..4a3696e 100644 --- a/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js +++ b/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js
@@ -300,7 +300,7 @@ /** * Obtains location information from an entry. * - * @param {(!Entry|!FakeEntry)} entry File or directory entry. + * @param {(!Entry|!FakeEntry|!FilesAppEntry)} entry File or directory entry. * @return {EntryLocation} Location information. */ VolumeManagerWrapper.prototype.getLocationInfo = function(entry) {
diff --git a/ui/file_manager/integration_tests/file_manager/context_menu.js b/ui/file_manager/integration_tests/file_manager/context_menu.js index afe91335..737fe44 100644 --- a/ui/file_manager/integration_tests/file_manager/context_menu.js +++ b/ui/file_manager/integration_tests/file_manager/context_menu.js
@@ -15,6 +15,9 @@ * TODO(sashab): Generate the entries used in these tests at runtime, by * creating entries with pre-set combinations of permissions and ensuring the * outcome is always as expected. + * + * TODO(sashab): Once Team Drives is enabled, add tests for team drive roots + * and entries as well. */ /** @@ -199,6 +202,44 @@ }; /** + * Tests that the Share menu item is enabled if a read-write entry is selected. + */ +testcase.checkShareEnabledForReadWriteFile = function() { + checkContextMenu('share', 'hello.txt', true); +}; + +/** + * Tests that the Share menu item is enabled if a read-only document is + * selected. + */ +testcase.checkShareEnabledForReadOnlyDocument = function() { + checkContextMenu('share', 'Read-Only Doc.gdoc', true); +}; + +/** + * Tests that the Share menu item is disabled if a strict read-only document is + * selected. + */ +testcase.checkShareDisabledForStrictReadOnlyDocument = function() { + checkContextMenu('share', 'Read-Only (Strict) Doc.gdoc', false); +}; + +/** + * Tests that the Share menu item is enabled if a read-only file is selected. + */ +testcase.checkShareEnabledForReadOnlyFile = function() { + checkContextMenu('share', 'Read-Only File.jpg', true); +}; + +/** + * Tests that the Share menu item is enabled if a read-only folder is + * selected. + */ +testcase.checkShareEnabledForReadOnlyFolder = function() { + checkContextMenu('share', 'Read-Only Folder', true); +}; + +/** * Tests that the Copy menu item is enabled if a read-write entry is selected. */ testcase.checkCopyEnabledForReadWriteFile = function() {
diff --git a/ui/file_manager/integration_tests/file_manager/my_files.js b/ui/file_manager/integration_tests/file_manager/my_files.js new file mode 100644 index 0000000..ccbe555 --- /dev/null +++ b/ui/file_manager/integration_tests/file_manager/my_files.js
@@ -0,0 +1,53 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * Tests if MyFiles is displayed when flag is true. + */ +testcase.showMyFiles = function() { + let appId; + + const expectedElementLabels = [ + 'Recent: FakeItem', + 'My Files: EntryListItem', + 'Downloads: SubDirectoryItem', + 'Linux Files: SubDirectoryItem', + 'Google Drive: DriveVolumeItem', + 'My Drive: SubDirectoryItem', + 'Shared with me: SubDirectoryItem', + 'Offline: SubDirectoryItem', + 'Add new services: MenuItem', + ]; + + StepsRunner.run([ + // Open Files app on local Downloads. + function() { + setupAndWaitUntilReady( + null, RootPath.DOWNLOADS, this.next, [ENTRIES.beautiful], []); + }, + // Get the directory tree elements. + function(results) { + appId = results.windowId; + const dirTreeQuery = ['#directory-tree [dir-type]']; + remoteCall.callRemoteTestUtil('queryAllElements', appId, dirTreeQuery) + .then(this.next); + }, + // Check tree elements for the correct order and label/element type. + function(elements) { + var visibleElements = []; + for (let element of elements) { + if (!element.hidden) { // Ignore hidden elements. + visibleElements.push( + element.attributes['entry-label'] + ': ' + + element.attributes['dir-type']); + } + } + chrome.test.assertEq(expectedElementLabels, visibleElements); + this.next(); + }, + function() { + checkIfNoErrorsOccured(this.next); + }, + ]); +};
diff --git a/ui/file_manager/integration_tests/file_manager/open_audio_files.js b/ui/file_manager/integration_tests/file_manager/open_audio_files.js index ad35014..2b46217 100644 --- a/ui/file_manager/integration_tests/file_manager/open_audio_files.js +++ b/ui/file_manager/integration_tests/file_manager/open_audio_files.js
@@ -7,21 +7,6 @@ (function() { /** - * Returns an array of file entry row content, where the rows are the basic - * file entry set for the given path, plus the given file |entries|. - * - * @param {string} path Directory path (Downloads or Drive). - * @pram {Array<!TestEntryInfo>} entries Array of file TestEntryInfo. - * @return {Array} File entry row content. - */ -function getExpectedFileEntryRows(path, entries) { - const basicFileEntrySetForPath = - (path == RootPath.DRIVE) ? BASIC_DRIVE_ENTRY_SET : BASIC_LOCAL_ENTRY_SET; - return TestEntryInfo.getExpectedRows(basicFileEntrySetForPath) - .concat(TestEntryInfo.getExpectedRows(entries)); -} - -/** * Returns the title and artist text associated with the given audio track. * * @param {string} audioAppId The Audio Player window ID. @@ -135,7 +120,7 @@ const track = [ENTRIES.beautiful]; StepsRunner.run([ - // Open Files.App on Downloads, add an audio track to Downloads. + // Open Files.App on Downloads, add an audio file to Downloads. function() { setupAndWaitUntilReady(null, RootPath.DOWNLOADS, this.next, track, []); }, @@ -184,7 +169,7 @@ const tracks = [ENTRIES.beautiful, ENTRIES.newlyAdded]; StepsRunner.run([ - // Open Files.App on Drive, add the audio tracks to Drive. + // Open Files.App on Drive, add the audio files to Drive. function() { setupAndWaitUntilReady(null, RootPath.DRIVE, this.next, [], tracks); }, @@ -255,7 +240,8 @@ } /** - * Tests if the audio player play the next file after the current file. + * Tests that the audio player auto-advances viz., auto-plays the next audio + * track when the current track ends. * * @param {string} path Directory path to be tested. */ @@ -263,24 +249,16 @@ let appId; let audioAppId; + const tracks = [ENTRIES.beautiful, ENTRIES.newlyAdded]; + StepsRunner.run([ - // Open Files.App on the given (volume) path. + // Open Files.App on |path|, add audio files to Downloads and Drive. function() { - setupAndWaitUntilReady(null, path, this.next); - }, - // Add an additional audio file. - function(results) { - appId = results.windowId; - addEntries(['local', 'drive'], [ENTRIES.newlyAdded], this.next); - }, - // Wait for the file list to change. - function(result) { - chrome.test.assertTrue(result); - const expected = getExpectedFileEntryRows(path, [ENTRIES.newlyAdded]); - remoteCall.waitForFiles(appId, expected).then(this.next); + setupAndWaitUntilReady(null, path, this.next, tracks, tracks); }, // Open an audio file. - function() { + function(results) { + appId = results.windowId; remoteCall.callRemoteTestUtil( 'openFile', appId, ['Beautiful Song.ogg'], this.next); }, @@ -295,9 +273,13 @@ const playFile = audioPlayingQuery('Beautiful Song.ogg'); audioPlayerApp.waitForElement(audioAppId, playFile).then(this.next); }, - // Leap forward and check: the same file should still be playing. + // Leap forward in time. function() { audioTimeLeapForward(audioAppId); + this.next(); + }, + // Check: the same file should still be playing. + function() { const playFile = audioPlayingQuery('Beautiful Song.ogg'); audioPlayerApp.waitForElement(audioAppId, playFile).then(this.next); }, @@ -321,10 +303,12 @@ let appId; let audioAppId; + const track = [ENTRIES.beautiful]; + StepsRunner.run([ - // Open Files.App on the given (volume) path. + // Open Files.App on |path|, add an audio file to Downloads and Drive. function() { - setupAndWaitUntilReady(null, path, this.next); + setupAndWaitUntilReady(null, path, this.next, track, track); }, // Open an audio file. function(results) { @@ -382,10 +366,12 @@ let appId; let audioAppId; + const track = [ENTRIES.beautiful]; + StepsRunner.run([ - // Open Files.App on the given (volume) path. + // Open Files.App on |path|, add an audio file to Downloads and Drive. function() { - setupAndWaitUntilReady(null, path, this.next); + setupAndWaitUntilReady(null, path, this.next, track, track); }, // Open an audio file. function(results) { @@ -435,10 +421,12 @@ let appId; let audioAppId; + const track = [ENTRIES.beautiful]; + StepsRunner.run([ - // Open Files.App on the given (volume) path. + // Open Files.App on |path|, add an audio file to Downloads and Drive. function() { - setupAndWaitUntilReady(null, path, this.next); + setupAndWaitUntilReady(null, path, this.next, track, track); }, // Open an audio file. function(results) { @@ -503,24 +491,16 @@ let appId; let audioAppId; + const tracks = [ENTRIES.beautiful, ENTRIES.newlyAdded]; + StepsRunner.run([ - // Open Files.App on the given (volume) path. + // Open Files.App on |path|, add audio files to Downloads and Drive. function() { - setupAndWaitUntilReady(null, path, this.next); - }, - // Add an additional audio file. - function(results) { - appId = results.windowId; - addEntries(['local', 'drive'], [ENTRIES.newlyAdded], this.next); - }, - // Wait for the file list to change. - function(result) { - chrome.test.assertTrue(result); - const expected = getExpectedFileEntryRows(path, [ENTRIES.newlyAdded]); - remoteCall.waitForFiles(appId, expected).then(this.next); + setupAndWaitUntilReady(null, path, this.next, tracks, tracks); }, // Open an audio file. - function() { + function(results) { + appId = results.windowId; remoteCall.callRemoteTestUtil( 'openFile', appId, ['newly added file.ogg'], this.next); }, @@ -583,24 +563,16 @@ let appId; let audioAppId; + const tracks = [ENTRIES.beautiful, ENTRIES.newlyAdded]; + StepsRunner.run([ - // Open Files.App on the given (volume) path. + // Open Files.App on |path|, add audio files to Downloads and Drive. function() { - setupAndWaitUntilReady(null, path, this.next); - }, - // Add an additional audio file. - function(results) { - appId = results.windowId; - addEntries(['local', 'drive'], [ENTRIES.newlyAdded], this.next); - }, - // Wait for the file list to change. - function(result) { - chrome.test.assertTrue(result); - const expected = getExpectedFileEntryRows(path, [ENTRIES.newlyAdded]); - remoteCall.waitForFiles(appId, expected).then(this.next); + setupAndWaitUntilReady(null, path, this.next, tracks, tracks); }, // Open an audio file. - function() { + function(results) { + appId = results.windowId; remoteCall.callRemoteTestUtil( 'openFile', appId, ['newly added file.ogg'], this.next); }, @@ -646,23 +618,16 @@ let appId; let audioAppId; + const tracks = [ENTRIES.beautiful, ENTRIES.newlyAdded]; + StepsRunner.run([ - // Open Files.App on the given (volume) path. + // Open Files.App on |path|, add audio files to Downloads and Drive. function() { - setupAndWaitUntilReady(null, path, this.next); - }, - // Add an additional audio file. - function(results) { - appId = results.windowId; - addEntries(['local', 'drive'], [ENTRIES.newlyAdded], this.next); - }, - // Wait for the file list to change. - function(result) { - const expected = getExpectedFileEntryRows(path, [ENTRIES.newlyAdded]); - remoteCall.waitForFiles(appId, expected).then(this.next); + setupAndWaitUntilReady(null, path, this.next, tracks, tracks); }, // Open an audio file. - function() { + function(results) { + appId = results.windowId; remoteCall.callRemoteTestUtil( 'openFile', appId, ['newly added file.ogg'], this.next); },
diff --git a/ui/file_manager/integration_tests/file_manager_test_manifest.json b/ui/file_manager/integration_tests/file_manager_test_manifest.json index c2c38acc..f096302 100644 --- a/ui/file_manager/integration_tests/file_manager_test_manifest.json +++ b/ui/file_manager/integration_tests/file_manager_test_manifest.json
@@ -27,6 +27,7 @@ "file_manager/gear_menu.js", "file_manager/grid_view.js", "file_manager/keyboard_operations.js", + "file_manager/my_files.js", "file_manager/open_audio_files.js", "file_manager/open_image_files.js", "file_manager/open_video_files.js",
diff --git a/ui/file_manager/integration_tests/gallery/slide_mode.js b/ui/file_manager/integration_tests/gallery/slide_mode.js index da2bc604..69dd9a3 100644 --- a/ui/file_manager/integration_tests/gallery/slide_mode.js +++ b/ui/file_manager/integration_tests/gallery/slide_mode.js
@@ -149,7 +149,9 @@ return launchedPromise .then(function(result) { appId = result.appId; - return gallery.waitForElement(appId, '.gallery[mode="slide"]'); + // The buttons are disabled in the static gallery.html DOM, so wait for + // the image to be fully loaded before querying the button state. + return gallery.waitForSlideImage(appId, 640, 480, 'image3'); }) .then(function() { return gallery.callRemoteTestUtil(
diff --git a/ui/message_center/ui_controller.cc b/ui/message_center/ui_controller.cc index 15722431..af66d5f 100644 --- a/ui/message_center/ui_controller.cc +++ b/ui/message_center/ui_controller.cc
@@ -157,7 +157,8 @@ } void UiController::OnMessageCenterChanged() { - if (message_center_visible_ && message_center_->NotificationCount() == 0) { + if (hide_on_last_notification_ && message_center_visible_ && + message_center_->NotificationCount() == 0) { HideMessageCenterBubble(); return; }
diff --git a/ui/message_center/ui_controller.h b/ui/message_center/ui_controller.h index 5b489e0..b5443d0 100644 --- a/ui/message_center/ui_controller.h +++ b/ui/message_center/ui_controller.h
@@ -54,6 +54,9 @@ UiDelegate* delegate() { return delegate_; } const MessageCenter* message_center() const { return message_center_; } MessageCenter* message_center() { return message_center_; } + void set_hide_on_last_notification(bool hide_on_last_notification) { + hide_on_last_notification_ = hide_on_last_notification; + } // Overridden from MessageCenterObserver: void OnNotificationAdded(const std::string& notification_id) override; @@ -80,6 +83,9 @@ bool popups_visible_ = false; UiDelegate* delegate_; + // Set true to hide MessageCenterView when the last notification is dismissed. + bool hide_on_last_notification_ = true; + DISALLOW_COPY_AND_ASSIGN(UiController); };
diff --git a/ui/ozone/platform/headless/BUILD.gn b/ui/ozone/platform/headless/BUILD.gn index 848391d0..500d15e6 100644 --- a/ui/ozone/platform/headless/BUILD.gn +++ b/ui/ozone/platform/headless/BUILD.gn
@@ -10,6 +10,8 @@ "client_native_pixmap_factory_headless.h", "gl_surface_osmesa_png.cc", "gl_surface_osmesa_png.h", + "headless_native_display_delegate.cc", + "headless_native_display_delegate.h", "headless_surface_factory.cc", "headless_surface_factory.h", "headless_window.cc",
diff --git a/ui/ozone/platform/headless/headless_native_display_delegate.cc b/ui/ozone/platform/headless/headless_native_display_delegate.cc new file mode 100644 index 0000000..a4ca1d32 --- /dev/null +++ b/ui/ozone/platform/headless/headless_native_display_delegate.cc
@@ -0,0 +1,120 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/headless/headless_native_display_delegate.h" + +#include "ui/display/types/display_snapshot.h" +#include "ui/display/types/native_display_observer.h" + +namespace ui { + +namespace { +constexpr gfx::Size kDefaultWindowSize(800, 600); +constexpr int default_refresh = 60; +} // namespace + +HeadlessNativeDisplayDelegate::HeadlessNativeDisplayDelegate() = default; + +HeadlessNativeDisplayDelegate::~HeadlessNativeDisplayDelegate() = default; + +void HeadlessNativeDisplayDelegate::Initialize() { + // This shouldn't be called twice. + DCHECK(!current_snapshot_); + + if (next_display_id_ == std::numeric_limits<int64_t>::max()) { + LOG(FATAL) << "Exceeded display id limit"; + return; + } + + display::DisplaySnapshot::DisplayModeList modes; + std::unique_ptr<display::DisplayMode> display_mode = + std::make_unique<display::DisplayMode>(kDefaultWindowSize, false, + default_refresh); + modes.push_back(std::move(display_mode)); + const display::DisplayMode* mode = modes.back().get(); + + current_snapshot_ = std::make_unique<display::DisplaySnapshot>( + next_display_id(), gfx::Point(0, 0), kDefaultWindowSize, + display::DisplayConnectionType::DISPLAY_CONNECTION_TYPE_NONE, false, + false, false, false, gfx::ColorSpace(), "", base::FilePath(), + std::move(modes), std::vector<uint8_t>(), mode, mode, 0, 0, gfx::Size()); + + for (display::NativeDisplayObserver& observer : observers_) + observer.OnConfigurationChanged(); +} + +void HeadlessNativeDisplayDelegate::TakeDisplayControl( + display::DisplayControlCallback callback) { + NOTREACHED(); +} + +void HeadlessNativeDisplayDelegate::RelinquishDisplayControl( + display::DisplayControlCallback callback) { + NOTREACHED(); +} + +void HeadlessNativeDisplayDelegate::GetDisplays( + display::GetDisplaysCallback callback) { + std::vector<display::DisplaySnapshot*> snapshot; + snapshot.push_back(current_snapshot_.get()); + std::move(callback).Run(snapshot); +} + +void HeadlessNativeDisplayDelegate::Configure( + const display::DisplaySnapshot& output, + const display::DisplayMode* mode, + const gfx::Point& origin, + display::ConfigureCallback callback) { + NOTIMPLEMENTED(); + + // It should call |callback| after configuration. + // Even if we don't have implementation, it calls |callback| to finish the + // logic. + std::move(callback).Run(true); +} + +void HeadlessNativeDisplayDelegate::GetHDCPState( + const display::DisplaySnapshot& output, + display::GetHDCPStateCallback callback) { + NOTREACHED(); +} + +void HeadlessNativeDisplayDelegate::SetHDCPState( + const display::DisplaySnapshot& output, + display::HDCPState state, + display::SetHDCPStateCallback callback) { + NOTREACHED(); +} + +bool HeadlessNativeDisplayDelegate::SetColorMatrix( + int64_t display_id, + const std::vector<float>& color_matrix) { + NOTREACHED(); + return false; +} + +bool HeadlessNativeDisplayDelegate::SetGammaCorrection( + int64_t display_id, + const std::vector<display::GammaRampRGBEntry>& degamma_lut, + const std::vector<display::GammaRampRGBEntry>& gamma_lut) { + NOTREACHED(); + return false; +} + +void HeadlessNativeDisplayDelegate::AddObserver( + display::NativeDisplayObserver* observer) { + observers_.AddObserver(observer); +} + +void HeadlessNativeDisplayDelegate::RemoveObserver( + display::NativeDisplayObserver* observer) { + observers_.RemoveObserver(observer); +} + +display::FakeDisplayController* +HeadlessNativeDisplayDelegate::GetFakeDisplayController() { + return nullptr; +} + +} // namespace ui
diff --git a/ui/ozone/platform/headless/headless_native_display_delegate.h b/ui/ozone/platform/headless/headless_native_display_delegate.h new file mode 100644 index 0000000..af815a7 --- /dev/null +++ b/ui/ozone/platform/headless/headless_native_display_delegate.h
@@ -0,0 +1,62 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_HEADLESS_HEADLESS_NATIVE_DISPLAY_DELEGATE_H_ +#define UI_OZONE_PLATFORM_HEADLESS_HEADLESS_NATIVE_DISPLAY_DELEGATE_H_ + +#include "base/observer_list.h" +#include "ui/display/types/native_display_delegate.h" + +namespace display { +class DisplayMode; +class DisplaySnapshot; +} // namespace display + +namespace ui { + +class HeadlessNativeDisplayDelegate : public display::NativeDisplayDelegate { + public: + HeadlessNativeDisplayDelegate(); + ~HeadlessNativeDisplayDelegate() override; + + // display::NativeDisplayDelegate overrides: + void Initialize() override; + void TakeDisplayControl(display::DisplayControlCallback callback) override; + void RelinquishDisplayControl( + display::DisplayControlCallback callback) override; + void GetDisplays(display::GetDisplaysCallback callback) override; + void Configure(const display::DisplaySnapshot& output, + const display::DisplayMode* mode, + const gfx::Point& origin, + display::ConfigureCallback callback) override; + void GetHDCPState(const display::DisplaySnapshot& output, + display::GetHDCPStateCallback callback) override; + void SetHDCPState(const display::DisplaySnapshot& output, + display::HDCPState state, + display::SetHDCPStateCallback callback) override; + bool SetColorMatrix(int64_t display_id, + const std::vector<float>& color_matrix) override; + bool SetGammaCorrection( + int64_t display_id, + const std::vector<display::GammaRampRGBEntry>& degamma_lut, + const std::vector<display::GammaRampRGBEntry>& gamma_lut) override; + void AddObserver(display::NativeDisplayObserver* observer) override; + void RemoveObserver(display::NativeDisplayObserver* observer) override; + display::FakeDisplayController* GetFakeDisplayController() override; + + private: + int64_t next_display_id() { return next_display_id_++; } + std::unique_ptr<display::DisplaySnapshot> current_snapshot_; + + base::ObserverList<display::NativeDisplayObserver> observers_; + + // The next available display id. + int64_t next_display_id_ = 0; + + DISALLOW_COPY_AND_ASSIGN(HeadlessNativeDisplayDelegate); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_HEADLESS_HEADLESS_NATIVE_DISPLAY_DELEGATE_H_
diff --git a/ui/ozone/platform/headless/ozone_platform_headless.cc b/ui/ozone/platform/headless/ozone_platform_headless.cc index 3abad66..3e22233 100644 --- a/ui/ozone/platform/headless/ozone_platform_headless.cc +++ b/ui/ozone/platform/headless/ozone_platform_headless.cc
@@ -8,12 +8,12 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" -#include "ui/display/manager/fake_display_delegate.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" #include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h" #include "ui/events/platform/platform_event_source.h" #include "ui/events/system_input_injector.h" #include "ui/ozone/common/stub_overlay_manager.h" +#include "ui/ozone/platform/headless/headless_native_display_delegate.h" #include "ui/ozone/platform/headless/headless_surface_factory.h" #include "ui/ozone/platform/headless/headless_window.h" #include "ui/ozone/platform/headless/headless_window_manager.h" @@ -74,7 +74,7 @@ } std::unique_ptr<display::NativeDisplayDelegate> CreateNativeDisplayDelegate() override { - return std::make_unique<display::FakeDisplayDelegate>(); + return std::make_unique<HeadlessNativeDisplayDelegate>(); } void InitializeUI(const InitParams& params) override {
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html index 679e3a8..0d1601a 100644 --- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html +++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -16,7 +16,6 @@ color: #fff; display: flex; height: var(--cr-toolbar-height); - transition: visibility var(--cr-toolbar-overlay-animation-duration); } h1 { @@ -64,6 +63,7 @@ } :host([has-overlay]) { + transition: visibility var(--cr-toolbar-overlay-animation-duration); visibility: hidden; }