diff --git a/DEPS b/DEPS index 39a511b..653354c9 100644 --- a/DEPS +++ b/DEPS
@@ -39,7 +39,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': '67e676087cb847d40709ef639b2e2ecc7c99bbb2', + 'skia_revision': 'f469fc0e0f04197f19c94c26801ba6888c6329dc', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other.
diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc index 9f76064..1f6cb66 100644 --- a/base/message_loop/message_pump_perftest.cc +++ b/base/message_loop/message_pump_perftest.cc
@@ -25,6 +25,11 @@ public: ScheduleWorkTest() : counter_(0) {} + void SetUp() override { + if (base::ThreadTicks::IsSupported()) + base::ThreadTicks::WaitUntilInitialized(); + } + void Increment(uint64_t amount) { counter_ += amount; } void Schedule(int index) {
diff --git a/base/threading/thread_perftest.cc b/base/threading/thread_perftest.cc index e865ffa..d6fbc3e 100644 --- a/base/threading/thread_perftest.cc +++ b/base/threading/thread_perftest.cc
@@ -44,7 +44,10 @@ // To be implemented by each test. Subclass must uses threads_ such that // their cpu-time can be measured. Test must return from PingPong() _and_ // call FinishMeasurement from any thread to complete the test. - virtual void Init() {} + virtual void Init() { + if (ThreadTicks::IsSupported()) + ThreadTicks::WaitUntilInitialized(); + } virtual void PingPong(int hops) = 0; virtual void Reset() {}
diff --git a/base/time/time.h b/base/time/time.h index e0435d0..8da08ffd 100644 --- a/base/time/time.h +++ b/base/time/time.h
@@ -79,6 +79,8 @@ // For FILETIME in FromFileTime, until it moves to a new converter class. // See TODO(iyengar) below. #include <windows.h> + +#include "base/gtest_prod_util.h" #endif #include <limits> @@ -733,16 +735,28 @@ #if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \ (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_ANDROID) return true; +#elif defined(OS_WIN) + return IsSupportedWin(); #else return false; #endif } + // Waits until the initialization is completed. Needs to be guarded with a + // call to IsSupported(). + static void WaitUntilInitialized() { +#if defined(OS_WIN) + WaitUntilInitializedWin(); +#endif + } + // Returns thread-specific CPU-time on systems that support this feature. // Needs to be guarded with a call to IsSupported(). Use this timer // to (approximately) measure how much time the calling thread spent doing // actual work vs. being de-scheduled. May return bogus results if the thread - // migrates to another CPU between two calls. + // migrates to another CPU between two calls. Returns an empty ThreadTicks + // object until the initialization is completed. If a clock reading is + // absolutely needed, call WaitUntilInitialized() before this method. static ThreadTicks Now(); private: @@ -752,6 +766,19 @@ // and testing. explicit ThreadTicks(int64 us) : TimeBase(us) { } + +#if defined(OS_WIN) + FRIEND_TEST_ALL_PREFIXES(TimeTicks, TSCTicksPerSecond); + + // Returns the frequency of the TSC in ticks per second, or 0 if it hasn't + // been measured yet. Needs to be guarded with a call to IsSupported(). + // This method is declared here rather than in the anonymous namespace to + // allow testing. + static double TSCTicksPerSecond(); + + static bool IsSupportedWin(); + static void WaitUntilInitializedWin(); +#endif }; // For logging use only.
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc index 512fc37d..c8b403b 100644 --- a/base/time/time_unittest.cc +++ b/base/time/time_unittest.cc
@@ -646,6 +646,7 @@ #endif TEST(ThreadTicks, MAYBE_ThreadNow) { if (ThreadTicks::IsSupported()) { + ThreadTicks::WaitUntilInitialized(); TimeTicks begin = TimeTicks::Now(); ThreadTicks begin_thread = ThreadTicks::Now(); // Make sure that ThreadNow value is non-zero.
diff --git a/base/time/time_win.cc b/base/time/time_win.cc index 45436807..cadf4b6b 100644 --- a/base/time/time_win.cc +++ b/base/time/time_win.cc
@@ -100,6 +100,26 @@ base::LazyInstance<base::Lock>::Leaky g_high_res_lock = LAZY_INSTANCE_INITIALIZER; +// Returns a pointer to the QueryThreadCycleTime() function from Windows. +// Can't statically link to it because it is not available on XP. +using QueryThreadCycleTimePtr = decltype(::QueryThreadCycleTime)*; +QueryThreadCycleTimePtr GetQueryThreadCycleTimeFunction() { + static const QueryThreadCycleTimePtr query_thread_cycle_time_fn = + reinterpret_cast<QueryThreadCycleTimePtr>(::GetProcAddress( + ::GetModuleHandle(L"kernel32.dll"), "QueryThreadCycleTime")); + return query_thread_cycle_time_fn; +} + +// Returns the current value of the performance counter. +uint64 QPCNowRaw() { + LARGE_INTEGER perf_counter_now = {}; + // According to the MSDN documentation for QueryPerformanceCounter(), this + // will never fail on systems that run XP or later. + // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx + ::QueryPerformanceCounter(&perf_counter_now); + return perf_counter_now.QuadPart; +} + } // namespace // Time ----------------------------------------------------------------------- @@ -415,9 +435,7 @@ } TimeDelta QPCNow() { - LARGE_INTEGER now; - QueryPerformanceCounter(&now); - return QPCValueToTimeDelta(now.QuadPart); + return QPCValueToTimeDelta(QPCNowRaw()); } bool IsBuggyAthlon(const base::CPU& cpu) { @@ -504,8 +522,94 @@ // static ThreadTicks ThreadTicks::Now() { - NOTREACHED(); - return ThreadTicks(); + DCHECK(IsSupported()); + + // Get the number of TSC ticks used by the current thread. + ULONG64 thread_cycle_time = 0; + GetQueryThreadCycleTimeFunction()(::GetCurrentThread(), &thread_cycle_time); + + // Get the frequency of the TSC. + double tsc_ticks_per_second = TSCTicksPerSecond(); + if (tsc_ticks_per_second == 0) + return ThreadTicks(); + + // Return the CPU time of the current thread. + double thread_time_seconds = thread_cycle_time / tsc_ticks_per_second; + return ThreadTicks(static_cast<int64>( + thread_time_seconds * Time::kMicrosecondsPerSecond)); +} + +// static +bool ThreadTicks::IsSupportedWin() { + static bool is_supported = GetQueryThreadCycleTimeFunction() && + base::CPU().has_non_stop_time_stamp_counter() && + !IsBuggyAthlon(base::CPU()); + return is_supported; +} + +// static +void ThreadTicks::WaitUntilInitializedWin() { + while (TSCTicksPerSecond() == 0) + ::Sleep(10); +} + +double ThreadTicks::TSCTicksPerSecond() { + DCHECK(IsSupported()); + + // The value returned by QueryPerformanceFrequency() cannot be used as the TSC + // frequency, because there is no guarantee that the TSC frequency is equal to + // the performance counter frequency. + + // The TSC frequency is cached in a static variable because it takes some time + // to compute it. + static double tsc_ticks_per_second = 0; + if (tsc_ticks_per_second != 0) + return tsc_ticks_per_second; + + // Increase the thread priority to reduces the chances of having a context + // switch during a reading of the TSC and the performance counter. + int previous_priority = ::GetThreadPriority(::GetCurrentThread()); + ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + + // The first time that this function is called, make an initial reading of the + // TSC and the performance counter. + static const uint64 tsc_initial = __rdtsc(); + static const uint64 perf_counter_initial = QPCNowRaw(); + + // Make a another reading of the TSC and the performance counter every time + // that this function is called. + uint64 tsc_now = __rdtsc(); + uint64 perf_counter_now = QPCNowRaw(); + + // Reset the thread priority. + ::SetThreadPriority(::GetCurrentThread(), previous_priority); + + // Make sure that at least 50 ms elapsed between the 2 readings. The first + // time that this function is called, we don't expect this to be the case. + // Note: The longer the elapsed time between the 2 readings is, the more + // accurate the computed TSC frequency will be. The 50 ms value was + // chosen because local benchmarks show that it allows us to get a + // stddev of less than 1 tick/us between multiple runs. + // Note: According to the MSDN documentation for QueryPerformanceFrequency(), + // this will never fail on systems that run XP or later. + // https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx + LARGE_INTEGER perf_counter_frequency = {}; + ::QueryPerformanceFrequency(&perf_counter_frequency); + DCHECK_GE(perf_counter_now, perf_counter_initial); + uint64 perf_counter_ticks = perf_counter_now - perf_counter_initial; + double elapsed_time_seconds = + perf_counter_ticks / static_cast<double>(perf_counter_frequency.QuadPart); + + const double kMinimumEvaluationPeriodSeconds = 0.05; + if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds) + return 0; + + // Compute the frequency of the TSC. + DCHECK_GE(tsc_now, tsc_initial); + uint64 tsc_ticks = tsc_now - tsc_initial; + tsc_ticks_per_second = tsc_ticks / elapsed_time_seconds; + + return tsc_ticks_per_second; } // static
diff --git a/base/time/time_win_unittest.cc b/base/time/time_win_unittest.cc index c894b68..6f8a9b7d 100644 --- a/base/time/time_win_unittest.cc +++ b/base/time/time_win_unittest.cc
@@ -12,13 +12,10 @@ #include "base/threading/platform_thread.h" #include "base/time/time.h" +#include "base/win/registry.h" #include "testing/gtest/include/gtest/gtest.h" -using base::Time; -using base::TimeDelta; -using base::TimeTicks; -using base::TraceTicks; - +namespace base { namespace { class MockTimeTicks : public TimeTicks { @@ -192,18 +189,22 @@ // in order to create a single test case list. COMPILE_ASSERT(sizeof(TimeTicks) == sizeof(Time), test_only_works_with_same_sizes); - TestCase cases[] = { - { reinterpret_cast<TestFunc>(&Time::Now), "Time::Now" }, - { &TimeTicks::Now, "TimeTicks::Now" }, - { reinterpret_cast<TestFunc>(&TraceTicks::Now), "TraceTicks::Now" }, - { NULL, "" } - }; + std::vector<TestCase> cases; + cases.push_back({reinterpret_cast<TestFunc>(&Time::Now), "Time::Now"}); + cases.push_back({&TimeTicks::Now, "TimeTicks::Now"}); + cases.push_back( + {reinterpret_cast<TestFunc>(&TraceTicks::Now), "TraceTicks::Now"}); - int test_case = 0; - while (cases[test_case].func) { + if (ThreadTicks::IsSupported()) { + ThreadTicks::WaitUntilInitialized(); + cases.push_back( + {reinterpret_cast<TestFunc>(&ThreadTicks::Now), "ThreadTicks::Now"}); + } + + for (const auto& test_case : cases) { TimeTicks start = TimeTicks::Now(); for (int index = 0; index < kLoops; index++) - cases[test_case].func(); + test_case.func(); TimeTicks stop = TimeTicks::Now(); // Turning off the check for acceptible delays. Without this check, // the test really doesn't do much other than measure. But the @@ -213,9 +214,29 @@ // slow, and there is really no value for checking against a max timer. //const int kMaxTime = 35; // Maximum acceptible milliseconds for test. //EXPECT_LT((stop - start).InMilliseconds(), kMaxTime); - printf("%s: %1.2fus per call\n", cases[test_case].description, - (stop - start).InMillisecondsF() * 1000 / kLoops); - test_case++; + printf("%s: %1.2fus per call\n", test_case.description, + (stop - start).InMillisecondsF() * 1000 / kLoops); + } +} + +TEST(TimeTicks, TSCTicksPerSecond) { + if (ThreadTicks::IsSupported()) { + ThreadTicks::WaitUntilInitialized(); + + // Read the CPU frequency from the registry. + base::win::RegKey processor_key( + HKEY_LOCAL_MACHINE, + L"Hardware\\Description\\System\\CentralProcessor\\0", KEY_QUERY_VALUE); + ASSERT_TRUE(processor_key.Valid()); + DWORD processor_mhz_from_registry; + ASSERT_EQ(ERROR_SUCCESS, + processor_key.ReadValueDW(L"~MHz", &processor_mhz_from_registry)); + + // Expect the measured TSC frequency to be similar to the processor + // frequency from the registry (0.5% error). + double tsc_mhz_measured = ThreadTicks::TSCTicksPerSecond() / 1e6; + EXPECT_NEAR(tsc_mhz_measured, processor_mhz_from_registry, + 0.005 * processor_mhz_from_registry); } } @@ -273,3 +294,5 @@ << (ticks < Time::kQPCOverflowThreshold ? "FAST" : "SAFE"); } } + +} // namespace base
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc index e78ee61..66f231c 100644 --- a/base/trace_event/trace_event_impl.cc +++ b/base/trace_event/trace_event_impl.cc
@@ -177,7 +177,11 @@ const ThreadTicks& thread_now) { DCHECK_EQ(duration_.ToInternalValue(), -1); duration_ = now - timestamp_; - thread_duration_ = thread_now - thread_timestamp_; + + // |thread_timestamp_| can be empty if the thread ticks clock wasn't + // initialized when it was recorded. + if (thread_timestamp_ != ThreadTicks()) + thread_duration_ = thread_now - thread_timestamp_; } void TraceEvent::EstimateTraceMemoryOverhead(
diff --git a/cc/debug/lap_timer.cc b/cc/debug/lap_timer.cc index eb6900c..2c5d381 100644 --- a/cc/debug/lap_timer.cc +++ b/cc/debug/lap_timer.cc
@@ -42,6 +42,8 @@ : LapTimer(kWarmupRuns, base::TimeDelta::FromMilliseconds(kTimeLimitMillis), kTimeCheckInterval) { + if (base::ThreadTicks::IsSupported()) + base::ThreadTicks::WaitUntilInitialized(); } void LapTimer::Reset() {
diff --git a/cc/debug/rendering_stats_instrumentation.cc b/cc/debug/rendering_stats_instrumentation.cc index e793c5022..d80d912e 100644 --- a/cc/debug/rendering_stats_instrumentation.cc +++ b/cc/debug/rendering_stats_instrumentation.cc
@@ -14,6 +14,8 @@ RenderingStatsInstrumentation::RenderingStatsInstrumentation() : record_rendering_stats_(false) { + if (base::ThreadTicks::IsSupported()) + base::ThreadTicks::WaitUntilInitialized(); } RenderingStatsInstrumentation::~RenderingStatsInstrumentation() {}
diff --git a/chrome/VERSION b/chrome/VERSION index ef5f09e..1110489e 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=48 MINOR=0 -BUILD=2536 +BUILD=2537 PATCH=0
diff --git a/chrome/browser/android/preferences/pref_service_bridge.cc b/chrome/browser/android/preferences/pref_service_bridge.cc index c2b2c58..5b5f4b5 100644 --- a/chrome/browser/android/preferences/pref_service_bridge.cc +++ b/chrome/browser/android/preferences/pref_service_bridge.cc
@@ -70,7 +70,6 @@ case CONTENT_SETTING_BLOCK: return false; case CONTENT_SETTING_ALLOW: - return true; case CONTENT_SETTING_ASK: default: return true;
diff --git a/chrome/browser/download/notification/download_item_notification.cc b/chrome/browser/download/notification/download_item_notification.cc index 41c1d8d7..3c05228f 100644 --- a/chrome/browser/download/notification/download_item_notification.cc +++ b/chrome/browser/download/notification/download_item_notification.cc
@@ -204,6 +204,7 @@ if (item_ && item_->IsDangerous() && !item_->IsDone()) { content::RecordAction( UserMetricsAction("DownloadNotification.Close_Dangerous")); + closed_ = true; // Should be set before cancelling the download. item_->Cancel(true /* by_user */); return; } @@ -331,7 +332,7 @@ (download_state == content::DownloadItem::INTERRUPTED && previous_download_state_ != content::DownloadItem::INTERRUPTED)); - if (IsNotificationVisible()) { + if (IsNotificationVisible() && !closed_) { UpdateNotificationData(popup ? UPDATE_AND_POPUP : UPDATE); } else { if (show_next_ || popup) @@ -416,6 +417,7 @@ notification_->set_buttons(notification_actions); if (type == ADD) { + closed_ = false; g_browser_process->notification_ui_manager()-> Add(*notification_, profile()); } else if (type == UPDATE || @@ -443,6 +445,7 @@ Update(*notification_, profile()); } else if (type == UPDATE_AND_POPUP) { CloseNotificationByNonUser(); + closed_ = false; g_browser_process->notification_ui_manager()-> Add(*notification_, profile()); } else { @@ -574,6 +577,7 @@ // doesn't pop-up itself so this logic works as disabling pop-up. CloseNotificationByNonUser(); notification_->set_priority(message_center::LOW_PRIORITY); + closed_ = false; g_browser_process->notification_ui_manager()->Add(*notification_, profile()); }
diff --git a/chrome/browser/download/notification/download_item_notification.h b/chrome/browser/download/notification/download_item_notification.h index 0506235..50f87ef 100644 --- a/chrome/browser/download/notification/download_item_notification.h +++ b/chrome/browser/download/notification/download_item_notification.h
@@ -106,6 +106,10 @@ // goes visible. The initial value is true so it gets shown on initial update. bool show_next_ = true; + // Flag if the notification has been closed or not. Setting this flag + // prevents updates after close. + bool closed_ = false; + int image_resource_id_ = 0; std::pair<gfx::VectorIconId, SkColor> vector_icon_params_; content::DownloadItem::DownloadState previous_download_state_ =
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc index ea31298..d830cfa 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -1407,14 +1407,8 @@ #endif case IDC_SPELLCHECK_MENU: - return true; - case IDC_CONTENT_CONTEXT_OPENLINKWITH: - return true; - case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS: - return true; - case IDC_CONTENT_CONTEXT_FORCESAVEPASSWORD: return true;
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc index 645b600..3edcc691 100644 --- a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc +++ b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
@@ -228,9 +228,6 @@ return succeeded_; case IDC_CONTENT_CONTEXT_SPELLING_TOGGLE: - return integrate_spelling_service_.IsUserModifiable() && - !profile->IsOffTheRecord(); - case IDC_CONTENT_CONTEXT_AUTOCORRECT_SPELLING_TOGGLE: return integrate_spelling_service_.IsUserModifiable() && !profile->IsOffTheRecord();
diff --git a/chrome/browser/resources/md_downloads/compiled_resources.gyp b/chrome/browser/resources/md_downloads/compiled_resources.gyp index 33835847..a5a4e65 100644 --- a/chrome/browser/resources/md_downloads/compiled_resources.gyp +++ b/chrome/browser/resources/md_downloads/compiled_resources.gyp
@@ -18,6 +18,10 @@ '../../../../ui/webui/resources/js/cr/ui/focus_row.js', '../../../../ui/webui/resources/js/cr/ui/focus_grid.js', '../../../../ui/webui/resources/js/util.js', + '../../../../third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior-extracted.js', + '../../../../third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js', + '../../../../third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior-extracted.js', + '../../../../third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js', 'action_service.js', 'constants.js', 'throttled_icon_loader.js',
diff --git a/chrome/browser/resources/md_downloads/crisper.js b/chrome/browser/resources/md_downloads/crisper.js index d0a64e3..de97497 100644 --- a/chrome/browser/resources/md_downloads/crisper.js +++ b/chrome/browser/resources/md_downloads/crisper.js
@@ -2353,67 +2353,6 @@ extends: 'a', }); -// 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. - -/** @typedef {{img: HTMLImageElement, url: string}} */ -var LoadIconRequest; - -cr.define('downloads', function() { - /** - * @param {number} maxAllowed The maximum number of simultaneous downloads - * allowed. - * @constructor - */ - function ThrottledIconLoader(maxAllowed) { - assert(maxAllowed > 0); - - /** @private {number} */ - this.maxAllowed_ = maxAllowed; - - /** @private {!Array<!LoadIconRequest>} */ - this.requests_ = []; - } - - ThrottledIconLoader.prototype = { - /** @private {number} */ - loading_: 0, - - /** - * Load the provided |url| into |img.src| after appending ?scale=. - * @param {!HTMLImageElement} img An <img> to show the loaded image in. - * @param {string} url A remote image URL to load. - */ - loadScaledIcon: function(img, url) { - var scaledUrl = url + '?scale=' + window.devicePixelRatio + 'x'; - if (img.src == scaledUrl) - return; - - this.requests_.push({img: img, url: scaledUrl}); - this.loadNextIcon_(); - }, - - /** @private */ - loadNextIcon_: function() { - if (this.loading_ > this.maxAllowed_ || !this.requests_.length) - return; - - var request = this.requests_.shift(); - var img = request.img; - - img.onabort = img.onerror = img.onload = function() { - this.loading_--; - this.loadNextIcon_(); - }.bind(this); - - this.loading_++; - img.src = request.url; - }, - }; - - return {ThrottledIconLoader: ThrottledIconLoader}; -}); // Copyright 2014 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -3007,7 +2946,7 @@ } } }); -Polymer.version = '1.1.4'; +Polymer.version = '1.1.5'; Polymer.Base._addFeature({ _registerFeatures: function () { this._prepIs(); @@ -4884,7 +4823,15 @@ } }, listen: function (node, eventName, methodName) { -this._listen(node, eventName, this._createEventHandler(node, eventName, methodName)); +var handler = this._recallEventHandler(this, eventName, node, methodName); +if (!handler) { +handler = this._createEventHandler(node, eventName, methodName); +} +if (handler._listening) { +return; +} +this._listen(node, eventName, handler); +handler._listening = true; }, _boundListenerKey: function (eventName, methodName) { return eventName + ':' + methodName; @@ -4923,6 +4870,7 @@ host._warn(host._logf('_createEventHandler', 'listener method `' + methodName + '` not defined')); } }; +handler._listening = false; this._recordEventHandler(host, eventName, node, methodName, handler); return handler; }, @@ -4930,6 +4878,7 @@ var handler = this._recallEventHandler(this, eventName, node, methodName); if (handler) { this._unlisten(node, eventName, handler); +handler._listening = false; } }, _listen: function (node, eventName, handler) { @@ -5777,6 +5726,12 @@ } } return elt; +}, +isLightDescendant: function (node) { +return this.contains(node) && Polymer.dom(this).getOwnerRoot() === Polymer.dom(node).getOwnerRoot(); +}, +isLocalDescendant: function (node) { +return this.root === Polymer.dom(node).getOwnerRoot(); } }); Polymer.Bind = { @@ -6578,6 +6533,22 @@ this._notifySplice(array, path, 0, args.length, []); } return ret; +}, +prepareModelNotifyPath: function (model) { +this.mixin(model, { +fire: Polymer.Base.fire, +notifyPath: Polymer.Base.notifyPath, +_EVENT_CHANGED: Polymer.Base._EVENT_CHANGED, +_notifyPath: Polymer.Base._notifyPath, +_pathEffector: Polymer.Base._pathEffector, +_annotationPathEffect: Polymer.Base._annotationPathEffect, +_complexObserverPathEffect: Polymer.Base._complexObserverPathEffect, +_annotatedComputationPathEffect: Polymer.Base._annotatedComputationPathEffect, +_computePathEffect: Polymer.Base._computePathEffect, +_modelForPath: Polymer.Base._modelForPath, +_pathMatchesEffect: Polymer.Base._pathMatchesEffect, +_notifyBoundPaths: Polymer.Base._notifyBoundPaths +}); } }); }()); @@ -7784,6 +7755,7 @@ _instanceProps: Polymer.nob, _parentPropPrefix: '_parent_', templatize: function (template) { +this._templatized = template; if (!template._content) { template._content = template.content; } @@ -7794,11 +7766,11 @@ } var archetype = Object.create(Polymer.Base); this._customPrepAnnotations(archetype, template); +this._prepParentProperties(archetype, template); archetype._prepEffects(); this._customPrepEffects(archetype); archetype._prepBehaviors(); archetype._prepBindings(); -this._prepParentProperties(archetype, template); archetype._notifyPath = this._notifyPathImpl; archetype._scopeElementClass = this._scopeElementClassImpl; archetype.listen = this._listenImpl; @@ -7881,6 +7853,7 @@ proto = archetype._parentPropProto = Object.create(null); if (template != this) { Polymer.Bind.prepareModel(proto); +Polymer.Base.prepareModelNotifyPath(proto); } for (prop in parentProps) { var parentProp = this._parentPropPrefix + prop; @@ -7899,6 +7872,7 @@ template._forwardParentProp = this._forwardParentProp.bind(this); } this._extendTemplate(template, proto); +template._pathEffector = this._pathEffectorImpl.bind(this); } }, _createForwardPropEffector: function (prop) { @@ -7909,7 +7883,7 @@ _createHostPropEffector: function (prop) { var prefix = this._parentPropPrefix; return function (source, value) { -this.dataHost[prefix + prop] = value; +this.dataHost._templatized[prefix + prop] = value; }; }, _createInstancePropEffector: function (prop) { @@ -7941,16 +7915,17 @@ var root = dot < 0 ? path : path.slice(0, dot); dataHost._forwardInstancePath.call(dataHost, this, path, value); if (root in dataHost._parentProps) { -dataHost.notifyPath(dataHost._parentPropPrefix + path, value); +dataHost._templatized.notifyPath(dataHost._parentPropPrefix + path, value); } }, -_pathEffector: function (path, value, fromAbove) { +_pathEffectorImpl: function (path, value, fromAbove) { if (this._forwardParentPath) { if (path.indexOf(this._parentPropPrefix) === 0) { -this._forwardParentPath(path.substring(8), value); +var subPath = path.substring(this._parentPropPrefix.length); +this._forwardParentPath(subPath, value); } } -Polymer.Base._pathEffector.apply(this, arguments); +Polymer.Base._pathEffector.call(this._templatized, path, value, fromAbove); }, _constructorImpl: function (model, host) { this._rootDataHost = host._getRootDataHost(); @@ -7993,8 +7968,9 @@ stamp: function (model) { model = model || {}; if (this._parentProps) { +var templatized = this._templatized; for (var prop in this._parentProps) { -model[prop] = this[this._parentPropPrefix + prop]; +model[prop] = templatized[this._parentPropPrefix + prop]; } } return new this.ctor(model, this); @@ -8602,7 +8578,7 @@ } } else { this.push('selected', item); -skey = this._selectedColl.getKey(item); +var skey = this._selectedColl.getKey(item); this.linkPaths('selected.' + skey, 'items.' + key); } } else { @@ -8771,6 +8747,1336 @@ this.fire('dom-change'); } }); +/** + * `IronResizableBehavior` is a behavior that can be used in Polymer elements to + * coordinate the flow of resize events between "resizers" (elements that control the + * size or hidden state of their children) and "resizables" (elements that need to be + * notified when they are resized or un-hidden by their parents in order to take + * action on their new measurements). + * Elements that perform measurement should add the `IronResizableBehavior` behavior to + * their element definition and listen for the `iron-resize` event on themselves. + * This event will be fired when they become showing after having been hidden, + * when they are resized explicitly by another resizable, or when the window has been + * resized. + * Note, the `iron-resize` event is non-bubbling. + * + * @polymerBehavior Polymer.IronResizableBehavior + * @demo demo/index.html + **/ + Polymer.IronResizableBehavior = { + properties: { + /** + * The closest ancestor element that implements `IronResizableBehavior`. + */ + _parentResizable: { + type: Object, + observer: '_parentResizableChanged' + }, + + /** + * True if this element is currently notifying its descedant elements of + * resize. + */ + _notifyingDescendant: { + type: Boolean, + value: false + } + }, + + listeners: { + 'iron-request-resize-notifications': '_onIronRequestResizeNotifications' + }, + + created: function() { + // We don't really need property effects on these, and also we want them + // to be created before the `_parentResizable` observer fires: + this._interestedResizables = []; + this._boundNotifyResize = this.notifyResize.bind(this); + }, + + attached: function() { + this.fire('iron-request-resize-notifications', null, { + node: this, + bubbles: true, + cancelable: true + }); + + if (!this._parentResizable) { + window.addEventListener('resize', this._boundNotifyResize); + this.notifyResize(); + } + }, + + detached: function() { + if (this._parentResizable) { + this._parentResizable.stopResizeNotificationsFor(this); + } else { + window.removeEventListener('resize', this._boundNotifyResize); + } + + this._parentResizable = null; + }, + + /** + * Can be called to manually notify a resizable and its descendant + * resizables of a resize change. + */ + notifyResize: function() { + if (!this.isAttached) { + return; + } + + this._interestedResizables.forEach(function(resizable) { + if (this.resizerShouldNotify(resizable)) { + this._notifyDescendant(resizable); + } + }, this); + + this._fireResize(); + }, + + /** + * Used to assign the closest resizable ancestor to this resizable + * if the ancestor detects a request for notifications. + */ + assignParentResizable: function(parentResizable) { + this._parentResizable = parentResizable; + }, + + /** + * Used to remove a resizable descendant from the list of descendants + * that should be notified of a resize change. + */ + stopResizeNotificationsFor: function(target) { + var index = this._interestedResizables.indexOf(target); + + if (index > -1) { + this._interestedResizables.splice(index, 1); + this.unlisten(target, 'iron-resize', '_onDescendantIronResize'); + } + }, + + /** + * This method can be overridden to filter nested elements that should or + * should not be notified by the current element. Return true if an element + * should be notified, or false if it should not be notified. + * + * @param {HTMLElement} element A candidate descendant element that + * implements `IronResizableBehavior`. + * @return {boolean} True if the `element` should be notified of resize. + */ + resizerShouldNotify: function(element) { return true; }, + + _onDescendantIronResize: function(event) { + if (this._notifyingDescendant) { + event.stopPropagation(); + return; + } + + // NOTE(cdata): In ShadowDOM, event retargetting makes echoing of the + // otherwise non-bubbling event "just work." We do it manually here for + // the case where Polymer is not using shadow roots for whatever reason: + if (!Polymer.Settings.useShadow) { + this._fireResize(); + } + }, + + _fireResize: function() { + this.fire('iron-resize', null, { + node: this, + bubbles: false + }); + }, + + _onIronRequestResizeNotifications: function(event) { + var target = event.path ? event.path[0] : event.target; + + if (target === this) { + return; + } + + if (this._interestedResizables.indexOf(target) === -1) { + this._interestedResizables.push(target); + this.listen(target, 'iron-resize', '_onDescendantIronResize'); + } + + target.assignParentResizable(this); + this._notifyDescendant(target); + + event.stopPropagation(); + }, + + _parentResizableChanged: function(parentResizable) { + if (parentResizable) { + window.removeEventListener('resize', this._boundNotifyResize); + } + }, + + _notifyDescendant: function(descendant) { + // NOTE(cdata): In IE10, attached is fired on children first, so it's + // important not to notify them if the parent is not attached yet (or + // else they will get redundantly notified when the parent attaches). + if (!this.isAttached) { + return; + } + + this._notifyingDescendant = true; + descendant.notifyResize(); + this._notifyingDescendant = false; + } + }; +(function() { + + var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/); + var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8; + var DEFAULT_PHYSICAL_COUNT = 20; + var MAX_PHYSICAL_COUNT = 500; + + Polymer({ + + is: 'iron-list', + + properties: { + + /** + * An array containing items determining how many instances of the template + * to stamp and that that each template instance should bind to. + */ + items: { + type: Array + }, + + /** + * The name of the variable to add to the binding scope for the array + * element associated with a given template instance. + */ + as: { + type: String, + value: 'item' + }, + + /** + * The name of the variable to add to the binding scope with the index + * for the row. If `sort` is provided, the index will reflect the + * sorted order (rather than the original array order). + */ + indexAs: { + type: String, + value: 'index' + }, + + /** + * The name of the variable to add to the binding scope to indicate + * if the row is selected. + */ + selectedAs: { + type: String, + value: 'selected' + }, + + /** + * When true, tapping a row will select the item, placing its data model + * in the set of selected items retrievable via the selection property. + * + * Note that tapping focusable elements within the list item will not + * result in selection, since they are presumed to have their * own action. + */ + selectionEnabled: { + type: Boolean, + value: false + }, + + /** + * When `multiSelection` is false, this is the currently selected item, or `null` + * if no item is selected. + */ + selectedItem: { + type: Object, + notify: true + }, + + /** + * When `multiSelection` is true, this is an array that contains the selected items. + */ + selectedItems: { + type: Object, + notify: true + }, + + /** + * When `true`, multiple items may be selected at once (in this case, + * `selected` is an array of currently selected items). When `false`, + * only one item may be selected at a time. + */ + multiSelection: { + type: Boolean, + value: false + } + }, + + observers: [ + '_itemsChanged(items.*)', + '_selectionEnabledChanged(selectionEnabled)', + '_multiSelectionChanged(multiSelection)' + ], + + behaviors: [ + Polymer.Templatizer, + Polymer.IronResizableBehavior + ], + + listeners: { + 'iron-resize': '_resizeHandler' + }, + + /** + * The ratio of hidden tiles that should remain in the scroll direction. + * Recommended value ~0.5, so it will distribute tiles evely in both directions. + */ + _ratio: 0.5, + + /** + * The element that controls the scroll + * @type {?Element} + */ + _scroller: null, + + /** + * The padding-top value of the `scroller` element + */ + _scrollerPaddingTop: 0, + + /** + * This value is the same as `scrollTop`. + */ + _scrollPosition: 0, + + /** + * The number of tiles in the DOM. + */ + _physicalCount: 0, + + /** + * The k-th tile that is at the top of the scrolling list. + */ + _physicalStart: 0, + + /** + * The k-th tile that is at the bottom of the scrolling list. + */ + _physicalEnd: 0, + + /** + * The sum of the heights of all the tiles in the DOM. + */ + _physicalSize: 0, + + /** + * The average `offsetHeight` of the tiles observed till now. + */ + _physicalAverage: 0, + + /** + * The number of tiles which `offsetHeight` > 0 observed until now. + */ + _physicalAverageCount: 0, + + /** + * The Y position of the item rendered in the `_physicalStart` + * tile relative to the scrolling list. + */ + _physicalTop: 0, + + /** + * The number of items in the list. + */ + _virtualCount: 0, + + /** + * The n-th item rendered in the `_physicalStart` tile. + */ + _virtualStartVal: 0, + + /** + * A map between an item key and its physical item index + */ + _physicalIndexForKey: null, + + /** + * The estimated scroll height based on `_physicalAverage` + */ + _estScrollHeight: 0, + + /** + * The scroll height of the dom node + */ + _scrollHeight: 0, + + /** + * The size of the viewport + */ + _viewportSize: 0, + + /** + * An array of DOM nodes that are currently in the tree + * @type {?Array<!TemplatizerNode>} + */ + _physicalItems: null, + + /** + * An array of heights for each item in `_physicalItems` + * @type {?Array<number>} + */ + _physicalSizes: null, + + /** + * A cached value for the visible index. + * See `firstVisibleIndex` + * @type {?number} + */ + _firstVisibleIndexVal: null, + + /** + * A Polymer collection for the items. + * @type {?Polymer.Collection} + */ + _collection: null, + + /** + * True if the current item list was rendered for the first time + * after attached. + */ + _itemsRendered: false, + + /** + * The bottom of the physical content. + */ + get _physicalBottom() { + return this._physicalTop + this._physicalSize; + }, + + /** + * The n-th item rendered in the last physical item. + */ + get _virtualEnd() { + return this._virtualStartVal + this._physicalCount - 1; + }, + + /** + * The lowest n-th value for an item such that it can be rendered in `_physicalStart`. + */ + _minVirtualStart: 0, + + /** + * The largest n-th value for an item such that it can be rendered in `_physicalStart`. + */ + get _maxVirtualStart() { + return this._virtualCount < this._physicalCount ? + this._virtualCount : this._virtualCount - this._physicalCount; + }, + + /** + * The height of the physical content that isn't on the screen. + */ + get _hiddenContentSize() { + return this._physicalSize - this._viewportSize; + }, + + /** + * The maximum scroll top value. + */ + get _maxScrollTop() { + return this._estScrollHeight - this._viewportSize; + }, + + /** + * Sets the n-th item rendered in `_physicalStart` + */ + set _virtualStart(val) { + // clamp the value so that _minVirtualStart <= val <= _maxVirtualStart + this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val)); + this._physicalStart = this._virtualStartVal % this._physicalCount; + this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount; + }, + + /** + * Gets the n-th item rendered in `_physicalStart` + */ + get _virtualStart() { + return this._virtualStartVal; + }, + + /** + * An optimal physical size such that we will have enough physical items + * to fill up the viewport and recycle when the user scrolls. + * + * This default value assumes that we will at least have the equivalent + * to a viewport of physical items above and below the user's viewport. + */ + get _optPhysicalSize() { + return this._viewportSize * 3; + }, + + /** + * True if the current list is visible. + */ + get _isVisible() { + return this._scroller && Boolean(this._scroller.offsetWidth || this._scroller.offsetHeight); + }, + + /** + * Gets the first visible item in the viewport. + * + * @type {number} + */ + get firstVisibleIndex() { + var physicalOffset; + + if (this._firstVisibleIndexVal === null) { + physicalOffset = this._physicalTop; + + this._firstVisibleIndexVal = this._iterateItems( + function(pidx, vidx) { + physicalOffset += this._physicalSizes[pidx]; + + if (physicalOffset > this._scrollPosition) { + return vidx; + } + }) || 0; + } + + return this._firstVisibleIndexVal; + }, + + ready: function() { + if (IOS_TOUCH_SCROLLING) { + this._scrollListener = function() { + requestAnimationFrame(this._scrollHandler.bind(this)); + }.bind(this); + } else { + this._scrollListener = this._scrollHandler.bind(this); + } + }, + + /** + * When the element has been attached to the DOM tree. + */ + attached: function() { + // delegate to the parent's scroller + // e.g. paper-scroll-header-panel + var el = Polymer.dom(this); + + var parentNode = /** @type {?{scroller: ?Element}} */ (el.parentNode); + if (parentNode && parentNode.scroller) { + this._scroller = parentNode.scroller; + } else { + this._scroller = this; + this.classList.add('has-scroller'); + } + + if (IOS_TOUCH_SCROLLING) { + this._scroller.style.webkitOverflowScrolling = 'touch'; + } + + this._scroller.addEventListener('scroll', this._scrollListener); + + this.updateViewportBoundaries(); + this._render(); + }, + + /** + * When the element has been removed from the DOM tree. + */ + detached: function() { + this._itemsRendered = false; + if (this._scroller) { + this._scroller.removeEventListener('scroll', this._scrollListener); + } + }, + + /** + * Invoke this method if you dynamically update the viewport's + * size or CSS padding. + * + * @method updateViewportBoundaries + */ + updateViewportBoundaries: function() { + var scrollerStyle = window.getComputedStyle(this._scroller); + this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10); + this._viewportSize = this._scroller.offsetHeight; + }, + + /** + * Update the models, the position of the + * items in the viewport and recycle tiles as needed. + */ + _refresh: function() { + var SCROLL_DIRECTION_UP = -1; + var SCROLL_DIRECTION_DOWN = 1; + var SCROLL_DIRECTION_NONE = 0; + + // clamp the `scrollTop` value + // IE 10|11 scrollTop may go above `_maxScrollTop` + // iOS `scrollTop` may go below 0 and above `_maxScrollTop` + var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scroller.scrollTop)); + + var tileHeight, kth, recycledTileSet; + var ratio = this._ratio; + var delta = scrollTop - this._scrollPosition; + var direction = SCROLL_DIRECTION_NONE; + var recycledTiles = 0; + var hiddenContentSize = this._hiddenContentSize; + var currentRatio = ratio; + var movingUp = []; + + // track the last `scrollTop` + this._scrollPosition = scrollTop; + + // clear cached visible index + this._firstVisibleIndexVal = null; + + // random access + if (Math.abs(delta) > this._physicalSize) { + this._physicalTop += delta; + direction = SCROLL_DIRECTION_NONE; + recycledTiles = Math.round(delta / this._physicalAverage); + } + // scroll up + else if (delta < 0) { + var topSpace = scrollTop - this._physicalTop; + var virtualStart = this._virtualStart; + + direction = SCROLL_DIRECTION_UP; + recycledTileSet = []; + + kth = this._physicalEnd; + currentRatio = topSpace / hiddenContentSize; + + // move tiles from bottom to top + while ( + // approximate `currentRatio` to `ratio` + currentRatio < ratio && + // recycle less physical items than the total + recycledTiles < this._physicalCount && + // ensure that these recycled tiles are needed + virtualStart - recycledTiles > 0 + ) { + + tileHeight = this._physicalSizes[kth] || this._physicalAverage; + currentRatio += tileHeight / hiddenContentSize; + + recycledTileSet.push(kth); + recycledTiles++; + kth = (kth === 0) ? this._physicalCount - 1 : kth - 1; + } + + movingUp = recycledTileSet; + recycledTiles = -recycledTiles; + + } + // scroll down + else if (delta > 0) { + var bottomSpace = this._physicalBottom - (scrollTop + this._viewportSize); + var virtualEnd = this._virtualEnd; + var lastVirtualItemIndex = this._virtualCount-1; + + direction = SCROLL_DIRECTION_DOWN; + recycledTileSet = []; + + kth = this._physicalStart; + currentRatio = bottomSpace / hiddenContentSize; + + // move tiles from top to bottom + while ( + // approximate `currentRatio` to `ratio` + currentRatio < ratio && + // recycle less physical items than the total + recycledTiles < this._physicalCount && + // ensure that these recycled tiles are needed + virtualEnd + recycledTiles < lastVirtualItemIndex + ) { + + tileHeight = this._physicalSizes[kth] || this._physicalAverage; + currentRatio += tileHeight / hiddenContentSize; + + this._physicalTop += tileHeight; + recycledTileSet.push(kth); + recycledTiles++; + kth = (kth + 1) % this._physicalCount; + } + } + + if (recycledTiles !== 0) { + this._virtualStart = this._virtualStart + recycledTiles; + this._update(recycledTileSet, movingUp); + } + }, + + /** + * Update the list of items, starting from the `_virtualStartVal` item. + * @param {!Array<number>=} itemSet + * @param {!Array<number>=} movingUp + */ + _update: function(itemSet, movingUp) { + // update models + this._assignModels(itemSet); + + // measure heights + this._updateMetrics(itemSet); + + // adjust offset after measuring + if (movingUp) { + while (movingUp.length) { + this._physicalTop -= this._physicalSizes[movingUp.pop()]; + } + } + // update the position of the items + this._positionItems(); + + // set the scroller size + this._updateScrollerSize(); + + // increase the pool of physical items if needed + if (this._increasePoolIfNeeded()) { + // set models to the new items + this.async(this._update); + } + }, + + /** + * Creates a pool of DOM elements and attaches them to the local dom. + */ + _createPool: function(size) { + var physicalItems = new Array(size); + + this._ensureTemplatized(); + + for (var i = 0; i < size; i++) { + var inst = this.stamp(null); + + // First element child is item; Safari doesn't support children[0] + // on a doc fragment + physicalItems[i] = inst.root.querySelector('*'); + Polymer.dom(this).appendChild(inst.root); + } + + return physicalItems; + }, + + /** + * Increases the pool size. That is, the physical items in the DOM. + * This function will allocate additional physical items + * (limited by `MAX_PHYSICAL_COUNT`) if the content size is shorter than + * `_optPhysicalSize` + * + * @return boolean + */ + _increasePoolIfNeeded: function() { + if (this._physicalSize >= this._optPhysicalSize || this._physicalAverage === 0) { + return false; + } + + // the estimated number of physical items that we will need to reach + // the cap established by `_optPhysicalSize`. + var missingItems = Math.round( + (this._optPhysicalSize - this._physicalSize) * 1.2 / this._physicalAverage + ); + + // limit the size + var nextPhysicalCount = Math.min( + this._physicalCount + missingItems, + this._virtualCount, + MAX_PHYSICAL_COUNT + ); + + var prevPhysicalCount = this._physicalCount; + var delta = nextPhysicalCount - prevPhysicalCount; + + if (delta <= 0) { + return false; + } + + var newPhysicalItems = this._createPool(delta); + var emptyArray = new Array(delta); + + [].push.apply(this._physicalItems, newPhysicalItems); + [].push.apply(this._physicalSizes, emptyArray); + + this._physicalCount = prevPhysicalCount + delta; + + return true; + }, + + /** + * Render a new list of items. This method does exactly the same as `update`, + * but it also ensures that only one `update` cycle is created. + */ + _render: function() { + var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; + + if (this.isAttached && !this._itemsRendered && this._isVisible && requiresUpdate) { + this._update(); + this._itemsRendered = true; + } + }, + + /** + * Templetizes the user template. + */ + _ensureTemplatized: function() { + if (!this.ctor) { + // Template instance props that should be excluded from forwarding + var props = {}; + + props.__key__ = true; + props[this.as] = true; + props[this.indexAs] = true; + props[this.selectedAs] = true; + + this._instanceProps = props; + this._userTemplate = Polymer.dom(this).querySelector('template'); + + if (this._userTemplate) { + this.templatize(this._userTemplate); + } else { + console.warn('iron-list requires a template to be provided in light-dom'); + } + } + }, + + /** + * Implements extension point from Templatizer mixin. + */ + _getStampedChildren: function() { + return this._physicalItems; + }, + + /** + * Implements extension point from Templatizer + * Called as a side effect of a template instance path change, responsible + * for notifying items.<key-for-instance>.<path> change up to host. + */ + _forwardInstancePath: function(inst, path, value) { + if (path.indexOf(this.as + '.') === 0) { + this.notifyPath('items.' + inst.__key__ + '.' + + path.slice(this.as.length + 1), value); + } + }, + + /** + * Implements extension point from Templatizer mixin + * Called as side-effect of a host property change, responsible for + * notifying parent path change on each row. + */ + _forwardParentProp: function(prop, value) { + if (this._physicalItems) { + this._physicalItems.forEach(function(item) { + item._templateInstance[prop] = value; + }, this); + } + }, + + /** + * Implements extension point from Templatizer + * Called as side-effect of a host path change, responsible for + * notifying parent.<path> path change on each row. + */ + _forwardParentPath: function(path, value) { + if (this._physicalItems) { + this._physicalItems.forEach(function(item) { + item._templateInstance.notifyPath(path, value, true); + }, this); + } + }, + + /** + * Called as a side effect of a host items.<key>.<path> path change, + * responsible for notifying item.<path> changes to row for key. + */ + _forwardItemPath: function(path, value) { + if (this._physicalIndexForKey) { + var dot = path.indexOf('.'); + var key = path.substring(0, dot < 0 ? path.length : dot); + var idx = this._physicalIndexForKey[key]; + var row = this._physicalItems[idx]; + if (row) { + var inst = row._templateInstance; + if (dot >= 0) { + path = this.as + '.' + path.substring(dot+1); + inst.notifyPath(path, value, true); + } else { + inst[this.as] = value; + } + } + } + }, + + /** + * Called when the items have changed. That is, ressignments + * to `items`, splices or updates to a single item. + */ + _itemsChanged: function(change) { + if (change.path === 'items') { + // render the new set + this._itemsRendered = false; + + // update the whole set + this._virtualStartVal = 0; + this._physicalTop = 0; + this._virtualCount = this.items ? this.items.length : 0; + this._collection = this.items ? Polymer.Collection.get(this.items) : null; + this._physicalIndexForKey = {}; + + // scroll to the top + this._resetScrollPosition(0); + + // create the initial physical items + if (!this._physicalItems) { + this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, this._virtualCount)); + this._physicalItems = this._createPool(this._physicalCount); + this._physicalSizes = new Array(this._physicalCount); + } + + this.debounce('refresh', this._render); + + } else if (change.path === 'items.splices') { + // render the new set + this._itemsRendered = false; + + this._adjustVirtualIndex(change.value.indexSplices); + this._virtualCount = this.items ? this.items.length : 0; + + this.debounce('refresh', this._render); + + } else { + // update a single item + this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.value); + } + }, + + /** + * @param {!Array<!PolymerSplice>} splices + */ + _adjustVirtualIndex: function(splices) { + var i, splice, idx; + + for (i = 0; i < splices.length; i++) { + splice = splices[i]; + + // deselect removed items + splice.removed.forEach(this.$.selector.deselect, this.$.selector); + + idx = splice.index; + // We only need to care about changes happening above the current position + if (idx >= this._virtualStartVal) { + break; + } + + this._virtualStart = this._virtualStart + + Math.max(splice.addedCount - splice.removed.length, idx - this._virtualStartVal); + } + }, + + _scrollHandler: function() { + this._refresh(); + }, + + /** + * Executes a provided function per every physical index in `itemSet` + * `itemSet` default value is equivalent to the entire set of physical indexes. + * + * @param {!function(number, number)} fn + * @param {!Array<number>=} itemSet + */ + _iterateItems: function(fn, itemSet) { + var pidx, vidx, rtn, i; + + if (arguments.length === 2 && itemSet) { + for (i = 0; i < itemSet.length; i++) { + pidx = itemSet[i]; + if (pidx >= this._physicalStart) { + vidx = this._virtualStartVal + (pidx - this._physicalStart); + } else { + vidx = this._virtualStartVal + (this._physicalCount - this._physicalStart) + pidx; + } + if ((rtn = fn.call(this, pidx, vidx)) != null) { + return rtn; + } + } + } else { + pidx = this._physicalStart; + vidx = this._virtualStartVal; + + for (; pidx < this._physicalCount; pidx++, vidx++) { + if ((rtn = fn.call(this, pidx, vidx)) != null) { + return rtn; + } + } + + pidx = 0; + + for (; pidx < this._physicalStart; pidx++, vidx++) { + if ((rtn = fn.call(this, pidx, vidx)) != null) { + return rtn; + } + } + } + }, + + /** + * Assigns the data models to a given set of items. + * @param {!Array<number>=} itemSet + */ + _assignModels: function(itemSet) { + this._iterateItems(function(pidx, vidx) { + var el = this._physicalItems[pidx]; + var inst = el._templateInstance; + var item = this.items && this.items[vidx]; + + if (item) { + inst[this.as] = item; + inst.__key__ = this._collection.getKey(item); + inst[this.selectedAs] = + /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item); + inst[this.indexAs] = vidx; + el.removeAttribute('hidden'); + this._physicalIndexForKey[inst.__key__] = pidx; + } else { + inst.__key__ = null; + el.setAttribute('hidden', ''); + } + + }, itemSet); + }, + + /** + * Updates the height for a given set of items. + * + * @param {!Array<number>=} itemSet + */ + _updateMetrics: function(itemSet) { + var newPhysicalSize = 0; + var oldPhysicalSize = 0; + var prevAvgCount = this._physicalAverageCount; + var prevPhysicalAvg = this._physicalAverage; + // Make sure we distributed all the physical items + // so we can measure them + Polymer.dom.flush(); + + this._iterateItems(function(pidx, vidx) { + oldPhysicalSize += this._physicalSizes[pidx] || 0; + this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight; + newPhysicalSize += this._physicalSizes[pidx]; + this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0; + }, itemSet); + + this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize; + this._viewportSize = this._scroller.offsetHeight; + + // update the average if we measured something + if (this._physicalAverageCount !== prevAvgCount) { + this._physicalAverage = Math.round( + ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / + this._physicalAverageCount); + } + }, + + /** + * Updates the position of the physical items. + */ + _positionItems: function() { + this._adjustScrollPosition(); + + var y = this._physicalTop; + + this._iterateItems(function(pidx) { + + this.transform('translate3d(0, ' + y + 'px, 0)', this._physicalItems[pidx]); + y += this._physicalSizes[pidx]; + + }); + }, + + /** + * Adjusts the scroll position when it was overestimated. + */ + _adjustScrollPosition: function() { + var deltaHeight = this._virtualStartVal === 0 ? this._physicalTop : + Math.min(this._scrollPosition + this._physicalTop, 0); + + if (deltaHeight) { + this._physicalTop = this._physicalTop - deltaHeight; + + // juking scroll position during interial scrolling on iOS is no bueno + if (!IOS_TOUCH_SCROLLING) { + this._resetScrollPosition(this._scroller.scrollTop - deltaHeight); + } + } + }, + + /** + * Sets the position of the scroll. + */ + _resetScrollPosition: function(pos) { + if (this._scroller) { + this._scroller.scrollTop = pos; + this._scrollPosition = this._scroller.scrollTop; + } + }, + + /** + * Sets the scroll height, that's the height of the content, + * + * @param {boolean=} forceUpdate If true, updates the height no matter what. + */ + _updateScrollerSize: function(forceUpdate) { + this._estScrollHeight = (this._physicalBottom + + Math.max(this._virtualCount - this._physicalCount - this._virtualStartVal, 0) * this._physicalAverage); + + forceUpdate = forceUpdate || this._scrollHeight === 0; + forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize; + + // amortize height adjustment, so it won't trigger repaints very often + if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) { + this.$.items.style.height = this._estScrollHeight + 'px'; + this._scrollHeight = this._estScrollHeight; + } + }, + + /** + * Scroll to a specific item in the virtual list regardless + * of the physical items in the DOM tree. + * + * @method scrollToIndex + * @param {number} idx The index of the item + */ + scrollToIndex: function(idx) { + if (typeof idx !== 'number') { + return; + } + + var firstVisible = this.firstVisibleIndex; + + idx = Math.min(Math.max(idx, 0), this._virtualCount-1); + + // start at the previous virtual item + // so we have a item above the first visible item + this._virtualStart = idx - 1; + + // assign new models + this._assignModels(); + + // measure the new sizes + this._updateMetrics(); + + // estimate new physical offset + this._physicalTop = this._virtualStart * this._physicalAverage; + + var currentTopItem = this._physicalStart; + var currentVirtualItem = this._virtualStart; + var targetOffsetTop = 0; + var hiddenContentSize = this._hiddenContentSize; + + // scroll to the item as much as we can + while (currentVirtualItem !== idx && targetOffsetTop < hiddenContentSize) { + targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem]; + currentTopItem = (currentTopItem + 1) % this._physicalCount; + currentVirtualItem++; + } + + // update the scroller size + this._updateScrollerSize(true); + + // update the position of the items + this._positionItems(); + + // set the new scroll position + this._resetScrollPosition(this._physicalTop + targetOffsetTop + 1); + + // increase the pool of physical items if needed + if (this._increasePoolIfNeeded()) { + // set models to the new items + this.async(this._update); + } + + // clear cached visible index + this._firstVisibleIndexVal = null; + }, + + /** + * Reset the physical average and the average count. + */ + _resetAverage: function() { + this._physicalAverage = 0; + this._physicalAverageCount = 0; + }, + + /** + * A handler for the `iron-resize` event triggered by `IronResizableBehavior` + * when the element is resized. + */ + _resizeHandler: function() { + this.debounce('resize', function() { + this._render(); + if (this._itemsRendered && this._physicalItems && this._isVisible) { + this._resetAverage(); + this.updateViewportBoundaries(); + this.scrollToIndex(this.firstVisibleIndex); + } + }); + }, + + _getModelFromItem: function(item) { + var key = this._collection.getKey(item); + var pidx = this._physicalIndexForKey[key]; + + if (pidx !== undefined) { + return this._physicalItems[pidx]._templateInstance; + } + return null; + }, + + /** + * Gets a valid item instance from its index or the object value. + * + * @param {(Object|number)} item The item object or its index + */ + _getNormalizedItem: function(item) { + if (typeof item === 'number') { + item = this.items[item]; + if (!item) { + throw new RangeError('<item> not found'); + } + } else if (this._collection.getKey(item) === undefined) { + throw new TypeError('<item> should be a valid item'); + } + return item; + }, + + /** + * Select the list item at the given index. + * + * @method selectItem + * @param {(Object|number)} item The item object or its index + */ + selectItem: function(item) { + item = this._getNormalizedItem(item); + var model = this._getModelFromItem(item); + + if (!this.multiSelection && this.selectedItem) { + this.deselectItem(this.selectedItem); + } + if (model) { + model[this.selectedAs] = true; + } + this.$.selector.select(item); + }, + + /** + * Deselects the given item list if it is already selected. + * + + * @method deselect + * @param {(Object|number)} item The item object or its index + */ + deselectItem: function(item) { + item = this._getNormalizedItem(item); + var model = this._getModelFromItem(item); + + if (model) { + model[this.selectedAs] = false; + } + this.$.selector.deselect(item); + }, + + /** + * Select or deselect a given item depending on whether the item + * has already been selected. + * + * @method toggleSelectionForItem + * @param {(Object|number)} item The item object or its index + */ + toggleSelectionForItem: function(item) { + item = this._getNormalizedItem(item); + if (/** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item)) { + this.deselectItem(item); + } else { + this.selectItem(item); + } + }, + + /** + * Clears the current selection state of the list. + * + * @method clearSelection + */ + clearSelection: function() { + function unselect(item) { + var model = this._getModelFromItem(item); + if (model) { + model[this.selectedAs] = false; + } + } + + if (Array.isArray(this.selectedItems)) { + this.selectedItems.forEach(unselect, this); + } else if (this.selectedItem) { + unselect.call(this, this.selectedItem); + } + + /** @type {!ArraySelectorElement} */ (this.$.selector).clearSelection(); + }, + + /** + * Add an event listener to `tap` if `selectionEnabled` is true, + * it will remove the listener otherwise. + */ + _selectionEnabledChanged: function(selectionEnabled) { + if (selectionEnabled) { + this.listen(this, 'tap', '_selectionHandler'); + this.listen(this, 'keypress', '_selectionHandler'); + } else { + this.unlisten(this, 'tap', '_selectionHandler'); + this.unlisten(this, 'keypress', '_selectionHandler'); + } + }, + + /** + * Select an item from an event object. + */ + _selectionHandler: function(e) { + if (e.type !== 'keypress' || e.keyCode === 13) { + var model = this.modelForElement(e.target); + if (model) { + this.toggleSelectionForItem(model[this.as]); + } + } + }, + + _multiSelectionChanged: function(multiSelection) { + this.clearSelection(); + this.$.selector.multi = multiSelection; + }, + + /** + * Updates the size of an item. + * + * @method updateSizeForItem + * @param {(Object|number)} item The item object or its index + */ + updateSizeForItem: function(item) { + item = this._getNormalizedItem(item); + var key = this._collection.getKey(item); + var pidx = this._physicalIndexForKey[key]; + + if (pidx !== undefined) { + this._updateMetrics([pidx]); + this._positionItems(); + } + } + }); + +})(); (function() { 'use strict'; @@ -9018,6 +10324,535 @@ }); })(); +(function() { + + // monostate data + var metaDatas = {}; + var metaArrays = {}; + + Polymer.IronMeta = Polymer({ + + is: 'iron-meta', + + properties: { + + /** + * The type of meta-data. All meta-data of the same type is stored + * together. + */ + type: { + type: String, + value: 'default', + observer: '_typeChanged' + }, + + /** + * The key used to store `value` under the `type` namespace. + */ + key: { + type: String, + observer: '_keyChanged' + }, + + /** + * The meta-data to store or retrieve. + */ + value: { + type: Object, + notify: true, + observer: '_valueChanged' + }, + + /** + * If true, `value` is set to the iron-meta instance itself. + */ + self: { + type: Boolean, + observer: '_selfChanged' + }, + + /** + * Array of all meta-data values for the given type. + */ + list: { + type: Array, + notify: true + } + + }, + + /** + * Only runs if someone invokes the factory/constructor directly + * e.g. `new Polymer.IronMeta()` + */ + factoryImpl: function(config) { + if (config) { + for (var n in config) { + switch(n) { + case 'type': + case 'key': + case 'value': + this[n] = config[n]; + break; + } + } + } + }, + + created: function() { + // TODO(sjmiles): good for debugging? + this._metaDatas = metaDatas; + this._metaArrays = metaArrays; + }, + + _keyChanged: function(key, old) { + this._resetRegistration(old); + }, + + _valueChanged: function(value) { + this._resetRegistration(this.key); + }, + + _selfChanged: function(self) { + if (self) { + this.value = this; + } + }, + + _typeChanged: function(type) { + this._unregisterKey(this.key); + if (!metaDatas[type]) { + metaDatas[type] = {}; + } + this._metaData = metaDatas[type]; + if (!metaArrays[type]) { + metaArrays[type] = []; + } + this.list = metaArrays[type]; + this._registerKeyValue(this.key, this.value); + }, + + /** + * Retrieves meta data value by key. + * + * @method byKey + * @param {string} key The key of the meta-data to be returned. + * @return {*} + */ + byKey: function(key) { + return this._metaData && this._metaData[key]; + }, + + _resetRegistration: function(oldKey) { + this._unregisterKey(oldKey); + this._registerKeyValue(this.key, this.value); + }, + + _unregisterKey: function(key) { + this._unregister(key, this._metaData, this.list); + }, + + _registerKeyValue: function(key, value) { + this._register(key, value, this._metaData, this.list); + }, + + _register: function(key, value, data, list) { + if (key && data && value !== undefined) { + data[key] = value; + list.push(value); + } + }, + + _unregister: function(key, data, list) { + if (key && data) { + if (key in data) { + var value = data[key]; + delete data[key]; + this.arrayDelete(list, value); + } + } + } + + }); + + /** + `iron-meta-query` can be used to access infomation stored in `iron-meta`. + + Examples: + + If I create an instance like this: + + <iron-meta key="info" value="foo/bar"></iron-meta> + + Note that value="foo/bar" is the metadata I've defined. I could define more + attributes or use child nodes to define additional metadata. + + Now I can access that element (and it's metadata) from any `iron-meta-query` instance: + + var value = new Polymer.IronMetaQuery({key: 'info'}).value; + + @group Polymer Iron Elements + @element iron-meta-query + */ + Polymer.IronMetaQuery = Polymer({ + + is: 'iron-meta-query', + + properties: { + + /** + * The type of meta-data. All meta-data of the same type is stored + * together. + */ + type: { + type: String, + value: 'default', + observer: '_typeChanged' + }, + + /** + * Specifies a key to use for retrieving `value` from the `type` + * namespace. + */ + key: { + type: String, + observer: '_keyChanged' + }, + + /** + * The meta-data to store or retrieve. + */ + value: { + type: Object, + notify: true, + readOnly: true + }, + + /** + * Array of all meta-data values for the given type. + */ + list: { + type: Array, + notify: true + } + + }, + + /** + * Actually a factory method, not a true constructor. Only runs if + * someone invokes it directly (via `new Polymer.IronMeta()`); + */ + factoryImpl: function(config) { + if (config) { + for (var n in config) { + switch(n) { + case 'type': + case 'key': + this[n] = config[n]; + break; + } + } + } + }, + + created: function() { + // TODO(sjmiles): good for debugging? + this._metaDatas = metaDatas; + this._metaArrays = metaArrays; + }, + + _keyChanged: function(key) { + this._setValue(this._metaData && this._metaData[key]); + }, + + _typeChanged: function(type) { + this._metaData = metaDatas[type]; + this.list = metaArrays[type]; + if (this.key) { + this._keyChanged(this.key); + } + }, + + /** + * Retrieves meta data value by key. + * @param {string} key The key of the meta-data to be returned. + * @return {*} + */ + byKey: function(key) { + return this._metaData && this._metaData[key]; + } + + }); + + })(); +Polymer({ + + is: 'iron-icon', + + properties: { + + /** + * The name of the icon to use. The name should be of the form: + * `iconset_name:icon_name`. + */ + icon: { + type: String, + observer: '_iconChanged' + }, + + /** + * The name of the theme to used, if one is specified by the + * iconset. + */ + theme: { + type: String, + observer: '_updateIcon' + }, + + /** + * If using iron-icon without an iconset, you can set the src to be + * the URL of an individual icon image file. Note that this will take + * precedence over a given icon attribute. + */ + src: { + type: String, + observer: '_srcChanged' + }, + + /** + * @type {!Polymer.IronMeta} + */ + _meta: { + value: Polymer.Base.create('iron-meta', {type: 'iconset'}) + } + + }, + + _DEFAULT_ICONSET: 'icons', + + _iconChanged: function(icon) { + var parts = (icon || '').split(':'); + this._iconName = parts.pop(); + this._iconsetName = parts.pop() || this._DEFAULT_ICONSET; + this._updateIcon(); + }, + + _srcChanged: function(src) { + this._updateIcon(); + }, + + _usesIconset: function() { + return this.icon || !this.src; + }, + + /** @suppress {visibility} */ + _updateIcon: function() { + if (this._usesIconset()) { + if (this._iconsetName) { + this._iconset = /** @type {?Polymer.Iconset} */ ( + this._meta.byKey(this._iconsetName)); + if (this._iconset) { + this._iconset.applyIcon(this, this._iconName, this.theme); + this.unlisten(window, 'iron-iconset-added', '_updateIcon'); + } else { + this.listen(window, 'iron-iconset-added', '_updateIcon'); + } + } + } else { + if (!this._img) { + this._img = document.createElement('img'); + this._img.style.width = '100%'; + this._img.style.height = '100%'; + this._img.draggable = false; + } + this._img.src = this.src; + Polymer.dom(this.root).appendChild(this._img); + } + } + + }); +/** + * The `iron-iconset-svg` element allows users to define their own icon sets + * that contain svg icons. The svg icon elements should be children of the + * `iron-iconset-svg` element. Multiple icons should be given distinct id's. + * + * Using svg elements to create icons has a few advantages over traditional + * bitmap graphics like jpg or png. Icons that use svg are vector based so they + * are resolution independent and should look good on any device. They are + * stylable via css. Icons can be themed, colorized, and even animated. + * + * Example: + * + * <iron-iconset-svg name="my-svg-icons" size="24"> + * <svg> + * <defs> + * <g id="shape"> + * <rect x="50" y="50" width="50" height="50" /> + * <circle cx="50" cy="50" r="50" /> + * </g> + * </defs> + * </svg> + * </iron-iconset-svg> + * + * This will automatically register the icon set "my-svg-icons" to the iconset + * database. To use these icons from within another element, make a + * `iron-iconset` element and call the `byId` method + * to retrieve a given iconset. To apply a particular icon inside an + * element use the `applyIcon` method. For example: + * + * iconset.applyIcon(iconNode, 'car'); + * + * @element iron-iconset-svg + * @demo demo/index.html + */ + Polymer({ + + is: 'iron-iconset-svg', + + properties: { + + /** + * The name of the iconset. + * + * @attribute name + * @type string + */ + name: { + type: String, + observer: '_nameChanged' + }, + + /** + * The size of an individual icon. Note that icons must be square. + * + * @attribute iconSize + * @type number + * @default 24 + */ + size: { + type: Number, + value: 24 + } + + }, + + /** + * Construct an array of all icon names in this iconset. + * + * @return {!Array} Array of icon names. + */ + getIconNames: function() { + this._icons = this._createIconMap(); + return Object.keys(this._icons).map(function(n) { + return this.name + ':' + n; + }, this); + }, + + /** + * Applies an icon to the given element. + * + * An svg icon is prepended to the element's shadowRoot if it exists, + * otherwise to the element itself. + * + * @method applyIcon + * @param {Element} element Element to which the icon is applied. + * @param {string} iconName Name of the icon to apply. + * @return {Element} The svg element which renders the icon. + */ + applyIcon: function(element, iconName) { + // insert svg element into shadow root, if it exists + element = element.root || element; + // Remove old svg element + this.removeIcon(element); + // install new svg element + var svg = this._cloneIcon(iconName); + if (svg) { + var pde = Polymer.dom(element); + pde.insertBefore(svg, pde.childNodes[0]); + return element._svgIcon = svg; + } + return null; + }, + + /** + * Remove an icon from the given element by undoing the changes effected + * by `applyIcon`. + * + * @param {Element} element The element from which the icon is removed. + */ + removeIcon: function(element) { + // Remove old svg element + if (element._svgIcon) { + Polymer.dom(element).removeChild(element._svgIcon); + element._svgIcon = null; + } + }, + + /** + * + * When name is changed, register iconset metadata + * + */ + _nameChanged: function() { + new Polymer.IronMeta({type: 'iconset', key: this.name, value: this}); + this.async(function() { + this.fire('iron-iconset-added', this, {node: window}); + }); + }, + + /** + * Create a map of child SVG elements by id. + * + * @return {!Object} Map of id's to SVG elements. + */ + _createIconMap: function() { + // Objects chained to Object.prototype (`{}`) have members. Specifically, + // on FF there is a `watch` method that confuses the icon map, so we + // need to use a null-based object here. + var icons = Object.create(null); + Polymer.dom(this).querySelectorAll('[id]') + .forEach(function(icon) { + icons[icon.id] = icon; + }); + return icons; + }, + + /** + * Produce installable clone of the SVG element matching `id` in this + * iconset, or `undefined` if there is no matching element. + * + * @return {Element} Returns an installable clone of the SVG element + * matching `id`. + */ + _cloneIcon: function(id) { + // create the icon map on-demand, since the iconset itself has no discrete + // signal to know when it's children are fully parsed + this._icons = this._icons || this._createIconMap(); + return this._prepareSvgClone(this._icons[id], this.size); + }, + + /** + * @param {Element} sourceSvg + * @param {number} size + * @return {Element} + */ + _prepareSvgClone: function(sourceSvg, size) { + if (sourceSvg) { + var content = sourceSvg.cloneNode(true), + svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), + viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size; + svg.setAttribute('viewBox', viewBox); + svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); + // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136 + // TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root + svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;'; + svg.appendChild(content).removeAttribute('id'); + return svg; + } + return null; + } + + }); Polymer({ is: 'paper-material', @@ -9838,6 +11673,17 @@ observer: '_holdDownChanged' }, + /** + * If true, the ripple will not generate a ripple effect + * via pointer interaction. + * Calling ripple's imperative api like `simulatedRipple` will + * still generate the ripple effect. + */ + noink: { + type: Boolean, + value: false + }, + _animating: { type: Boolean }, @@ -9850,6 +11696,10 @@ } }, + observers: [ + '_noinkChanged(noink, isAttached)' + ], + get target () { var ownerRoot = Polymer.dom(this).getOwnerRoot(); var target; @@ -9870,12 +11720,13 @@ }, attached: function() { - this.listen(this.target, 'up', 'upAction'); - this.listen(this.target, 'down', 'downAction'); + this.listen(this.target, 'up', 'uiUpAction'); + this.listen(this.target, 'down', 'uiDownAction'); + }, - if (!this.target.hasAttribute('noink')) { - this.keyEventTarget = this.target; - } + detached: function() { + this.unlisten(this.target, 'up', 'uiUpAction'); + this.unlisten(this.target, 'down', 'uiDownAction'); }, get shouldKeepAnimating () { @@ -9897,7 +11748,22 @@ }, 1); }, - /** @param {Event=} event */ + /** + * Provokes a ripple down effect via a UI event, + * respecting the `noink` property. + * @param {Event=} event + */ + uiDownAction: function(event) { + if (!this.noink) { + this.downAction(event); + } + }, + + /** + * Provokes a ripple down effect via a UI event, + * *not* respecting the `noink` property. + * @param {Event=} event + */ downAction: function(event) { if (this.holdDown && this.ripples.length > 0) { return; @@ -9912,7 +11778,22 @@ } }, - /** @param {Event=} event */ + /** + * Provokes a ripple up effect via a UI event, + * respecting the `noink` property. + * @param {Event=} event + */ + uiUpAction: function(event) { + if (!this.noink) { + this.upAction(event); + } + }, + + /** + * Provokes a ripple up effect via a UI event, + * *not* respecting the `noink` property. + * @param {Event=} event + */ upAction: function(event) { if (this.holdDown) { return; @@ -9985,24 +11866,35 @@ }, _onEnterKeydown: function() { - this.downAction(); - this.async(this.upAction, 1); + this.uiDownAction(); + this.async(this.uiUpAction, 1); }, _onSpaceKeydown: function() { - this.downAction(); + this.uiDownAction(); }, _onSpaceKeyup: function() { - this.upAction(); + this.uiUpAction(); }, - _holdDownChanged: function(holdDown) { - if (holdDown) { + // note: holdDown does not respect noink since it can be a focus based + // effect. + _holdDownChanged: function(newVal, oldVal) { + if (oldVal === undefined) { + return; + } + if (newVal) { this.downAction(); } else { this.upAction(); } + }, + + _noinkChanged: function(noink, attached) { + if (attached) { + this.keyEventTarget = noink ? this : this.target; + } } }); })(); @@ -10208,33 +12100,7 @@ } }, - _eventSourceIsPrimaryInput: function(event) { - event = event.detail.sourceEvent || event; - - // Always true for non-mouse events.... - if (!this._mouseEventRe.test(event.type)) { - return true; - } - - // http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons - if ('buttons' in event) { - return event.buttons === 1; - } - - // http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which - if (typeof event.which === 'number') { - return event.which < 2; - } - - // http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button - return event.button < 1; - }, - _downHandler: function(event) { - if (!this._eventSourceIsPrimaryInput(event)) { - return; - } - this._setPointerDown(true); this._setPressed(true); this._setReceivedFocusFromKeyboard(false); @@ -10312,24 +12178,138 @@ Polymer.IronA11yKeysBehavior, Polymer.IronButtonStateImpl ]; -/** @polymerBehavior */ +/** + * `Polymer.PaperRippleBehavior` dynamically implements a ripple + * when the element has focus via pointer or keyboard. + * + * NOTE: This behavior is intended to be used in conjunction with and after + * `Polymer.IronButtonState` and `Polymer.IronControlState`. + * + * @polymerBehavior Polymer.PaperRippleBehavior + */ + Polymer.PaperRippleBehavior = { + + properties: { + /** + * If true, the element will not produce a ripple effect when interacted + * with via the pointer. + */ + noink: { + type: Boolean, + observer: '_noinkChanged' + } + }, + + /** + * Ensures a `<paper-ripple>` element is available when the element is + * focused. + */ + _buttonStateChanged: function() { + if (this.focused) { + this.ensureRipple(); + } + }, + + /** + * In addition to the functionality provided in `IronButtonState`, ensures + * a ripple effect is created when the element is in a `pressed` state. + */ + _downHandler: function(event) { + Polymer.IronButtonStateImpl._downHandler.call(this, event); + if (this.pressed) { + this.ensureRipple(event); + } + }, + + /** + * Ensures this element contains a ripple effect. For startup efficiency + * the ripple effect is dynamically on demand when needed. + * @param {!Event=} opt_triggeringEvent (optional) event that triggered the + * ripple. + */ + ensureRipple: function(opt_triggeringEvent) { + if (!this.hasRipple()) { + this._ripple = this._createRipple(); + this._ripple.noink = this.noink; + var rippleContainer = this._rippleContainer || this.root; + if (rippleContainer) { + Polymer.dom(rippleContainer).appendChild(this._ripple); + } + var domContainer = rippleContainer === this.shadyRoot ? this : + rippleContainer; + if (opt_triggeringEvent && + domContainer.contains(opt_triggeringEvent.target)) { + this._ripple.uiDownAction(opt_triggeringEvent); + } + } + }, + + /** + * Returns the `<paper-ripple>` element used by this element to create + * ripple effects. The element's ripple is created on demand, when + * necessary, and calling this method will force the + * ripple to be created. + */ + getRipple: function() { + this.ensureRipple(); + return this._ripple; + }, + + /** + * Returns true if this element currently contains a ripple effect. + * @return {boolean} + */ + hasRipple: function() { + return Boolean(this._ripple); + }, + + /** + * Create the element's ripple effect via creating a `<paper-ripple>`. + * Override this method to customize the ripple element. + * @return {element} Returns a `<paper-ripple>` element. + */ + _createRipple: function() { + return document.createElement('paper-ripple'); + }, + + _noinkChanged: function(noink) { + if (this.hasRipple()) { + this._ripple.noink = noink; + } + } + + }; +/** @polymerBehavior Polymer.PaperButtonBehavior */ Polymer.PaperButtonBehaviorImpl = { properties: { - _elevation: { - type: Number + /** + * The z-depth of this element, from 0-5. Setting to 0 will remove the + * shadow, and each increasing number greater than 0 will be "deeper" + * than the last. + * + * @attribute elevation + * @type number + * @default 1 + */ + elevation: { + type: Number, + reflectToAttribute: true, + readOnly: true } }, observers: [ - '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)' + '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)', + '_computeKeyboardClass(receivedFocusFromKeyboard)' ], hostAttributes: { role: 'button', - tabindex: '0' + tabindex: '0', + animated: true }, _calculateElevation: function() { @@ -10341,14 +12321,42 @@ } else if (this.receivedFocusFromKeyboard) { e = 3; } - this._elevation = e; + this._setElevation(e); + }, + + _computeKeyboardClass: function(receivedFocusFromKeyboard) { + this.classList.toggle('keyboard-focus', receivedFocusFromKeyboard); + }, + + /** + * In addition to `IronButtonState` behavior, when space key goes down, + * create a ripple down effect. + */ + _spaceKeyDownHandler: function(event) { + Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event); + if (this.hasRipple()) { + this._ripple.uiDownAction(); + } + }, + + /** + * In addition to `IronButtonState` behavior, when space key goes up, + * create a ripple up effect. + */ + _spaceKeyUpHandler: function(event) { + Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event); + if (this.hasRipple()) { + this._ripple.uiUpAction(); + } } + }; /** @polymerBehavior */ Polymer.PaperButtonBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, + Polymer.PaperRippleBehavior, Polymer.PaperButtonBehaviorImpl ]; Polymer({ @@ -10372,18 +12380,10 @@ _calculateElevation: function() { if (!this.raised) { - this._elevation = 0; + this.elevation = 0; } else { Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this); } - }, - - _computeContentClass: function(receivedFocusFromKeyboard) { - var className = 'content '; - if (receivedFocusFromKeyboard) { - className += ' keyboard-focus'; - } - return className; } }); /** @@ -10587,14 +12587,6 @@ var Item = Polymer({ is: 'downloads-item', - /** - * @param {!downloads.ThrottledIconLoader} iconLoader - */ - factoryImpl: function(iconLoader) { - /** @private {!downloads.ThrottledIconLoader} */ - this.iconLoader_ = iconLoader; - }, - properties: { data: { type: Object, @@ -10605,15 +12597,6 @@ value: true, }, - readyPromise: { - type: Object, - value: function() { - return new Promise(function(resolve, reject) { - this.resolveReadyPromise_ = resolve; - }.bind(this)); - }, - }, - completelyOnDisk_: { computed: 'computeCompletelyOnDisk_(' + 'data.state, data.file_externally_removed)', @@ -10686,21 +12669,11 @@ // TODO(dbeam): this gets called way more when I observe data.by_ext_id // and data.by_ext_name directly. Why? 'observeControlledBy_(controlledBy_)', + 'observeIsDangerous_(isDangerous_, data.file_path)', ], ready: function() { this.content = this.$.content; - this.resolveReadyPromise_(); - }, - - /** @param {!downloads.Data} data */ - update: function(data) { - this.data = data; - - if (!this.isDangerous_) { - var icon = 'chrome://fileicon/' + encodeURIComponent(data.file_path); - this.iconLoader_.loadScaledIcon(this.$['file-icon'], icon); - } }, /** @private */ @@ -10736,6 +12709,23 @@ }, /** @private */ + computeDangerIcon_: function() { + if (!this.isDangerous_) + return ''; + + switch (this.data.danger_type) { + case downloads.DangerType.DANGEROUS_CONTENT: + case downloads.DangerType.DANGEROUS_HOST: + case downloads.DangerType.DANGEROUS_URL: + case downloads.DangerType.POTENTIALLY_UNWANTED: + case downloads.DangerType.UNCOMMON_CONTENT: + return 'remove-circle'; + default: + return 'warning'; + } + }, + + /** @private */ computeDate_: function() { if (this.hideDate) return ''; @@ -10844,6 +12834,14 @@ }, /** @private */ + observeIsDangerous_: function() { + if (this.data && !this.isDangerous_) { + var filePath = encodeURIComponent(this.data.file_path); + this.$['file-icon'].src = 'chrome://fileicon/' + filePath; + } + }, + + /** @private */ onCancelTap_: function() { downloads.ActionService.getInstance().cancel(this.data.id); }, @@ -10904,533 +12902,6 @@ return {Item: Item}; }); -(function() { - - // monostate data - var metaDatas = {}; - var metaArrays = {}; - - Polymer.IronMeta = Polymer({ - - is: 'iron-meta', - - properties: { - - /** - * The type of meta-data. All meta-data of the same type is stored - * together. - */ - type: { - type: String, - value: 'default', - observer: '_typeChanged' - }, - - /** - * The key used to store `value` under the `type` namespace. - */ - key: { - type: String, - observer: '_keyChanged' - }, - - /** - * The meta-data to store or retrieve. - */ - value: { - type: Object, - notify: true, - observer: '_valueChanged' - }, - - /** - * If true, `value` is set to the iron-meta instance itself. - */ - self: { - type: Boolean, - observer: '_selfChanged' - }, - - /** - * Array of all meta-data values for the given type. - */ - list: { - type: Array, - notify: true - } - - }, - - /** - * Only runs if someone invokes the factory/constructor directly - * e.g. `new Polymer.IronMeta()` - */ - factoryImpl: function(config) { - if (config) { - for (var n in config) { - switch(n) { - case 'type': - case 'key': - case 'value': - this[n] = config[n]; - break; - } - } - } - }, - - created: function() { - // TODO(sjmiles): good for debugging? - this._metaDatas = metaDatas; - this._metaArrays = metaArrays; - }, - - _keyChanged: function(key, old) { - this._resetRegistration(old); - }, - - _valueChanged: function(value) { - this._resetRegistration(this.key); - }, - - _selfChanged: function(self) { - if (self) { - this.value = this; - } - }, - - _typeChanged: function(type) { - this._unregisterKey(this.key); - if (!metaDatas[type]) { - metaDatas[type] = {}; - } - this._metaData = metaDatas[type]; - if (!metaArrays[type]) { - metaArrays[type] = []; - } - this.list = metaArrays[type]; - this._registerKeyValue(this.key, this.value); - }, - - /** - * Retrieves meta data value by key. - * - * @method byKey - * @param {string} key The key of the meta-data to be returned. - * @return {*} - */ - byKey: function(key) { - return this._metaData && this._metaData[key]; - }, - - _resetRegistration: function(oldKey) { - this._unregisterKey(oldKey); - this._registerKeyValue(this.key, this.value); - }, - - _unregisterKey: function(key) { - this._unregister(key, this._metaData, this.list); - }, - - _registerKeyValue: function(key, value) { - this._register(key, value, this._metaData, this.list); - }, - - _register: function(key, value, data, list) { - if (key && data && value !== undefined) { - data[key] = value; - list.push(value); - } - }, - - _unregister: function(key, data, list) { - if (key && data) { - if (key in data) { - var value = data[key]; - delete data[key]; - this.arrayDelete(list, value); - } - } - } - - }); - - /** - `iron-meta-query` can be used to access infomation stored in `iron-meta`. - - Examples: - - If I create an instance like this: - - <iron-meta key="info" value="foo/bar"></iron-meta> - - Note that value="foo/bar" is the metadata I've defined. I could define more - attributes or use child nodes to define additional metadata. - - Now I can access that element (and it's metadata) from any `iron-meta-query` instance: - - var value = new Polymer.IronMetaQuery({key: 'info'}).value; - - @group Polymer Iron Elements - @element iron-meta-query - */ - Polymer.IronMetaQuery = Polymer({ - - is: 'iron-meta-query', - - properties: { - - /** - * The type of meta-data. All meta-data of the same type is stored - * together. - */ - type: { - type: String, - value: 'default', - observer: '_typeChanged' - }, - - /** - * Specifies a key to use for retrieving `value` from the `type` - * namespace. - */ - key: { - type: String, - observer: '_keyChanged' - }, - - /** - * The meta-data to store or retrieve. - */ - value: { - type: Object, - notify: true, - readOnly: true - }, - - /** - * Array of all meta-data values for the given type. - */ - list: { - type: Array, - notify: true - } - - }, - - /** - * Actually a factory method, not a true constructor. Only runs if - * someone invokes it directly (via `new Polymer.IronMeta()`); - */ - factoryImpl: function(config) { - if (config) { - for (var n in config) { - switch(n) { - case 'type': - case 'key': - this[n] = config[n]; - break; - } - } - } - }, - - created: function() { - // TODO(sjmiles): good for debugging? - this._metaDatas = metaDatas; - this._metaArrays = metaArrays; - }, - - _keyChanged: function(key) { - this._setValue(this._metaData && this._metaData[key]); - }, - - _typeChanged: function(type) { - this._metaData = metaDatas[type]; - this.list = metaArrays[type]; - if (this.key) { - this._keyChanged(this.key); - } - }, - - /** - * Retrieves meta data value by key. - * @param {string} key The key of the meta-data to be returned. - * @return {*} - */ - byKey: function(key) { - return this._metaData && this._metaData[key]; - } - - }); - - })(); -Polymer({ - - is: 'iron-icon', - - properties: { - - /** - * The name of the icon to use. The name should be of the form: - * `iconset_name:icon_name`. - */ - icon: { - type: String, - observer: '_iconChanged' - }, - - /** - * The name of the theme to used, if one is specified by the - * iconset. - */ - theme: { - type: String, - observer: '_updateIcon' - }, - - /** - * If using iron-icon without an iconset, you can set the src to be - * the URL of an individual icon image file. Note that this will take - * precedence over a given icon attribute. - */ - src: { - type: String, - observer: '_srcChanged' - }, - - /** - * @type {!Polymer.IronMeta} - */ - _meta: { - value: Polymer.Base.create('iron-meta', {type: 'iconset'}) - } - - }, - - _DEFAULT_ICONSET: 'icons', - - _iconChanged: function(icon) { - var parts = (icon || '').split(':'); - this._iconName = parts.pop(); - this._iconsetName = parts.pop() || this._DEFAULT_ICONSET; - this._updateIcon(); - }, - - _srcChanged: function(src) { - this._updateIcon(); - }, - - _usesIconset: function() { - return this.icon || !this.src; - }, - - /** @suppress {visibility} */ - _updateIcon: function() { - if (this._usesIconset()) { - if (this._iconsetName) { - this._iconset = /** @type {?Polymer.Iconset} */ ( - this._meta.byKey(this._iconsetName)); - if (this._iconset) { - this._iconset.applyIcon(this, this._iconName, this.theme); - this.unlisten(window, 'iron-iconset-added', '_updateIcon'); - } else { - this.listen(window, 'iron-iconset-added', '_updateIcon'); - } - } - } else { - if (!this._img) { - this._img = document.createElement('img'); - this._img.style.width = '100%'; - this._img.style.height = '100%'; - this._img.draggable = false; - } - this._img.src = this.src; - Polymer.dom(this.root).appendChild(this._img); - } - } - - }); -/** - * The `iron-iconset-svg` element allows users to define their own icon sets - * that contain svg icons. The svg icon elements should be children of the - * `iron-iconset-svg` element. Multiple icons should be given distinct id's. - * - * Using svg elements to create icons has a few advantages over traditional - * bitmap graphics like jpg or png. Icons that use svg are vector based so they - * are resolution independent and should look good on any device. They are - * stylable via css. Icons can be themed, colorized, and even animated. - * - * Example: - * - * <iron-iconset-svg name="my-svg-icons" size="24"> - * <svg> - * <defs> - * <g id="shape"> - * <rect x="50" y="50" width="50" height="50" /> - * <circle cx="50" cy="50" r="50" /> - * </g> - * </defs> - * </svg> - * </iron-iconset-svg> - * - * This will automatically register the icon set "my-svg-icons" to the iconset - * database. To use these icons from within another element, make a - * `iron-iconset` element and call the `byId` method - * to retrieve a given iconset. To apply a particular icon inside an - * element use the `applyIcon` method. For example: - * - * iconset.applyIcon(iconNode, 'car'); - * - * @element iron-iconset-svg - * @demo demo/index.html - */ - Polymer({ - - is: 'iron-iconset-svg', - - properties: { - - /** - * The name of the iconset. - * - * @attribute name - * @type string - */ - name: { - type: String, - observer: '_nameChanged' - }, - - /** - * The size of an individual icon. Note that icons must be square. - * - * @attribute iconSize - * @type number - * @default 24 - */ - size: { - type: Number, - value: 24 - } - - }, - - /** - * Construct an array of all icon names in this iconset. - * - * @return {!Array} Array of icon names. - */ - getIconNames: function() { - this._icons = this._createIconMap(); - return Object.keys(this._icons).map(function(n) { - return this.name + ':' + n; - }, this); - }, - - /** - * Applies an icon to the given element. - * - * An svg icon is prepended to the element's shadowRoot if it exists, - * otherwise to the element itself. - * - * @method applyIcon - * @param {Element} element Element to which the icon is applied. - * @param {string} iconName Name of the icon to apply. - * @return {Element} The svg element which renders the icon. - */ - applyIcon: function(element, iconName) { - // insert svg element into shadow root, if it exists - element = element.root || element; - // Remove old svg element - this.removeIcon(element); - // install new svg element - var svg = this._cloneIcon(iconName); - if (svg) { - var pde = Polymer.dom(element); - pde.insertBefore(svg, pde.childNodes[0]); - return element._svgIcon = svg; - } - return null; - }, - - /** - * Remove an icon from the given element by undoing the changes effected - * by `applyIcon`. - * - * @param {Element} element The element from which the icon is removed. - */ - removeIcon: function(element) { - // Remove old svg element - if (element._svgIcon) { - Polymer.dom(element).removeChild(element._svgIcon); - element._svgIcon = null; - } - }, - - /** - * - * When name is changed, register iconset metadata - * - */ - _nameChanged: function() { - new Polymer.IronMeta({type: 'iconset', key: this.name, value: this}); - this.async(function() { - this.fire('iron-iconset-added', this, {node: window}); - }); - }, - - /** - * Create a map of child SVG elements by id. - * - * @return {!Object} Map of id's to SVG elements. - */ - _createIconMap: function() { - // Objects chained to Object.prototype (`{}`) have members. Specifically, - // on FF there is a `watch` method that confuses the icon map, so we - // need to use a null-based object here. - var icons = Object.create(null); - Polymer.dom(this).querySelectorAll('[id]') - .forEach(function(icon) { - icons[icon.id] = icon; - }); - return icons; - }, - - /** - * Produce installable clone of the SVG element matching `id` in this - * iconset, or `undefined` if there is no matching element. - * - * @return {Element} Returns an installable clone of the SVG element - * matching `id`. - */ - _cloneIcon: function(id) { - // create the icon map on-demand, since the iconset itself has no discrete - // signal to know when it's children are fully parsed - this._icons = this._icons || this._createIconMap(); - return this._prepareSvgClone(this._icons[id], this.size); - }, - - /** - * @param {Element} sourceSvg - * @param {number} size - * @return {Element} - */ - _prepareSvgClone: function(sourceSvg, size) { - if (sourceSvg) { - var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - svg.setAttribute('viewBox', ['0', '0', size, size].join(' ')); - svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); - // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136 - // TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root - svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;'; - svg.appendChild(sourceSvg.cloneNode(true)).removeAttribute('id'); - return svg; - } - return null; - } - - }); Polymer({ is: 'paper-item', @@ -11642,7 +13113,7 @@ * @type {object} * @default {template: 1} */ - excludedLocalNames: { + _excludedLocalNames: { type: Object, value: function() { return { @@ -11659,6 +13130,9 @@ created: function() { this._bindFilterItem = this._filterItem.bind(this); this._selection = new Polymer.IronSelection(this._applySelection.bind(this)); + // TODO(cdata): When polymer/polymer#2535 lands, we do not need to do this + // book keeping anymore: + this.__listeningForActivate = false; }, attached: function() { @@ -11667,6 +13141,7 @@ if (!this.selectedItem && this.selected) { this._updateSelected(this.attrForSelected,this.selected) } + this._addListener(this.activateEvent); }, detached: function() { @@ -11733,11 +13208,17 @@ }, _addListener: function(eventName) { + if (!this.isAttached || this.__listeningForActivate) { + return; + } + + this.__listeningForActivate = true; this.listen(this, eventName, '_activateHandler'); }, _removeListener: function(eventName) { this.unlisten(this, eventName, '_activateHandler'); + this.__listeningForActivate = false; }, _activateEventChanged: function(eventName, old) { @@ -11754,7 +13235,7 @@ }, _filterItem: function(node) { - return !this.excludedLocalNames[node.localName]; + return !this._excludedLocalNames[node.localName]; }, _valueToItem: function(value) { @@ -12265,184 +13746,6 @@ })(); /** - * `IronResizableBehavior` is a behavior that can be used in Polymer elements to - * coordinate the flow of resize events between "resizers" (elements that control the - * size or hidden state of their children) and "resizables" (elements that need to be - * notified when they are resized or un-hidden by their parents in order to take - * action on their new measurements). - * Elements that perform measurement should add the `IronResizableBehavior` behavior to - * their element definition and listen for the `iron-resize` event on themselves. - * This event will be fired when they become showing after having been hidden, - * when they are resized explicitly by another resizable, or when the window has been - * resized. - * Note, the `iron-resize` event is non-bubbling. - * - * @polymerBehavior Polymer.IronResizableBehavior - * @demo demo/index.html - **/ - Polymer.IronResizableBehavior = { - properties: { - /** - * The closest ancestor element that implements `IronResizableBehavior`. - */ - _parentResizable: { - type: Object, - observer: '_parentResizableChanged' - }, - - /** - * True if this element is currently notifying its descedant elements of - * resize. - */ - _notifyingDescendant: { - type: Boolean, - value: false - } - }, - - listeners: { - 'iron-request-resize-notifications': '_onIronRequestResizeNotifications' - }, - - created: function() { - // We don't really need property effects on these, and also we want them - // to be created before the `_parentResizable` observer fires: - this._interestedResizables = []; - this._boundNotifyResize = this.notifyResize.bind(this); - }, - - attached: function() { - this.fire('iron-request-resize-notifications', null, { - node: this, - bubbles: true, - cancelable: true - }); - - if (!this._parentResizable) { - window.addEventListener('resize', this._boundNotifyResize); - this.notifyResize(); - } - }, - - detached: function() { - if (this._parentResizable) { - this._parentResizable.stopResizeNotificationsFor(this); - } else { - window.removeEventListener('resize', this._boundNotifyResize); - } - - this._parentResizable = null; - }, - - /** - * Can be called to manually notify a resizable and its descendant - * resizables of a resize change. - */ - notifyResize: function() { - if (!this.isAttached) { - return; - } - - this._interestedResizables.forEach(function(resizable) { - if (this.resizerShouldNotify(resizable)) { - this._notifyDescendant(resizable); - } - }, this); - - this._fireResize(); - }, - - /** - * Used to assign the closest resizable ancestor to this resizable - * if the ancestor detects a request for notifications. - */ - assignParentResizable: function(parentResizable) { - this._parentResizable = parentResizable; - }, - - /** - * Used to remove a resizable descendant from the list of descendants - * that should be notified of a resize change. - */ - stopResizeNotificationsFor: function(target) { - var index = this._interestedResizables.indexOf(target); - - if (index > -1) { - this._interestedResizables.splice(index, 1); - this.unlisten(target, 'iron-resize', '_onDescendantIronResize'); - } - }, - - /** - * This method can be overridden to filter nested elements that should or - * should not be notified by the current element. Return true if an element - * should be notified, or false if it should not be notified. - * - * @param {HTMLElement} element A candidate descendant element that - * implements `IronResizableBehavior`. - * @return {boolean} True if the `element` should be notified of resize. - */ - resizerShouldNotify: function(element) { return true; }, - - _onDescendantIronResize: function(event) { - if (this._notifyingDescendant) { - event.stopPropagation(); - return; - } - - // NOTE(cdata): In ShadowDOM, event retargetting makes echoing of the - // otherwise non-bubbling event "just work." We do it manually here for - // the case where Polymer is not using shadow roots for whatever reason: - if (!Polymer.Settings.useShadow) { - this._fireResize(); - } - }, - - _fireResize: function() { - this.fire('iron-resize', null, { - node: this, - bubbles: false - }); - }, - - _onIronRequestResizeNotifications: function(event) { - var target = event.path ? event.path[0] : event.target; - - if (target === this) { - return; - } - - if (this._interestedResizables.indexOf(target) === -1) { - this._interestedResizables.push(target); - this.listen(target, 'iron-resize', '_onDescendantIronResize'); - } - - target.assignParentResizable(this); - this._notifyDescendant(target); - - event.stopPropagation(); - }, - - _parentResizableChanged: function(parentResizable) { - if (parentResizable) { - window.removeEventListener('resize', this._boundNotifyResize); - } - }, - - _notifyDescendant: function(descendant) { - // NOTE(cdata): In IE10, attached is fired on children first, so it's - // important not to notify them if the parent is not attached yet (or - // else they will get redundantly notified when the parent attaches). - if (!this.isAttached) { - return; - } - - this._notifyingDescendant = true; - descendant.notifyResize(); - this._notifyingDescendant = false; - } - }; -/** Polymer.IronFitBehavior fits an element in another element using `max-height` and `max-width`, and optionally centers it in the window or another element. @@ -12857,7 +14160,8 @@ it will cancel whenever the user taps outside it or presses the escape key. This behavior is configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties. `close()` should be called explicitly by the implementer when the user interacts with a control -in the overlay element. +in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled' +event. Call `preventDefault` on this event to prevent the overlay from closing. ### Positioning @@ -13028,6 +14332,11 @@ * Cancels the overlay. */ cancel: function() { + var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelable: true}); + if (cancelEvent.defaultPrevented) { + return; + } + this.opened = false; this._setCanceled(true); }, @@ -14576,7 +15885,7 @@ /** * `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus. * - * @polymerBehavior Polymer.PaperInkyFocusBehavior + * @polymerBehavior Polymer.PaperInkyFocusBehaviorImpl */ Polymer.PaperInkyFocusBehaviorImpl = { @@ -14585,11 +15894,20 @@ ], _focusedChanged: function(receivedFocusFromKeyboard) { - if (!this.$.ink) { - return; + if (receivedFocusFromKeyboard) { + this.ensureRipple(); } + if (this.hasRipple()) { + this._ripple.holdDown = receivedFocusFromKeyboard; + } + }, - this.$.ink.holdDown = receivedFocusFromKeyboard; + _createRipple: function() { + var ripple = Polymer.PaperRippleBehavior._createRipple(); + ripple.id = 'ink'; + ripple.setAttribute('center', ''); + ripple.classList.add('circle'); + return ripple; } }; @@ -14598,56 +15916,57 @@ Polymer.PaperInkyFocusBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, + Polymer.PaperRippleBehavior, Polymer.PaperInkyFocusBehaviorImpl ]; Polymer({ - is: 'paper-icon-button', + is: 'paper-icon-button', - hostAttributes: { - role: 'button', - tabindex: '0' - }, - - behaviors: [ - Polymer.PaperInkyFocusBehavior - ], - - properties: { - /** - * The URL of an image for the icon. If the src property is specified, - * the icon property should not be. - */ - src: { - type: String + hostAttributes: { + role: 'button', + tabindex: '0' }, - /** - * Specifies the icon name or index in the set of icons available in - * the icon's icon set. If the icon property is specified, - * the src property should not be. - */ - icon: { - type: String + behaviors: [ + Polymer.PaperInkyFocusBehavior + ], + + properties: { + /** + * The URL of an image for the icon. If the src property is specified, + * the icon property should not be. + */ + src: { + type: String + }, + + /** + * Specifies the icon name or index in the set of icons available in + * the icon's icon set. If the icon property is specified, + * the src property should not be. + */ + icon: { + type: String + }, + + /** + * Specifies the alternate text for the button, for accessibility. + */ + alt: { + type: String, + observer: "_altChanged" + } }, - /** - * Specifies the alternate text for the button, for accessibility. - */ - alt: { - type: String, - observer: "_altChanged" - } - }, + _altChanged: function(newValue, oldValue) { + var label = this.getAttribute('aria-label'); - _altChanged: function(newValue, oldValue) { - var label = this.getAttribute('aria-label'); - - // Don't stomp over a user-set aria-label. - if (!label || oldValue == label) { - this.setAttribute('aria-label', newValue); + // Don't stomp over a user-set aria-label. + if (!label || oldValue == label) { + this.setAttribute('aria-label', newValue); + } } - } - }); + }); /** * Use `Polymer.IronValidatableBehavior` to implement an element that validates user input. * @@ -15445,15 +16764,10 @@ type: Boolean, value: false, }, - }, - /** - * @return {number} A guess at how many items could be visible at once. - * @private - */ - guesstimateNumberOfVisibleItems_: function() { - var toolbarHeight = this.$.toolbar.offsetHeight; - return Math.floor((window.innerHeight - toolbarHeight) / 46) + 1; + items_: { + type: Array, + }, }, /** @@ -15493,33 +16807,6 @@ downloads.ActionService.getInstance().search(''); }, - /** @private */ - rebuildFocusGrid_: function() { - var activeElement = this.shadowRoot.activeElement; - - var activeItem; - if (activeElement && activeElement.tagName == 'downloads-item') - activeItem = activeElement; - - var activeControl = activeItem && activeItem.shadowRoot.activeElement; - - /** @private {!cr.ui.FocusGrid} */ - this.focusGrid_ = this.focusGrid_ || new cr.ui.FocusGrid; - this.focusGrid_.destroy(); - - var boundary = this.$['downloads-list']; - - this.items_.forEach(function(item) { - var focusRow = new downloads.FocusRow(item.content, boundary); - this.focusGrid_.addRow(focusRow); - - if (item == activeItem && !cr.ui.FocusRow.isFocusable(activeControl)) - focusRow.getEquivalentElement(activeControl).focus(); - }, this); - - this.focusGrid_.ensureRowActive(); - }, - /** * @return {number} The number of downloads shown on the page. * @private @@ -15534,62 +16821,24 @@ * @private */ updateAll_: function(list) { - var oldIdMap = this.idMap_ || {}; - - /** @private {!Object<!downloads.Item>} */ - this.idMap_ = {}; - - /** @private {!Array<!downloads.Item>} */ - this.items_ = []; - - if (!this.iconLoader_) { - var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1); - /** @private {downloads.ThrottledIconLoader} */ - this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate); - } + /** @private {!Object<number>} */ + this.idToIndex_ = {}; for (var i = 0; i < list.length; ++i) { var data = list[i]; - var id = data.id; - // Re-use old items when possible (saves work, preserves focus). - var item = oldIdMap[id] || new downloads.Item(this.iconLoader_); + this.idToIndex_[data.id] = data.index = i; - this.idMap_[id] = item; // Associated by ID for fast lookup. - this.items_.push(item); // Add to sorted list for order. - - // Render |item| but don't actually add to the DOM yet. |this.items_| - // must be fully created to be able to find the right spot to insert. - item.update(data); - - // Collapse redundant dates. var prev = list[i - 1]; - item.hideDate = !!prev && prev.date_string == data.date_string; - - delete oldIdMap[id]; + data.hideDate = !!prev && prev.date_string == data.date_string; } - // Remove stale, previously rendered items from the DOM. - for (var id in oldIdMap) { - if (oldIdMap[id].parentNode) - oldIdMap[id].parentNode.removeChild(oldIdMap[id]); - delete oldIdMap[id]; - } - - for (var i = 0; i < this.items_.length; ++i) { - var item = this.items_[i]; - if (item.parentNode) // Already in the DOM; skip. - continue; - - var before = null; - // Find the next rendered item after this one, and insert before it. - for (var j = i + 1; !before && j < this.items_.length; ++j) { - if (this.items_[j].parentNode) - before = this.items_[j]; - } - // If |before| is null, |item| will just get added at the end. - this.$['downloads-list'].insertBefore(item, before); - } + // TODO(dbeam): this resets the scroll position, which is a huge bummer. + // Removing something from the bottom of the list should not scroll you + // back to the top. The grand plan is to restructure how the C++ sends the + // JS data so that it only gets updates (rather than the most recent set + // of items). TL;DR - we can't ship with this bug. + this.items_ = list; var hasDownloads = this.size_() > 0; if (!hasDownloads) { @@ -15604,9 +16853,6 @@ this.$.toolbar.downloadsShowing = this.hasDownloads_; this.$.panel.classList.remove('loading'); - - var allReady = this.items_.map(function(i) { return i.readyPromise; }); - Promise.all(allReady).then(this.rebuildFocusGrid_.bind(this)); }, /** @@ -15614,19 +16860,9 @@ * @private */ updateItem_: function(data) { - var item = this.idMap_[data.id]; - - var activeControl = this.shadowRoot.activeElement == item ? - item.shadowRoot.activeElement : null; - - item.update(data); - - this.async(function() { - if (activeControl && !cr.ui.FocusRow.isFocusable(activeControl)) { - var focusRow = this.focusGrid_.getRowForRoot(item.content); - focusRow.getEquivalentElement(activeControl).focus(); - } - }.bind(this)); + var index = this.idToIndex_[data.id]; + this.set('items_.' + index, data); + this.$['downloads-list'].updateSizeForItem(index); }, });
diff --git a/chrome/browser/resources/md_downloads/item.css b/chrome/browser/resources/md_downloads/item.css index 4e668267..9a4576f 100644 --- a/chrome/browser/resources/md_downloads/item.css +++ b/chrome/browser/resources/md_downloads/item.css
@@ -5,14 +5,13 @@ :host { display: flex; flex-direction: column; - margin: 12px 0; } #date { color: #7c7c7c; font-size: 100%; font-weight: 500; - margin: 18px auto 16px; /* This top margin + :host top margin = 30px. */ + margin: 24px auto 10px; width: var(--downloads-item-width); } @@ -21,7 +20,7 @@ border-radius: 2px; display: flex; flex: none; - margin: 0 auto; + margin: 6px auto; min-height: 103px; position: relative; width: var(--downloads-item-width);
diff --git a/chrome/browser/resources/md_downloads/item.html b/chrome/browser/resources/md_downloads/item.html index 56d7e1b..ffe95f5 100644 --- a/chrome/browser/resources/md_downloads/item.html +++ b/chrome/browser/resources/md_downloads/item.html
@@ -8,7 +8,6 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html"> <link rel="import" href="chrome://downloads/action_service.html"> <link rel="import" href="chrome://downloads/constants.html"> -<link rel="import" href="chrome://downloads/throttled_icon_loader.html"> <dom-module id="downloads-item"> <template>
diff --git a/chrome/browser/resources/md_downloads/item.js b/chrome/browser/resources/md_downloads/item.js index fb5ad71..3bce0bf9 100644 --- a/chrome/browser/resources/md_downloads/item.js +++ b/chrome/browser/resources/md_downloads/item.js
@@ -6,14 +6,6 @@ var Item = Polymer({ is: 'downloads-item', - /** - * @param {!downloads.ThrottledIconLoader} iconLoader - */ - factoryImpl: function(iconLoader) { - /** @private {!downloads.ThrottledIconLoader} */ - this.iconLoader_ = iconLoader; - }, - properties: { data: { type: Object, @@ -24,15 +16,6 @@ value: true, }, - readyPromise: { - type: Object, - value: function() { - return new Promise(function(resolve, reject) { - this.resolveReadyPromise_ = resolve; - }.bind(this)); - }, - }, - completelyOnDisk_: { computed: 'computeCompletelyOnDisk_(' + 'data.state, data.file_externally_removed)', @@ -105,21 +88,11 @@ // TODO(dbeam): this gets called way more when I observe data.by_ext_id // and data.by_ext_name directly. Why? 'observeControlledBy_(controlledBy_)', + 'observeIsDangerous_(isDangerous_, data.file_path)', ], ready: function() { this.content = this.$.content; - this.resolveReadyPromise_(); - }, - - /** @param {!downloads.Data} data */ - update: function(data) { - this.data = data; - - if (!this.isDangerous_) { - var icon = 'chrome://fileicon/' + encodeURIComponent(data.file_path); - this.iconLoader_.loadScaledIcon(this.$['file-icon'], icon); - } }, /** @private */ @@ -280,6 +253,14 @@ }, /** @private */ + observeIsDangerous_: function() { + if (this.data && !this.isDangerous_) { + var filePath = encodeURIComponent(this.data.file_path); + this.$['file-icon'].src = 'chrome://fileicon/' + filePath; + } + }, + + /** @private */ onCancelTap_: function() { downloads.ActionService.getInstance().cancel(this.data.id); },
diff --git a/chrome/browser/resources/md_downloads/manager.html b/chrome/browser/resources/md_downloads/manager.html index 46fb6ebc..628f0ea 100644 --- a/chrome/browser/resources/md_downloads/manager.html +++ b/chrome/browser/resources/md_downloads/manager.html
@@ -5,6 +5,7 @@ <link rel="import" href="chrome://resources/html/cr/ui/focus_grid.html"> <link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-header-panel/paper-header-panel.html"> <link rel="import" href="chrome://downloads/action_service.html"> <link rel="import" href="chrome://downloads/constants.html"> @@ -16,8 +17,14 @@ <template> <paper-header-panel id="panel" class="loading"> <downloads-toolbar class="paper-header" id="toolbar"></downloads-toolbar> - <div id="downloads-list" hidden$="[[!hasDownloads_]]"></div> - <div id="no-downloads" hidden$="[[hasDownloads_]]"> + <iron-list id="downloads-list" items="{{items_}}" + hidden="[[!hasDownloads_]]"> + <template> + <downloads-item data="[[item]]" hide-date="[[item.hideDate]]"> + </downloads-item> + </template> + </iron-list> + <div id="no-downloads" hidden="[[hasDownloads_]]"> <div> <div class="illustration"></div> <span><!-- Text populated in Manager#updateAll_(). --></span>
diff --git a/chrome/browser/resources/md_downloads/manager.js b/chrome/browser/resources/md_downloads/manager.js index 9c4c9ff..e4c54e4 100644 --- a/chrome/browser/resources/md_downloads/manager.js +++ b/chrome/browser/resources/md_downloads/manager.js
@@ -11,15 +11,10 @@ type: Boolean, value: false, }, - }, - /** - * @return {number} A guess at how many items could be visible at once. - * @private - */ - guesstimateNumberOfVisibleItems_: function() { - var toolbarHeight = this.$.toolbar.offsetHeight; - return Math.floor((window.innerHeight - toolbarHeight) / 46) + 1; + items_: { + type: Array, + }, }, /** @@ -59,33 +54,6 @@ downloads.ActionService.getInstance().search(''); }, - /** @private */ - rebuildFocusGrid_: function() { - var activeElement = this.shadowRoot.activeElement; - - var activeItem; - if (activeElement && activeElement.tagName == 'downloads-item') - activeItem = activeElement; - - var activeControl = activeItem && activeItem.shadowRoot.activeElement; - - /** @private {!cr.ui.FocusGrid} */ - this.focusGrid_ = this.focusGrid_ || new cr.ui.FocusGrid; - this.focusGrid_.destroy(); - - var boundary = this.$['downloads-list']; - - this.items_.forEach(function(item) { - var focusRow = new downloads.FocusRow(item.content, boundary); - this.focusGrid_.addRow(focusRow); - - if (item == activeItem && !cr.ui.FocusRow.isFocusable(activeControl)) - focusRow.getEquivalentElement(activeControl).focus(); - }, this); - - this.focusGrid_.ensureRowActive(); - }, - /** * @return {number} The number of downloads shown on the page. * @private @@ -100,62 +68,24 @@ * @private */ updateAll_: function(list) { - var oldIdMap = this.idMap_ || {}; - - /** @private {!Object<!downloads.Item>} */ - this.idMap_ = {}; - - /** @private {!Array<!downloads.Item>} */ - this.items_ = []; - - if (!this.iconLoader_) { - var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1); - /** @private {downloads.ThrottledIconLoader} */ - this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate); - } + /** @private {!Object<number>} */ + this.idToIndex_ = {}; for (var i = 0; i < list.length; ++i) { var data = list[i]; - var id = data.id; - // Re-use old items when possible (saves work, preserves focus). - var item = oldIdMap[id] || new downloads.Item(this.iconLoader_); + this.idToIndex_[data.id] = data.index = i; - this.idMap_[id] = item; // Associated by ID for fast lookup. - this.items_.push(item); // Add to sorted list for order. - - // Render |item| but don't actually add to the DOM yet. |this.items_| - // must be fully created to be able to find the right spot to insert. - item.update(data); - - // Collapse redundant dates. var prev = list[i - 1]; - item.hideDate = !!prev && prev.date_string == data.date_string; - - delete oldIdMap[id]; + data.hideDate = !!prev && prev.date_string == data.date_string; } - // Remove stale, previously rendered items from the DOM. - for (var id in oldIdMap) { - if (oldIdMap[id].parentNode) - oldIdMap[id].parentNode.removeChild(oldIdMap[id]); - delete oldIdMap[id]; - } - - for (var i = 0; i < this.items_.length; ++i) { - var item = this.items_[i]; - if (item.parentNode) // Already in the DOM; skip. - continue; - - var before = null; - // Find the next rendered item after this one, and insert before it. - for (var j = i + 1; !before && j < this.items_.length; ++j) { - if (this.items_[j].parentNode) - before = this.items_[j]; - } - // If |before| is null, |item| will just get added at the end. - this.$['downloads-list'].insertBefore(item, before); - } + // TODO(dbeam): this resets the scroll position, which is a huge bummer. + // Removing something from the bottom of the list should not scroll you + // back to the top. The grand plan is to restructure how the C++ sends the + // JS data so that it only gets updates (rather than the most recent set + // of items). TL;DR - we can't ship with this bug. + this.items_ = list; var hasDownloads = this.size_() > 0; if (!hasDownloads) { @@ -170,9 +100,6 @@ this.$.toolbar.downloadsShowing = this.hasDownloads_; this.$.panel.classList.remove('loading'); - - var allReady = this.items_.map(function(i) { return i.readyPromise; }); - Promise.all(allReady).then(this.rebuildFocusGrid_.bind(this)); }, /** @@ -180,19 +107,9 @@ * @private */ updateItem_: function(data) { - var item = this.idMap_[data.id]; - - var activeControl = this.shadowRoot.activeElement == item ? - item.shadowRoot.activeElement : null; - - item.update(data); - - this.async(function() { - if (activeControl && !cr.ui.FocusRow.isFocusable(activeControl)) { - var focusRow = this.focusGrid_.getRowForRoot(item.content); - focusRow.getEquivalentElement(activeControl).focus(); - } - }.bind(this)); + var index = this.idToIndex_[data.id]; + this.set('items_.' + index, data); + this.$['downloads-list'].updateSizeForItem(index); }, });
diff --git a/chrome/browser/resources/md_downloads/vulcanized.html b/chrome/browser/resources/md_downloads/vulcanized.html index 37e4737..fff21ea 100644 --- a/chrome/browser/resources/md_downloads/vulcanized.html +++ b/chrome/browser/resources/md_downloads/vulcanized.html
@@ -1632,7 +1632,46 @@ </style> <script src="chrome://downloads/strings.js"></script> </head> -<body><div hidden="" by-vulcanize=""><dom-module id="paper-header-panel" assetpath="chrome://resources/polymer/v1_0/paper-header-panel/"> +<body><div hidden="" by-vulcanize=""><dom-module id="iron-list" assetpath="chrome://resources/polymer/v1_0/iron-list/"> + <template> + <style> + :host { + display: block; + } + + :host(.has-scroller) { + overflow: auto; + } + + :host(:not(.has-scroller)) { + position: relative; + } + + #items { + @apply(--iron-list-items-container); + position: relative; + } + + #items > ::content > * { + width: 100%; + box-sizing: border-box; + position: absolute; + top: 0; + will-change: transform; + } + </style> + + <array-selector id="selector" items="{{items}}" selected="{{selectedItems}}" selected-item="{{selectedItem}}"> + </array-selector> + + <div id="items"> + <content></content> + </div> + + </template> +</dom-module> + +<dom-module id="paper-header-panel" assetpath="chrome://resources/polymer/v1_0/paper-header-panel/"> <style> /** @@ -1770,674 +1809,6 @@ </dom-module> -<dom-module id="paper-material" assetpath="chrome://resources/polymer/v1_0/paper-material/"> - <style> - :host { - display: block; - position: relative; - } - - :host([animated]) { - @apply(--shadow-transition); - } - - :host([elevation="1"]) { - @apply(--shadow-elevation-2dp); - } - - :host([elevation="2"]) { - @apply(--shadow-elevation-4dp); - } - - :host([elevation="3"]) { - @apply(--shadow-elevation-6dp); - } - - :host([elevation="4"]) { - @apply(--shadow-elevation-8dp); - } - - :host([elevation="5"]) { - @apply(--shadow-elevation-16dp); - } - </style> - <template> - <content></content> - </template> -</dom-module> -<dom-module id="paper-ripple" assetpath="chrome://resources/polymer/v1_0/paper-ripple/"> - - - - <template> - <style> - :host { - display: block; - position: absolute; - border-radius: inherit; - overflow: hidden; - top: 0; - left: 0; - right: 0; - bottom: 0; - } - - :host([animating]) { - /* This resolves a rendering issue in Chrome (as of 40) where the - ripple is not properly clipped by its parent (which may have - rounded corners). See: http://jsbin.com/temexa/4 - - Note: We only apply this style conditionally. Otherwise, the browser - will create a new compositing layer for every ripple element on the - page, and that would be bad. */ - -webkit-transform: translate(0, 0); - transform: translate3d(0, 0, 0); - } - - :host([noink]) { - pointer-events: none; - } - - #background, - #waves, - .wave-container, - .wave { - pointer-events: none; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - } - - #background, - .wave { - opacity: 0; - } - - #waves, - .wave { - overflow: hidden; - } - - .wave-container, - .wave { - border-radius: 50%; - } - - :host(.circle) #background, - :host(.circle) #waves { - border-radius: 50%; - } - - :host(.circle) .wave-container { - overflow: hidden; - } - </style> - - <div id="background"></div> - <div id="waves"></div> - </template> -</dom-module> -<dom-module id="paper-button" assetpath="chrome://resources/polymer/v1_0/paper-button/"> - <template> - - <style> - :host { - display: inline-block; - position: relative; - box-sizing: border-box; - min-width: 5.14em; - margin: 0 0.29em; - background: transparent; - text-align: center; - font: inherit; - text-transform: uppercase; - outline-width: 0; - border-radius: 3px; - -moz-user-select: none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - cursor: pointer; - z-index: 0; - padding: 0.7em 0.57em; - - @apply(--paper-button); - } - - :host([raised]) .keyboard-focus { - font-weight: bold; - @apply(--paper-button-raised-keyboard-focus); - } - - :host(:not([raised])) .keyboard-focus { - font-weight: bold; - @apply(--paper-button-flat-keyboard-focus); - } - - :host([disabled]) { - background: #eaeaea; - color: #a8a8a8; - cursor: auto; - pointer-events: none; - - @apply(--paper-button-disabled); - } - - :host([noink]) paper-ripple { - display: none; - } - - paper-ripple { - color: var(--paper-button-ink-color); - } - - paper-material { - border-radius: inherit; - @apply(--layout-fit); - } - - .content > ::content * { - text-transform: inherit; - } - </style> - - <paper-ripple></paper-ripple> - - <paper-material elevation="[[_elevation]]" animated=""></paper-material> - - <div class$="[[_computeContentClass(receivedFocusFromKeyboard)]]"> - <content></content> - </div> - - </template> -</dom-module> - -<dom-module id="paper-progress" assetpath="chrome://resources/polymer/v1_0/paper-progress/"> - <style> - :host { - display: block; - width: 200px; - position: relative; - overflow: hidden; - } - - #progressContainer { - position: relative; - } - - #progressContainer, - /* the stripe for the indeterminate animation*/ - .indeterminate:after { - height: var(--paper-progress-height, 4px); - } - - #primaryProgress, - #secondaryProgress, - .indeterminate:after { - @apply(--layout-fit); - } - - #progressContainer, - .indeterminate:after { - background-color: var(--paper-progress-container-color, --google-grey-300); - } - - :host(.transiting) #primaryProgress, - :host(.transiting) #secondaryProgress { - -webkit-transition-property: -webkit-transform; - transition-property: transform; - - /* Duration */ - -webkit-transition-duration: var(--paper-progress-transition-duration, 0.08s); - transition-duration: var(--paper-progress-transition-duration, 0.08s); - - /* Timing function */ - -webkit-transition-timing-function: var(--paper-progress-transition-timing-function, ease); - transition-timing-function: var(--paper-progress-transition-timing-function, ease); - - /* Delay */ - -webkit-transition-delay: var(--paper-progress-transition-delay, 0s); - transition-delay: var(--paper-progress-transition-delay, 0s); - } - - #primaryProgress, - #secondaryProgress { - @apply(--layout-fit); - -webkit-transform-origin: left center; - transform-origin: left center; - -webkit-transform: scaleX(0); - transform: scaleX(0); - will-change: transform; - } - - #primaryProgress { - background-color: var(--paper-progress-active-color, --google-green-500); - } - - #secondaryProgress { - position: relative; - background-color: var(--paper-progress-secondary-color, --google-green-100); - } - - :host([disabled]) #primaryProgress { - background-color: var(--paper-progress-disabled-active-color, --google-grey-500); - } - - :host([disabled]) #secondaryProgress { - background-color: var(--paper-progress-disabled-active-color, --google-grey-300); - } - - :host(:not([disabled])) #primaryProgress.indeterminate { - -webkit-transform-origin: right center; - transform-origin: right center; - -webkit-animation: indeterminate-bar 2s linear infinite; - animation: indeterminate-bar 2s linear infinite; - } - - :host(:not([disabled])) #primaryProgress.indeterminate:after { - content: ""; - -webkit-transform-origin: center center; - transform-origin: center center; - - -webkit-animation: indeterminate-splitter 2s linear infinite; - animation: indeterminate-splitter 2s linear infinite; - } - - @-webkit-keyframes indeterminate-bar { - 0% { - -webkit-transform: scaleX(1) translateX(-100%); - } - 50% { - -webkit-transform: scaleX(1) translateX(0%); - } - 75% { - -webkit-transform: scaleX(1) translateX(0%); - -webkit-animation-timing-function: cubic-bezier(.28,.62,.37,.91); - } - 100% { - -webkit-transform: scaleX(0) translateX(0%); - } - } - - @-webkit-keyframes indeterminate-splitter { - 0% { - -webkit-transform: scaleX(.75) translateX(-125%); - } - 30% { - -webkit-transform: scaleX(.75) translateX(-125%); - -webkit-animation-timing-function: cubic-bezier(.42,0,.6,.8); - } - 90% { - -webkit-transform: scaleX(.75) translateX(125%); - } - 100% { - -webkit-transform: scaleX(.75) translateX(125%); - } - } - - @keyframes indeterminate-bar { - 0% { - transform: scaleX(1) translateX(-100%); - } - 50% { - transform: scaleX(1) translateX(0%); - } - 75% { - transform: scaleX(1) translateX(0%); - animation-timing-function: cubic-bezier(.28,.62,.37,.91); - } - 100% { - transform: scaleX(0) translateX(0%); - } - } - - @keyframes indeterminate-splitter { - 0% { - transform: scaleX(.75) translateX(-125%); - } - 30% { - transform: scaleX(.75) translateX(-125%); - animation-timing-function: cubic-bezier(.42,0,.6,.8); - } - 90% { - transform: scaleX(.75) translateX(125%); - } - 100% { - transform: scaleX(.75) translateX(125%); - } - } - </style> - <template> - <div id="progressContainer"> - <div id="secondaryProgress" hidden$="[[_hideSecondaryProgress(secondaryRatio)]]"></div> - <div id="primaryProgress"></div> - </div> - </template> -</dom-module> - -<dom-module id="downloads-item" assetpath="chrome://downloads/"> - <template> - <template is="dom-if" if="[[!hideDate]]"> - <h3 id="date">[[computeDate_(data.since_string, data.date_string)]]</h3> - </template> - - <div id="content" on-dragstart="onDragStart_" class$="[[computeClass_(isActive_, isDangerous_, showProgress_)]]"> - <div id="file-icon-wrapper" class="icon-wrapper"> - <img class="icon" id="file-icon" alt="" hidden="[[isDangerous_]]"> - <div class="icon" id="warning" hidden="[[!isDangerous_]]"></div> - </div> - - <div id="details"> - <div id="title-area"><a is="action-link" id="file-link" href="[[data.url]]" on-tap="onFileLinkTap_" hidden="[[!completelyOnDisk_]]">[[data.file_name]]</a><span id="name" hidden="[[completelyOnDisk_]]">[[data.file_name]]</span> - <span id="tag">[[computeTag_(data.state, data.last_reason_text, data.file_externally_removed)]]</span> - </div> - - <a id="url" target="_blank" href="[[data.url]]">[[data.url]]</a> - - <div id="description">[[computeDescription_(data.state, data.danger_type, data.file_name, data.progress_status_text)]]</div> - - <template is="dom-if" if="[[showProgress_]]"> - <paper-progress id="progress" indeterminate="[[isIndeterminate_(data.percent)]]" value="[[data.percent]]"></paper-progress> - </template> - - <div id="safe" class="controls" hidden="[[isDangerous_]]"> - <a is="action-link" id="show" i18n-content="controlShowInFolder" on-tap="onShowTap_" hidden="[[!completelyOnDisk_]]"></a> - <template is="dom-if" if="[[data.retry]]"> - <paper-button id="retry" on-tap="onRetryTap_">[[i18n_.retry]]</paper-button> - </template> - <template is="dom-if" if="[[isInProgress_]]"> - <paper-button id="pause" on-tap="onPauseTap_">[[i18n_.pause]]</paper-button> - </template> - <template is="dom-if" if="[[data.resume]]"> - <paper-button id="resume" on-tap="onResumeTap_">[[i18n_.resume]]</paper-button> - </template> - <template is="dom-if" if="[[showCancel_]]"> - <paper-button id="cancel" on-tap="onCancelTap_">[[i18n_.cancel]]</paper-button> - </template> - <span id="controlled-by"></span> - </div> - - <template is="dom-if" if="[[isDangerous_]]"> - <div id="dangerous" class="controls"> - - <template is="dom-if" if="[[!isMalware_]]"> - <paper-button id="discard" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.discard]]</paper-button> - <paper-button id="save" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.save]]</paper-button> - </template> - - - <template is="dom-if" if="[[isMalware_]]"> - <paper-button id="danger-remove" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.remove]]</paper-button> - <paper-button id="restore" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.restore]</paper-button> - </template> - </div> - </template> - </div> - - <div id="remove-wrapper" class="icon-wrapper"> - <paper-icon-button id="remove" icon="clear" i18n-values="title:controlRemoveFromList" style$="[[computeRemoveStyle_(isDangerous_, showCancel_)]]" on-tap="onRemoveTap_"></paper-icon-button> - </div> - - <div id="incognito" i18n-values="title:inIncognito" hidden="[[!data.otr]]"></div> - </div> - - </template> - <style> -/* 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. */ - -[is='action-link'] { - cursor: pointer; - display: inline-block; - text-decoration: none; -} - -[is='action-link']:hover { - text-decoration: underline; -} - -[is='action-link']:active { - color: rgb(5, 37, 119); - text-decoration: underline; -} - -[is='action-link'][disabled] { - color: #999; - cursor: default; - pointer-events: none; - text-decoration: none; -} - -[is='action-link'].no-outline { - outline: none; -} - -</style> - <style> -/* 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. */ - -* { - --downloads-item-width: 622px; -} - -paper-button { - font-weight: 500; - margin: 0; - min-width: auto; -} - -</style> - <style> -/* 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. */ - -:host { - display: flex; - flex-direction: column; - margin: 12px 0; -} - -#date { - color: #7c7c7c; - font-size: 100%; - font-weight: 500; - margin: 18px auto 16px; /* This top margin + :host top margin = 30px. */ - width: var(--downloads-item-width); -} - -#content { - background: white; - border-radius: 2px; - display: flex; - flex: none; - margin: 0 auto; - min-height: 103px; - position: relative; - width: var(--downloads-item-width); -} - -#content.is-active { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .05), 0 1px 4px 0 rgba(0, 0, 0, .08), - 0 3px 1px -2px rgba(0, 0, 0, .2); -} - -#content:not(.is-active) { - opacity: .6; -} - -#details { - -webkit-border-start: 1px #d8d8d8 solid; - -webkit-padding-end: 16px; - -webkit-padding-start: 24px; - display: flex; - flex: 1; - flex-direction: column; - padding-bottom: 12px; - padding-top: 16px; -} - -#content:not(.is-active) #details { - color: #bababa; -} - -#content:not(.is-active) #name { - text-decoration: line-through; -} - -.icon-wrapper { - align-self: center; - flex: none; - justify-content: center; - margin: 0 24px; -} - -.icon { - height: 32px; - width: 32px; -} - -#content:-webkit-any(.show-progress, .dangerous) #file-icon-wrapper { - /* TODO(dbeam): animate from top-aligned to centered when items finish? */ - align-self: flex-start; - padding-top: 16px; -} - -#content:not(.is-active) .icon { - -webkit-filter: grayscale(100%); - opacity: .5; -} - -#warning { - -webkit-mask-image: url("chrome://resources/images/warning.svg"); - -webkit-mask-repeat: no-repeat; - -webkit-mask-size: 100%; - background: rgb(255, 193, 7); -} - -#name, -#file-link, -#url { - max-width: 100%; -} - -#name, -#file-link { - font-weight: 500; - word-break: break-all; -} - -#name { - -webkit-margin-end: 12px; /* Only really affects #tag. */ -} - -.is-active :-webkit-any(#name, #file-link, #pause, #resume, #show) { - color: rgb(51, 103, 214); -} - -#tag { - color: #5a5a5a; - font-weight: 500; -} - -#url { - color: inherit; - margin-top: 6px; - overflow: hidden; - text-decoration: none; - text-overflow: ellipsis; - white-space: nowrap; -} - -.is-active #url { - color: #969696; -} - -#progress, -#description:not(:empty), -.controls { - margin-top: 16px; -} - -.is-active #description { - color: #616161; -} - -.dangerous #description { - color: rgb(239, 108, 0); -} - -#progress { - --paper-progress-active-color: rgb(54, 126, 237); - --paper-progress-container-color: rgb(223, 222, 223); - width: auto; -} - -.controls { - -webkit-margin-start: -.57em; -} - -#cancel, -#retry, -.keep, -.discard { - color: #5a5a5a; -} - -#show { - margin: .7em .57em; -} - -#controlled-by { - -webkit-margin-start: 8px; -} - -#controlled-by, -#controlled-by a { - color: #5a5a5a; -} - -.is-active #controlled-by { - color: #333; -} - -.is-active #controlled-by a { - color: rgb(51, 103, 214); -} - -#remove-wrapper { - align-self: flex-start; - margin: 0; -} - -#remove { - --iron-icon-height: 16px; - --iron-icon-width: 16px; - --layout-inline: { - /* HACK(dbeam): we probably shouldn't be overriding Polymer like this. */ - }; - color: #969696; - height: 16px; - padding: 8px; - width: 16px; -} - -#incognito { - bottom: 20px; - content: -webkit-image-set( - url("chrome://downloads/1x/incognito_marker.png") 1x, - url("chrome://downloads/2x/incognito_marker.png") 2x); - position: absolute; - right: 10px; -} - -</style> - </dom-module> <dom-module id="iron-icon" assetpath="chrome://resources/polymer/v1_0/iron-icon/"> <style> @@ -2727,6 +2098,662 @@ <g id="zoom-out"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7z"></path></g> </defs></svg> </iron-iconset-svg> +<dom-module id="paper-material" assetpath="chrome://resources/polymer/v1_0/paper-material/"> + <style> + :host { + display: block; + position: relative; + } + + :host([animated]) { + @apply(--shadow-transition); + } + + :host([elevation="1"]) { + @apply(--shadow-elevation-2dp); + } + + :host([elevation="2"]) { + @apply(--shadow-elevation-4dp); + } + + :host([elevation="3"]) { + @apply(--shadow-elevation-6dp); + } + + :host([elevation="4"]) { + @apply(--shadow-elevation-8dp); + } + + :host([elevation="5"]) { + @apply(--shadow-elevation-16dp); + } + </style> + <template> + <content></content> + </template> +</dom-module> +<dom-module id="paper-ripple" assetpath="chrome://resources/polymer/v1_0/paper-ripple/"> + + + + <template> + <style> + :host { + display: block; + position: absolute; + border-radius: inherit; + overflow: hidden; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + + :host([animating]) { + /* This resolves a rendering issue in Chrome (as of 40) where the + ripple is not properly clipped by its parent (which may have + rounded corners). See: http://jsbin.com/temexa/4 + + Note: We only apply this style conditionally. Otherwise, the browser + will create a new compositing layer for every ripple element on the + page, and that would be bad. */ + -webkit-transform: translate(0, 0); + transform: translate3d(0, 0, 0); + } + + :host([noink]) { + pointer-events: none; + } + + #background, + #waves, + .wave-container, + .wave { + pointer-events: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + + #background, + .wave { + opacity: 0; + } + + #waves, + .wave { + overflow: hidden; + } + + .wave-container, + .wave { + border-radius: 50%; + } + + :host(.circle) #background, + :host(.circle) #waves { + border-radius: 50%; + } + + :host(.circle) .wave-container { + overflow: hidden; + } + </style> + + <div id="background"></div> + <div id="waves"></div> + </template> +</dom-module> +<dom-module id="paper-button" assetpath="chrome://resources/polymer/v1_0/paper-button/"> + <template strip-whitespace=""> + + <style include="paper-material"> + :host { + display: inline-block; + position: relative; + box-sizing: border-box; + min-width: 5.14em; + margin: 0 0.29em; + background: transparent; + text-align: center; + font: inherit; + text-transform: uppercase; + outline-width: 0; + border-radius: 3px; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + cursor: pointer; + z-index: 0; + padding: 0.7em 0.57em; + + @apply(--paper-button); + } + + :host([raised].keyboard-focus) { + font-weight: bold; + @apply(--paper-button-raised-keyboard-focus); + } + + :host(:not([raised]).keyboard-focus) { + font-weight: bold; + @apply(--paper-button-flat-keyboard-focus); + } + + :host([disabled]) { + background: #eaeaea; + color: #a8a8a8; + cursor: auto; + pointer-events: none; + + @apply(--paper-button-disabled); + } + + paper-ripple { + color: var(--paper-button-ink-color); + } + + :host > ::content * { + text-transform: inherit; + } + </style> + <content></content> + </template> +</dom-module> + +<dom-module id="paper-progress" assetpath="chrome://resources/polymer/v1_0/paper-progress/"> + <style> + :host { + display: block; + width: 200px; + position: relative; + overflow: hidden; + } + + #progressContainer { + position: relative; + } + + #progressContainer, + /* the stripe for the indeterminate animation*/ + .indeterminate:after { + height: var(--paper-progress-height, 4px); + } + + #primaryProgress, + #secondaryProgress, + .indeterminate:after { + @apply(--layout-fit); + } + + #progressContainer, + .indeterminate:after { + background-color: var(--paper-progress-container-color, --google-grey-300); + } + + :host(.transiting) #primaryProgress, + :host(.transiting) #secondaryProgress { + -webkit-transition-property: -webkit-transform; + transition-property: transform; + + /* Duration */ + -webkit-transition-duration: var(--paper-progress-transition-duration, 0.08s); + transition-duration: var(--paper-progress-transition-duration, 0.08s); + + /* Timing function */ + -webkit-transition-timing-function: var(--paper-progress-transition-timing-function, ease); + transition-timing-function: var(--paper-progress-transition-timing-function, ease); + + /* Delay */ + -webkit-transition-delay: var(--paper-progress-transition-delay, 0s); + transition-delay: var(--paper-progress-transition-delay, 0s); + } + + #primaryProgress, + #secondaryProgress { + @apply(--layout-fit); + -webkit-transform-origin: left center; + transform-origin: left center; + -webkit-transform: scaleX(0); + transform: scaleX(0); + will-change: transform; + } + + #primaryProgress { + background-color: var(--paper-progress-active-color, --google-green-500); + } + + #secondaryProgress { + position: relative; + background-color: var(--paper-progress-secondary-color, --google-green-100); + } + + :host([disabled]) #primaryProgress { + background-color: var(--paper-progress-disabled-active-color, --google-grey-500); + } + + :host([disabled]) #secondaryProgress { + background-color: var(--paper-progress-disabled-active-color, --google-grey-300); + } + + :host(:not([disabled])) #primaryProgress.indeterminate { + -webkit-transform-origin: right center; + transform-origin: right center; + -webkit-animation: indeterminate-bar 2s linear infinite; + animation: indeterminate-bar 2s linear infinite; + } + + :host(:not([disabled])) #primaryProgress.indeterminate:after { + content: ""; + -webkit-transform-origin: center center; + transform-origin: center center; + + -webkit-animation: indeterminate-splitter 2s linear infinite; + animation: indeterminate-splitter 2s linear infinite; + } + + @-webkit-keyframes indeterminate-bar { + 0% { + -webkit-transform: scaleX(1) translateX(-100%); + } + 50% { + -webkit-transform: scaleX(1) translateX(0%); + } + 75% { + -webkit-transform: scaleX(1) translateX(0%); + -webkit-animation-timing-function: cubic-bezier(.28,.62,.37,.91); + } + 100% { + -webkit-transform: scaleX(0) translateX(0%); + } + } + + @-webkit-keyframes indeterminate-splitter { + 0% { + -webkit-transform: scaleX(.75) translateX(-125%); + } + 30% { + -webkit-transform: scaleX(.75) translateX(-125%); + -webkit-animation-timing-function: cubic-bezier(.42,0,.6,.8); + } + 90% { + -webkit-transform: scaleX(.75) translateX(125%); + } + 100% { + -webkit-transform: scaleX(.75) translateX(125%); + } + } + + @keyframes indeterminate-bar { + 0% { + transform: scaleX(1) translateX(-100%); + } + 50% { + transform: scaleX(1) translateX(0%); + } + 75% { + transform: scaleX(1) translateX(0%); + animation-timing-function: cubic-bezier(.28,.62,.37,.91); + } + 100% { + transform: scaleX(0) translateX(0%); + } + } + + @keyframes indeterminate-splitter { + 0% { + transform: scaleX(.75) translateX(-125%); + } + 30% { + transform: scaleX(.75) translateX(-125%); + animation-timing-function: cubic-bezier(.42,0,.6,.8); + } + 90% { + transform: scaleX(.75) translateX(125%); + } + 100% { + transform: scaleX(.75) translateX(125%); + } + } + </style> + <template> + <div id="progressContainer"> + <div id="secondaryProgress" hidden$="[[_hideSecondaryProgress(secondaryRatio)]]"></div> + <div id="primaryProgress"></div> + </div> + </template> +</dom-module> + +<dom-module id="downloads-item" assetpath="chrome://downloads/"> + <template> + <template is="dom-if" if="[[!hideDate]]"> + <h3 id="date">[[computeDate_(hideDate, data.since_string, data.date_string)]]</h3> + </template> + + <div id="content" on-dragstart="onDragStart_" class$="[[computeClass_(isActive_, isDangerous_, showProgress_)]]"> + <div id="file-icon-wrapper" class="icon-wrapper"> + <img class="icon" id="file-icon" alt="" hidden="[[isDangerous_]]"> + <iron-icon id="danger-icon" icon$="[[computeDangerIcon_(isDangerous_, data.danger_type)]]" hidden="[[!isDangerous_]]"></iron-icon> + </div> + + <div id="details"> + <div id="title-area"><a is="action-link" id="file-link" href="[[data.url]]" on-tap="onFileLinkTap_" hidden="[[!completelyOnDisk_]]">[[data.file_name]]</a><span id="name" hidden="[[completelyOnDisk_]]">[[data.file_name]]</span> + <span id="tag">[[computeTag_(data.state, data.last_reason_text, data.file_externally_removed)]]</span> + </div> + + <a id="url" target="_blank" href="[[data.url]]">[[data.url]]</a> + + <div id="description">[[computeDescription_(data.state, data.danger_type, data.file_name, data.progress_status_text)]]</div> + + <template is="dom-if" if="[[showProgress_]]"> + <paper-progress id="progress" indeterminate="[[isIndeterminate_(data.percent)]]" value="[[data.percent]]"></paper-progress> + </template> + + <div id="safe" class="controls" hidden="[[isDangerous_]]"> + <a is="action-link" id="show" i18n-content="controlShowInFolder" on-tap="onShowTap_" hidden="[[!completelyOnDisk_]]"></a> + <template is="dom-if" if="[[data.retry]]"> + <paper-button id="retry" on-tap="onRetryTap_">[[i18n_.retry]]</paper-button> + </template> + <template is="dom-if" if="[[isInProgress_]]"> + <paper-button id="pause" on-tap="onPauseTap_">[[i18n_.pause]]</paper-button> + </template> + <template is="dom-if" if="[[data.resume]]"> + <paper-button id="resume" on-tap="onResumeTap_">[[i18n_.resume]]</paper-button> + </template> + <template is="dom-if" if="[[showCancel_]]"> + <paper-button id="cancel" on-tap="onCancelTap_">[[i18n_.cancel]]</paper-button> + </template> + <span id="controlled-by"></span> + </div> + + <template is="dom-if" if="[[isDangerous_]]"> + <div id="dangerous" class="controls"> + + <template is="dom-if" if="[[!isMalware_]]"> + <paper-button id="discard" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.discard]]</paper-button> + <paper-button id="save" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.save]]</paper-button> + </template> + + + <template is="dom-if" if="[[isMalware_]]"> + <paper-button id="danger-remove" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.remove]]</paper-button> + <paper-button id="restore" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.restore]</paper-button> + </template> + </div> + </template> + </div> + + <div id="remove-wrapper" class="icon-wrapper"> + <paper-icon-button id="remove" icon="clear" i18n-values="title:controlRemoveFromList" style$="[[computeRemoveStyle_(isDangerous_, showCancel_)]]" on-tap="onRemoveTap_"></paper-icon-button> + </div> + + <div id="incognito" i18n-values="title:inIncognito" hidden="[[!data.otr]]"></div> + </div> + + </template> + <style> +/* 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. */ + +[is='action-link'] { + cursor: pointer; + display: inline-block; + text-decoration: none; +} + +[is='action-link']:hover { + text-decoration: underline; +} + +[is='action-link']:active { + color: rgb(5, 37, 119); + text-decoration: underline; +} + +[is='action-link'][disabled] { + color: #999; + cursor: default; + pointer-events: none; + text-decoration: none; +} + +[is='action-link'].no-outline { + outline: none; +} + +</style> + <style> +/* 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. */ + +* { + --downloads-item-width: 622px; +} + +paper-button { + font-weight: 500; + margin: 0; + min-width: auto; +} + +</style> + <style> +/* 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. */ + +:host { + display: flex; + flex-direction: column; +} + +#date { + color: #7c7c7c; + font-size: 100%; + font-weight: 500; + margin: 24px auto 10px; + width: var(--downloads-item-width); +} + +#content { + background: white; + border-radius: 2px; + display: flex; + flex: none; + margin: 6px auto; + min-height: 103px; + position: relative; + width: var(--downloads-item-width); +} + +#content.is-active { + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .05), 0 1px 4px 0 rgba(0, 0, 0, .08), + 0 3px 1px -2px rgba(0, 0, 0, .2); +} + +#content:not(.is-active) { + opacity: .6; +} + +#details { + -webkit-border-start: 1px #d8d8d8 solid; + -webkit-padding-end: 16px; + -webkit-padding-start: 24px; + display: flex; + flex: 1; + flex-direction: column; + padding-bottom: 12px; + padding-top: 16px; +} + +#content:not(.is-active) #details { + color: #bababa; +} + +#content:not(.is-active) #name { + text-decoration: line-through; +} + +.icon-wrapper { + align-self: center; + flex: none; + justify-content: center; + margin: 0 24px; +} + +.icon { + height: 32px; + width: 32px; +} + +#content:-webkit-any(.show-progress, .dangerous) #file-icon-wrapper { + /* TODO(dbeam): animate from top-aligned to centered when items finish? */ + align-self: flex-start; + padding-top: 16px; +} + +#content:not(.is-active) .icon { + -webkit-filter: grayscale(100%); + opacity: .5; +} + +#danger-icon { + height: 32px; + width: 32px; +} + +#danger-icon[icon='warning'] { + color: rgb(255, 193, 7); +} + +#danger-icon[icon='remove-circle'] { + color: rgb(244, 67, 54); +} + +#name, +#file-link, +#url { + max-width: 100%; +} + +#name, +#file-link { + font-weight: 500; + word-break: break-all; +} + +#name { + -webkit-margin-end: 12px; /* Only really affects #tag. */ +} + +.is-active :-webkit-any(#name, #file-link, #pause, #resume, #show) { + color: rgb(51, 103, 214); +} + +#tag { + color: #5a5a5a; + font-weight: 500; +} + +#url { + color: inherit; + margin-top: 6px; + overflow: hidden; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; +} + +.is-active #url { + color: #969696; +} + +#progress, +#description:not(:empty), +.controls { + margin-top: 16px; +} + +.is-active #description { + color: #616161; +} + +.dangerous #description { + color: rgb(239, 108, 0); +} + +#progress { + --paper-progress-active-color: rgb(54, 126, 237); + --paper-progress-container-color: rgb(223, 222, 223); + width: auto; +} + +.controls { + -webkit-margin-start: -.57em; +} + +#cancel, +#retry, +.keep, +.discard { + color: #5a5a5a; +} + +#show { + margin: .7em .57em; +} + +#controlled-by { + -webkit-margin-start: 8px; +} + +#controlled-by, +#controlled-by a { + color: #5a5a5a; +} + +.is-active #controlled-by { + color: #333; +} + +.is-active #controlled-by a { + color: rgb(51, 103, 214); +} + +#remove-wrapper { + align-self: flex-start; + margin: 0; +} + +#remove { + --iron-icon-height: 16px; + --iron-icon-width: 16px; + --layout-inline: { + /* HACK(dbeam): we probably shouldn't be overriding Polymer like this. */ + }; + color: #969696; + height: 16px; + padding: 8px; + width: 16px; +} + +#incognito { + bottom: 20px; + content: -webkit-image-set( + url("chrome://downloads/1x/incognito_marker.png") 1x, + url("chrome://downloads/2x/incognito_marker.png") 2x); + position: absolute; + right: 10px; +} + +</style> + </dom-module> <dom-module id="paper-item-shared-styles" assetpath="chrome://resources/polymer/v1_0/paper-item/"> <template> <style> @@ -2927,41 +2954,46 @@ </template> </dom-module> <dom-module id="paper-icon-button" assetpath="chrome://resources/polymer/v1_0/paper-icon-button/"> - <style> + <template strip-whitespace=""> + <style> + :host { + display: inline-block; + position: relative; + padding: 8px; + outline: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + z-index: 0; - :host { - display: inline-block; - position: relative; - padding: 8px; - outline: none; - -webkit-tap-highlight-color: rgba(0,0,0,0); - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - cursor: pointer; - z-index: 0; + width: 24px; + @apply(--paper-icon-button); + } - @apply(--paper-icon-button); - } + :host #ink { + color: var(--paper-icon-button-ink-color, --primary-text-color); + opacity: 0.6; + } - :host #ink { - color: var(--paper-icon-button-ink-color, --primary-text-color); - opacity: 0.6; - } + :host([disabled]) { + color: var(--paper-icon-button-disabled-text, --disabled-text-color); + pointer-events: none; + cursor: auto; + @apply(--paper-icon-button-disabled); + } - :host([disabled]) { - color: var(--paper-icon-button-disabled-text, --disabled-text-color); - pointer-events: none; - cursor: auto; - @apply(--paper-icon-button-disabled); - } - </style> - <template> - <paper-ripple id="ink" class="circle" center=""></paper-ripple> + iron-icon { + --iron-icon-width: 100%; + --iron-icon-height: 100%; + } + </style> <iron-icon id="icon" src="[[src]]" icon="[[icon]]" alt$="[[alt]]"></iron-icon> </template> -</dom-module> + + </dom-module> <dom-module id="paper-input-container" assetpath="chrome://resources/polymer/v1_0/paper-input/"> <template> @@ -3313,6 +3345,10 @@ width: var(--downloads-item-width); } +:host-context(.loading) #actions { + visibility: hidden; +} + :host(:not([downloads-showing])) #actions { justify-content: center; } @@ -3400,8 +3436,13 @@ <template> <paper-header-panel id="panel" class="loading"> <downloads-toolbar class="paper-header" id="toolbar"></downloads-toolbar> - <div id="downloads-list" hidden$="[[!hasDownloads_]]"></div> - <div id="no-downloads" hidden$="[[hasDownloads_]]"> + <iron-list id="downloads-list" items="{{items_}}" hidden="[[!hasDownloads_]]"> + <template> + <downloads-item data="[[item]]" hide-date="[[item.hideDate]]"> + </downloads-item> + </template> + </iron-list> + <div id="no-downloads" hidden="[[hasDownloads_]]"> <div> <div class="illustration"></div> <span></span>
diff --git a/chrome/browser/ui/browser_command_controller_unittest.cc b/chrome/browser/ui/browser_command_controller_unittest.cc index 69533b358..13ad9d4 100644 --- a/chrome/browser/ui/browser_command_controller_unittest.cc +++ b/chrome/browser/ui/browser_command_controller_unittest.cc
@@ -26,6 +26,7 @@ #include "components/signin/core/common/profile_management_switches.h" #include "components/signin/core/common/signin_pref_names.h" #include "content/public/browser/native_web_keyboard_event.h" +#include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/keyboard_codes.h" typedef BrowserWithTestWindowTest BrowserCommandControllerTest; @@ -35,54 +36,59 @@ // F1-3 keys are reserved Chrome accelerators on Chrome OS. EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey( IDC_BACK, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_BROWSER_BACK, 0, 0))); + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK, + ui::DomCode::BROWSER_BACK, 0)))); EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_FORWARD, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_BROWSER_FORWARD, 0, 0))); + IDC_FORWARD, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_FORWARD, + ui::DomCode::BROWSER_FORWARD, 0)))); EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey( IDC_RELOAD, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_BROWSER_REFRESH, 0, 0))); + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_REFRESH, + ui::DomCode::BROWSER_REFRESH, 0)))); // When there are modifier keys pressed, don't reserve. EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_RELOAD_IGNORING_CACHE, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_F3, ui::EF_SHIFT_DOWN, 0))); + IDC_RELOAD_IGNORING_CACHE, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F3, + ui::DomCode::F3, ui::EF_SHIFT_DOWN)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_RELOAD_IGNORING_CACHE, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_F3, ui::EF_CONTROL_DOWN, 0))); + IDC_RELOAD_IGNORING_CACHE, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F3, + ui::DomCode::F3, ui::EF_CONTROL_DOWN)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( IDC_FULLSCREEN, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_F4, ui::EF_SHIFT_DOWN, 0))); + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_F4, + ui::DomCode::F4, ui::EF_SHIFT_DOWN)))); // F4-10 keys are not reserved since they are Ash accelerators. EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F4, 0, 0))); + -1, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F4, ui::DomCode::F4, 0)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F5, 0, 0))); + -1, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F5, ui::DomCode::F5, 0)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F6, 0, 0))); + -1, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F6, ui::DomCode::F6, 0)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F7, 0, 0))); + -1, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F7, ui::DomCode::F7, 0)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F8, 0, 0))); + -1, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F8, ui::DomCode::F8, 0)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F9, 0, 0))); + -1, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F9, ui::DomCode::F9, 0)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F10, 0, 0))); + -1, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F10, ui::DomCode::F10, 0)))); // Shift+Control+Alt+F3 is also an Ash accelerator. Don't reserve it. EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - -1, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, - ui::VKEY_F3, - ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, 0))); + -1, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F3, ui::DomCode::F3, + ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)))); #endif // OS_CHROMEOS #if defined(USE_AURA) @@ -91,14 +97,17 @@ // The content::NativeWebKeyboardEvent constructor is available only when // USE_AURA is #defined. EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_NEW_WINDOW, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_N, ui::EF_CONTROL_DOWN, 0))); + IDC_NEW_WINDOW, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_N, ui::DomCode::KEY_N, + ui::EF_CONTROL_DOWN)))); EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_CLOSE_TAB, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_W, ui::EF_CONTROL_DOWN, 0))); + IDC_CLOSE_TAB, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_W, ui::DomCode::KEY_W, + ui::EF_CONTROL_DOWN)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( IDC_FIND, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_F, ui::EF_CONTROL_DOWN, 0))); + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_F, + ui::DomCode::KEY_F, ui::EF_CONTROL_DOWN)))); #endif // USE_AURA } @@ -109,31 +118,34 @@ // When is_app(), no keys are reserved. #if defined(OS_CHROMEOS) EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_BACK, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F1, 0, 0))); + IDC_BACK, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F1, ui::DomCode::F1, 0)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_FORWARD, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F2, 0, 0))); + IDC_FORWARD, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F2, ui::DomCode::F2, 0)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_RELOAD, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F3, 0, 0))); + IDC_RELOAD, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F3, ui::DomCode::F3, 0)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false, - ui::VKEY_F4, 0, 0))); + -1, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_F4, ui::DomCode::F4, 0)))); #endif // OS_CHROMEOS #if defined(USE_AURA) // The content::NativeWebKeyboardEvent constructor is available only when // USE_AURA is #defined. EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_NEW_WINDOW, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_N, ui::EF_CONTROL_DOWN, 0))); + IDC_NEW_WINDOW, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_N, ui::DomCode::KEY_N, + ui::EF_CONTROL_DOWN)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( - IDC_CLOSE_TAB, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_W, ui::EF_CONTROL_DOWN, 0))); + IDC_CLOSE_TAB, content::NativeWebKeyboardEvent(ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_W, ui::DomCode::KEY_W, + ui::EF_CONTROL_DOWN)))); EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey( IDC_FIND, content::NativeWebKeyboardEvent( - ui::ET_KEY_PRESSED, false, ui::VKEY_F, ui::EF_CONTROL_DOWN, 0))); + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_F, + ui::DomCode::KEY_F, ui::EF_CONTROL_DOWN)))); #endif // USE_AURA }
diff --git a/chrome/browser/ui/panels/panel_browsertest.cc b/chrome/browser/ui/panels/panel_browsertest.cc index e7e58af..639e101 100644 --- a/chrome/browser/ui/panels/panel_browsertest.cc +++ b/chrome/browser/ui/panels/panel_browsertest.cc
@@ -44,7 +44,9 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/hit_test.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/events/event.h" #include "ui/events/event_utils.h" +#include "ui/events/keycodes/dom/dom_code.h" #include "ui/gfx/screen.h" using content::WebContents; @@ -1696,21 +1698,10 @@ content::WindowedNotificationObserver signal( chrome::NOTIFICATION_PANEL_CLOSED, content::Source<Panel>(panel)); -#if defined(USE_AURA) - double now = ui::EventTimeForNow().InSecondsF(); - content::NativeWebKeyboardEvent key_event( - ui::ET_KEY_PRESSED, - false, - ui::VKEY_W, - ui::EF_CONTROL_DOWN, - now); -#elif defined(OS_WIN) - ::MSG key_msg = { NULL, WM_KEYDOWN, ui::VKEY_W, 0 }; - content::NativeWebKeyboardEvent key_event(key_msg); - key_event.modifiers = content::NativeWebKeyboardEvent::ControlKey; -#else - content::NativeWebKeyboardEvent key_event; -#endif + + ui::KeyEvent ui_event(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::DomCode::KEY_W, + ui::EF_CONTROL_DOWN); + content::NativeWebKeyboardEvent key_event(ui_event); panel->HandleKeyboardEvent(key_event); signal.Wait(); EXPECT_EQ(0, panel_manager->num_panels());
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc index ee5e257..dba5638 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -10,6 +10,8 @@ #include "chrome/test/base/testing_profile.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/dom/dom_code.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/input_method/input_method_configuration.h" @@ -115,11 +117,17 @@ // Checks that a single change of the text in the omnibox invokes // only one call to OmniboxViewViews::UpdatePopup(). TEST_F(OmniboxViewViewsTest, UpdatePopupCall) { - omnibox_textfield()->InsertChar('a', 0); + ui::KeyEvent char_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, 0, + ui::DomKey::FromCharacter('a'), + ui::EventTimeForNow()); + omnibox_textfield()->InsertChar(char_event); omnibox_view()->CheckUpdatePopupCallInfo( 1, base::ASCIIToUTF16("a"), gfx::Range(1)); - omnibox_textfield()->InsertChar('b', 0); + char_event = + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, ui::DomCode::KEY_B, 0, + ui::DomKey::FromCharacter('b'), ui::EventTimeForNow()); + omnibox_textfield()->InsertChar(char_event); omnibox_view()->CheckUpdatePopupCallInfo( 2, base::ASCIIToUTF16("ab"), gfx::Range(2));
diff --git a/chrome/browser/ui/webui/options/font_settings_handler.cc b/chrome/browser/ui/webui/options/font_settings_handler.cc index 865e6715..690e053 100644 --- a/chrome/browser/ui/webui/options/font_settings_handler.cc +++ b/chrome/browser/ui/webui/options/font_settings_handler.cc
@@ -64,7 +64,8 @@ namespace options { FontSettingsHandler::FontSettingsHandler() - : extension_registry_observer_(this) { + : extension_registry_observer_(this), + weak_ptr_factory_(this) { } FontSettingsHandler::~FontSettingsHandler() { @@ -190,7 +191,7 @@ void FontSettingsHandler::HandleFetchFontsData(const base::ListValue* args) { content::GetFontListAsync( base::Bind(&FontSettingsHandler::FontsListHasLoaded, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); } void FontSettingsHandler::FontsListHasLoaded(
diff --git a/chrome/browser/ui/webui/options/font_settings_handler.h b/chrome/browser/ui/webui/options/font_settings_handler.h index 98da3f3..171d731 100644 --- a/chrome/browser/ui/webui/options/font_settings_handler.h +++ b/chrome/browser/ui/webui/options/font_settings_handler.h
@@ -5,7 +5,9 @@ #ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_HANDLER_H_ #define CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_HANDLER_H_ +#include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/prefs/pref_member.h" #include "base/scoped_observer.h" #include "chrome/browser/ui/webui/options/options_ui.h" @@ -80,6 +82,8 @@ extensions::ExtensionRegistryObserver> extension_registry_observer_; + base::WeakPtrFactory<FontSettingsHandler> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(FontSettingsHandler); };
diff --git a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js index 8befb1a..46f3d744 100644 --- a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js +++ b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
@@ -8,8 +8,6 @@ var fileSystemProviderInternal = require('binding').Binding.create('fileSystemProviderInternal').generate(); var eventBindings = require('event_bindings'); -var fileSystemNatives = requireNative('file_system_natives'); -var GetDOMError = fileSystemNatives.GetDOMError; /** * Maximum size of the thumbnail in bytes.
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index 1334527b..d8af78d 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -7544.0.0 \ No newline at end of file +7546.0.0 \ No newline at end of file
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc index 699a72ad..3b326924 100644 --- a/components/autofill/content/renderer/form_autofill_util.cc +++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1355,8 +1355,7 @@ extract_mask, form, field); } -std::vector<WebFormControlElement> -GetUnownedAutofillableFormFieldElements( +std::vector<WebFormControlElement> GetUnownedFormFieldElements( const WebElementCollection& elements, std::vector<WebElement>* fieldsets) { std::vector<WebFormControlElement> unowned_fieldset_children; @@ -1374,7 +1373,14 @@ fieldsets->push_back(element); } } - return ExtractAutofillableElementsFromSet(unowned_fieldset_children); + return unowned_fieldset_children; +} + +std::vector<WebFormControlElement> GetUnownedAutofillableFormFieldElements( + const WebElementCollection& elements, + std::vector<WebElement>* fieldsets) { + return ExtractAutofillableElementsFromSet( + GetUnownedFormFieldElements(elements, fieldsets)); } bool UnownedCheckoutFormElementsAndFieldSetsToFormData(
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h index 04b1b7c..97872d1c 100644 --- a/components/autofill/content/renderer/form_autofill_util.h +++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -142,6 +142,12 @@ // Get all form control elements from |elements| that are not part of a form. // If |fieldsets| is not NULL, also append the fieldsets encountered that are // not part of a form. +std::vector<blink::WebFormControlElement> GetUnownedFormFieldElements( + const blink::WebElementCollection& elements, + std::vector<blink::WebElement>* fieldsets); + +// A shorthand for filtering the results of GetUnownedFormFieldElements with +// ExtractAutofillableElementsFromSet. std::vector<blink::WebFormControlElement> GetUnownedAutofillableFormFieldElements( const blink::WebElementCollection& elements,
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc index c7a3e05..dc87799d 100644 --- a/components/autofill/content/renderer/password_form_conversion_utils.cc +++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -43,6 +43,8 @@ ~SyntheticForm(); std::vector<blink::WebElement> fieldsets; + // Contains control elements of the represented form, including not fillable + // ones. std::vector<blink::WebFormControlElement> control_elements; blink::WebDocument document; blink::WebString action; @@ -606,9 +608,8 @@ const ModifiedValues* nonscript_modified_values, const FormsPredictionsMap* form_predictions) { SyntheticForm synthetic_form; - synthetic_form.control_elements = - form_util::GetUnownedAutofillableFormFieldElements( - frame.document().all(), &synthetic_form.fieldsets); + synthetic_form.control_elements = form_util::GetUnownedFormFieldElements( + frame.document().all(), &synthetic_form.fieldsets); synthetic_form.document = frame.document(); if (synthetic_form.control_elements.empty())
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc index 19f3e25b4..6d62e64a 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -24,6 +24,13 @@ const char kUMAProxyStartupStateHistogram[] = "DataReductionProxy.StartupState"; +void RecordSettingsEnabledState( + data_reduction_proxy::DataReductionSettingsEnabledAction action) { + UMA_HISTOGRAM_ENUMERATION( + "DataReductionProxy.EnabledState", action, + data_reduction_proxy::DATA_REDUCTION_SETTINGS_ACTION_BOUNDARY); +} + } // namespace namespace data_reduction_proxy { @@ -254,6 +261,12 @@ prefs->SetBoolean(prefs::kDataReductionProxyWasEnabledBefore, true); ResetDataReductionStatistics(); } + if (!at_startup) { + if (IsDataReductionProxyEnabled()) + RecordSettingsEnabledState(DATA_REDUCTION_SETTINGS_ACTION_OFF_TO_ON); + else + RecordSettingsEnabledState(DATA_REDUCTION_SETTINGS_ACTION_ON_TO_OFF); + } // Configure use of the data reduction proxy if it is enabled. if (at_startup && !data_reduction_proxy_service_->Initialized()) deferred_initialization_ = true;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h index c6d6974..45d4f1c 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
@@ -55,6 +55,15 @@ LO_FI_OPT_OUT_ACTION_INDEX_BOUNDARY, }; +// Values of the UMA DataReductionProxy.EnabledState histogram. +// This enum must remain synchronized with DataReductionProxyEnabledState +// in metrics/histograms/histograms.xml. +enum DataReductionSettingsEnabledAction { + DATA_REDUCTION_SETTINGS_ACTION_OFF_TO_ON = 0, + DATA_REDUCTION_SETTINGS_ACTION_ON_TO_OFF, + DATA_REDUCTION_SETTINGS_ACTION_BOUNDARY, +}; + // Central point for configuring the data reduction proxy. // This object lives on the UI thread and all of its methods are expected to // be called from there. @@ -236,6 +245,8 @@ TestLoFiImplicitOptOutHistograms); FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, TestLoFiSessionStateHistograms); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest, + TestSettingsEnabledStateHistograms); // Override of DataReductionProxyService::Observer. void OnServiceInitialized() override;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc index 2c1b10d..28e63104 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
@@ -628,6 +628,33 @@ kUMALoFiSessionState, settings_->lo_fi_consecutive_session_disables_ + 3); } +TEST_F(DataReductionProxySettingsTest, TestSettingsEnabledStateHistograms) { + const char kUMAEnabledState[] = "DataReductionProxy.EnabledState"; + base::HistogramTester histogram_tester; + + settings_->InitPrefMembers(); + settings_->data_reduction_proxy_service_->SetIOData( + test_context_->io_data()->GetWeakPtr()); + + // No settings state histograms should be recorded during startup. + test_context_->RunUntilIdle(); + histogram_tester.ExpectTotalCount(kUMAEnabledState, 0); + + settings_->SetDataReductionProxyEnabled(true); + test_context_->RunUntilIdle(); + histogram_tester.ExpectBucketCount( + kUMAEnabledState, DATA_REDUCTION_SETTINGS_ACTION_OFF_TO_ON, 1); + histogram_tester.ExpectBucketCount( + kUMAEnabledState, DATA_REDUCTION_SETTINGS_ACTION_ON_TO_OFF, 0); + + settings_->SetDataReductionProxyEnabled(false); + test_context_->RunUntilIdle(); + histogram_tester.ExpectBucketCount( + kUMAEnabledState, DATA_REDUCTION_SETTINGS_ACTION_OFF_TO_ON, 1); + histogram_tester.ExpectBucketCount( + kUMAEnabledState, DATA_REDUCTION_SETTINGS_ACTION_ON_TO_OFF, 1); +} + TEST_F(DataReductionProxySettingsTest, TestGetDailyContentLengths) { ContentLengthList result = settings_->GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength);
diff --git a/components/history/core/browser/visit_database_unittest.cc b/components/history/core/browser/visit_database_unittest.cc index 8074dd9..936bb24c 100644 --- a/components/history/core/browser/visit_database_unittest.cc +++ b/components/history/core/browser/visit_database_unittest.cc
@@ -417,16 +417,12 @@ } TEST_F(VisitDatabaseTest, GetHistoryCount) { - Time today = Time::Now().LocalMidnight(); - // Find the beginning of yesterday and the day before yesterday. We cannot use - // TimeDelta::FromDays(1), as this simply removes 24 hours and thus does not - // work correctly with DST shifts. Instead, we'll jump 36 hours (i.e. - // somewhere in the middle of the previous day), and use |LocalMidnight()| to - // round down to the beginning of the day in the local time, taking timezones - // and DST into account. This is necessary to achieve the same equivalence - // class on days as the DATE(..., 'localtime') function in SQL. - Time yesterday = (today - TimeDelta::FromHours(36)).LocalMidnight(); - Time two_days_ago = (yesterday - TimeDelta::FromHours(36)).LocalMidnight(); + // Start with a day in the middle of summer, so that we are nowhere near + // DST shifts. + Time today; + ASSERT_TRUE(Time::FromString("2015-07-07", &today)); + Time yesterday = today - TimeDelta::FromDays(1); + Time two_days_ago = yesterday - TimeDelta::FromDays(1); Time now = two_days_ago; ui::PageTransition standard_transition = ui::PageTransitionFromInt( @@ -526,7 +522,14 @@ EXPECT_EQ(0, result); // If this timezone uses DST, test the behavior on days when the time - // is shifted forward and backward. + // is shifted forward and backward. Note that in this case we cannot use + // TimeDelta::FromDays(1) to move one day, as this simply removes 24 hours and + // thus does not work correctly with DST shifts. Instead, we'll go back + // 1 second (i.e. somewhere in the middle of the previous day), and use + // |LocalMidnight()| to round down to the beginning of the day in the local + // time, taking timezones and DST into account. This is necessary to achieve + // the same equivalence class on days as the DATE(..., 'localtime') function + // in SQL. Time shift_forward; Time shift_backward; Time current_day = (two_days_ago - TimeDelta::FromSeconds(1)).LocalMidnight();
diff --git a/components/html_viewer/web_layer_tree_view_impl.cc b/components/html_viewer/web_layer_tree_view_impl.cc index 0870aa8..9b1f468 100644 --- a/components/html_viewer/web_layer_tree_view_impl.cc +++ b/components/html_viewer/web_layer_tree_view_impl.cc
@@ -91,11 +91,7 @@ void WebLayerTreeViewImpl::BeginMainFrame(const cc::BeginFrameArgs& args) { VLOG(2) << "WebLayerTreeViewImpl::BeginMainFrame"; double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF(); - double deadline_sec = (args.deadline - base::TimeTicks()).InSecondsF(); - double interval_sec = args.interval.InSecondsF(); - blink::WebBeginFrameArgs web_begin_frame_args( - frame_time_sec, deadline_sec, interval_sec); - widget_->beginFrame(web_begin_frame_args); + widget_->beginFrame(frame_time_sec); layer_tree_host_->SetNeedsAnimate(); }
diff --git a/components/scheduler/base/task_queue_manager_perftest.cc b/components/scheduler/base/task_queue_manager_perftest.cc index 66e2e32..473ed0df 100644 --- a/components/scheduler/base/task_queue_manager_perftest.cc +++ b/components/scheduler/base/task_queue_manager_perftest.cc
@@ -24,6 +24,11 @@ num_tasks_to_post_(0), num_tasks_to_run_(0) {} + void SetUp() override { + if (base::ThreadTicks::IsSupported()) + base::ThreadTicks::WaitUntilInitialized(); + } + void Initialize(size_t num_queues) { num_queues_ = num_queues; message_loop_.reset(new base::MessageLoop());
diff --git a/components/test_runner/web_test_proxy.cc b/components/test_runner/web_test_proxy.cc index bc55b0c..3601ff505 100644 --- a/components/test_runner/web_test_proxy.cc +++ b/components/test_runner/web_test_proxy.cc
@@ -761,16 +761,11 @@ void WebTestProxyBase::AnimateNow() { if (animate_scheduled_) { base::TimeDelta animate_time = base::TimeTicks::Now() - base::TimeTicks(); - base::TimeDelta interval = base::TimeDelta::FromMicroseconds(16666); - blink::WebBeginFrameArgs args(animate_time.InSecondsF(), - (animate_time + interval).InSecondsF(), - interval.InSecondsF()); - animate_scheduled_ = false; - web_widget_->beginFrame(args); + web_widget_->beginFrame(animate_time.InSecondsF()); web_widget_->layout(); if (blink::WebPagePopup* popup = web_widget_->pagePopup()) { - popup->beginFrame(args); + popup->beginFrame(animate_time.InSecondsF()); popup->layout(); } }
diff --git a/content/browser/renderer_host/input/web_input_event_builders_win.cc b/content/browser/renderer_host/input/web_input_event_builders_win.cc index 9d41701..88aa3e8 100644 --- a/content/browser/renderer_host/input/web_input_event_builders_win.cc +++ b/content/browser/renderer_host/input/web_input_event_builders_win.cc
@@ -69,8 +69,7 @@ result.text[0] = result.windowsKeyCode; result.unmodifiedText[0] = result.windowsKeyCode; } - if (result.type != WebInputEvent::Char) - result.setKeyIdentifierFromWindowsKeyCode(); + result.setKeyIdentifierFromWindowsKeyCode(); if (::GetKeyState(VK_SHIFT) & 0x8000) result.modifiers |= WebInputEvent::ShiftKey;
diff --git a/content/browser/renderer_host/native_web_keyboard_event_aura.cc b/content/browser/renderer_host/native_web_keyboard_event_aura.cc index 5f3d8bd9..b84c879b 100644 --- a/content/browser/renderer_host/native_web_keyboard_event_aura.cc +++ b/content/browser/renderer_host/native_web_keyboard_event_aura.cc
@@ -19,14 +19,6 @@ return event ? ui::Event::Clone(*event).release() : nullptr; } -int EventFlagsToWebInputEventModifiers(int flags) { - return - (flags & ui::EF_SHIFT_DOWN ? blink::WebInputEvent::ShiftKey : 0) | - (flags & ui::EF_CONTROL_DOWN ? blink::WebInputEvent::ControlKey : 0) | - (flags & ui::EF_CAPS_LOCK_DOWN ? blink::WebInputEvent::CapsLockOn : 0) | - (flags & ui::EF_ALT_DOWN ? blink::WebInputEvent::AltKey : 0); -} - } // namespace using blink::WebKeyboardEvent; @@ -58,34 +50,16 @@ match_edit_command(false) { } -NativeWebKeyboardEvent::NativeWebKeyboardEvent( - ui::EventType key_event_type, - bool is_char, - wchar_t character, - int state, - double time_stamp_seconds) - : os_event(NULL), +NativeWebKeyboardEvent::NativeWebKeyboardEvent(const ui::KeyEvent& key_event, + base::char16 character) + : WebKeyboardEvent(MakeWebKeyboardEvent(key_event)), + os_event(NULL), skip_in_browser(false), match_edit_command(false) { - switch (key_event_type) { - case ui::ET_KEY_PRESSED: - type = is_char ? blink::WebInputEvent::Char : - blink::WebInputEvent::RawKeyDown; - break; - case ui::ET_KEY_RELEASED: - type = blink::WebInputEvent::KeyUp; - break; - default: - NOTREACHED(); - } - - modifiers = EventFlagsToWebInputEventModifiers(state); - timeStampSeconds = time_stamp_seconds; + type = blink::WebInputEvent::Char; windowsKeyCode = character; - nativeKeyCode = character; text[0] = character; unmodifiedText[0] = character; - isSystemKey = ui::IsSystemKeyModifier(state); setKeyIdentifierFromWindowsKeyCode(); }
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index c7aac43..96cfc510 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1607,22 +1607,17 @@ has_composition_text_ = false; } -void RenderWidgetHostViewAura::InsertChar(base::char16 ch, int flags) { +void RenderWidgetHostViewAura::InsertChar(const ui::KeyEvent& event) { if (popup_child_host_view_ && popup_child_host_view_->NeedsInputGrab()) { - popup_child_host_view_->InsertChar(ch, flags); + popup_child_host_view_->InsertChar(event); return; } // Ignore character messages for VKEY_RETURN sent on CTRL+M. crbug.com/315547 - if (host_ && (accept_return_character_ || ch != ui::VKEY_RETURN)) { - double now = ui::EventTimeForNow().InSecondsF(); + if (host_ && + (accept_return_character_ || event.GetCharacter() != ui::VKEY_RETURN)) { // Send a blink::WebInputEvent::Char event to |host_|. - NativeWebKeyboardEvent webkit_event(ui::ET_KEY_PRESSED, - true /* is_char */, - ch, - flags, - now); - ForwardKeyboardEvent(webkit_event); + ForwardKeyboardEvent(NativeWebKeyboardEvent(event, event.GetCharacter())); } }
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h index 99bb3ce..8cc79db 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -201,7 +201,7 @@ void ConfirmCompositionText() override; void ClearCompositionText() override; void InsertText(const base::string16& text) override; - void InsertChar(base::char16 ch, int flags) override; + void InsertChar(const ui::KeyEvent& event) override; ui::TextInputType GetTextInputType() const override; ui::TextInputMode GetTextInputMode() const override; int GetTextInputFlags() const override;
diff --git a/content/browser/renderer_host/render_widget_host_view_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_browsertest.cc index 3e16918..df02f17 100644 --- a/content/browser/renderer_host/render_widget_host_view_browsertest.cc +++ b/content/browser/renderer_host/render_widget_host_view_browsertest.cc
@@ -329,8 +329,14 @@ // Test basic frame subscription functionality. We subscribe, and then run // until at least one DeliverFrameCallback has been invoked. +// https://crbug.com/542896 +#if defined(MEMORY_SANITIZER) +#define MAYBE_FrameSubscriberTest DISABLED_FrameSubscriberTest +#else +#define MAYBE_FrameSubscriberTest FrameSubscriberTest +#endif IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest, - FrameSubscriberTest) { + MAYBE_FrameSubscriberTest) { SET_UP_SURFACE_OR_PASS_TEST(NULL); RenderWidgetHostViewBase* const view = GetRenderWidgetHostView(); @@ -698,8 +704,16 @@ std::string test_url_; }; +// https://crbug.com/542896 +#if defined(MEMORY_SANITIZER) +#define MAYBE_CopyFromCompositingSurface_Origin_Unscaled \ + DISABLED_CopyFromCompositingSurface_Origin_Unscaled +#else +#define MAYBE_CopyFromCompositingSurface_Origin_Unscaled \ + CopyFromCompositingSurface_Origin_Unscaled +#endif IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, - CopyFromCompositingSurface_Origin_Unscaled) { + MAYBE_CopyFromCompositingSurface_Origin_Unscaled) { gfx::Rect copy_rect(400, 300); gfx::Size output_size = copy_rect.size(); gfx::Size html_rect_size(400, 300); @@ -710,8 +724,16 @@ video_frame); } +// https://crbug.com/542896 +#if defined(MEMORY_SANITIZER) +#define MAYBE_CopyFromCompositingSurface_Origin_Scaled \ + DISABLED_FrameSubscriberTestCopyFromCompositingSurface_Origin_Scaled +#else +#define MAYBE_CopyFromCompositingSurface_Origin_Scaled \ + CopyFromCompositingSurface_Origin_Scaled +#endif IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, - CopyFromCompositingSurface_Origin_Scaled) { + MAYBE_CopyFromCompositingSurface_Origin_Scaled) { gfx::Rect copy_rect(400, 300); gfx::Size output_size(200, 100); gfx::Size html_rect_size(400, 300); @@ -722,8 +744,16 @@ video_frame); } +// https://crbug.com/542896 +#if defined(MEMORY_SANITIZER) +#define MAYBE_CopyFromCompositingSurface_Cropped_Unscaled \ + DISABLED_CopyFromCompositingSurface_Cropped_Unscaled +#else +#define MAYBE_CopyFromCompositingSurface_Cropped_Unscaled \ + CopyFromCompositingSurface_Cropped_Unscaled +#endif IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, - CopyFromCompositingSurface_Cropped_Unscaled) { + MAYBE_CopyFromCompositingSurface_Cropped_Unscaled) { // Grab 60x60 pixels from the center of the tab contents. gfx::Rect copy_rect(400, 300); copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(30, 30), @@ -737,8 +767,16 @@ video_frame); } +// https://crbug.com/542896 +#if defined(MEMORY_SANITIZER) +#define MAYBE_CopyFromCompositingSurface_Cropped_Scaled \ + DISABLED_CopyFromCompositingSurface_Cropped_Scaled +#else +#define MAYBE_CopyFromCompositingSurface_Cropped_Scaled \ + CopyFromCompositingSurface_Cropped_Scaled +#endif IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, - CopyFromCompositingSurface_Cropped_Scaled) { + MAYBE_CopyFromCompositingSurface_Cropped_Scaled) { // Grab 60x60 pixels from the center of the tab contents. gfx::Rect copy_rect(400, 300); copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(30, 30), @@ -752,8 +790,16 @@ video_frame); } +// https://crbug.com/542896 +#if defined(MEMORY_SANITIZER) +#define MAYBE_CopyFromCompositingSurface_ForVideoFrame \ + DISABLED_CopyFromCompositingSurface_ForVideoFrame +#else +#define MAYBE_CopyFromCompositingSurface_ForVideoFrame \ + CopyFromCompositingSurface_ForVideoFrame +#endif IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, - CopyFromCompositingSurface_ForVideoFrame) { + MAYBE_CopyFromCompositingSurface_ForVideoFrame) { // Grab 90x60 pixels from the center of the tab contents. gfx::Rect copy_rect(400, 300); copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(45, 30), @@ -767,8 +813,16 @@ video_frame); } +// https://crbug.com/542896 +#if defined(MEMORY_SANITIZER) +#define MAYBE_CopyFromCompositingSurface_ForVideoFrame_Scaled \ + DISABLED_CopyFromCompositingSurface_ForVideoFrame_Scaled +#else +#define MAYBE_CopyFromCompositingSurface_ForVideoFrame_Scaled \ + CopyFromCompositingSurface_ForVideoFrame_Scaled +#endif IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, - CopyFromCompositingSurface_ForVideoFrame_Scaled) { + MAYBE_CopyFromCompositingSurface_ForVideoFrame_Scaled) { // Grab 90x60 pixels from the center of the tab contents. gfx::Rect copy_rect(400, 300); copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(45, 30), @@ -819,7 +873,8 @@ // NineImagePainter implementation crashes the process on Windows when this // content_browsertest forces a device scale factor. http://crbug.com/399349 -#if defined(OS_WIN) +// Disabled under MSAN. http://crbug.com/542896 +#if defined(OS_WIN) || defined(MEMORY_SANITIZER) #define MAYBE_CopyToBitmap_EntireRegion DISABLED_CopyToBitmap_EntireRegion #define MAYBE_CopyToBitmap_CenterRegion DISABLED_CopyToBitmap_CenterRegion #define MAYBE_CopyToBitmap_ScaledResult DISABLED_CopyToBitmap_ScaledResult
diff --git a/content/browser/service_worker/service_worker_cache_writer.cc b/content/browser/service_worker/service_worker_cache_writer.cc index e934300..20c6987 100644 --- a/content/browser/service_worker/service_worker_cache_writer.cc +++ b/content/browser/service_worker/service_worker_cache_writer.cc
@@ -109,7 +109,7 @@ state_ = STATE_DONE; break; } - } while (status >= net::OK && state_ != STATE_DONE); + } while (status != net::ERR_IO_PENDING && state_ != STATE_DONE); io_pending_ = (status == net::ERR_IO_PENDING); return status; } @@ -209,6 +209,7 @@ } int ServiceWorkerCacheWriter::DoReadHeadersForCompare(int result) { + DCHECK_GE(result, 0); DCHECK(headers_to_write_); headers_to_read_ = new HttpResponseInfoIOBuffer; @@ -228,6 +229,7 @@ } int ServiceWorkerCacheWriter::DoReadDataForCompare(int result) { + DCHECK_GE(result, 0); DCHECK(data_to_write_); data_to_read_ = new net::IOBuffer(len_to_write_); @@ -244,13 +246,14 @@ DCHECK(data_to_read_); DCHECK(data_to_write_); DCHECK_EQ(len_to_read_, len_to_write_); - DCHECK_LE(result + compare_offset_, static_cast<size_t>(len_to_write_)); if (result < 0) { state_ = STATE_DONE; return result; } + DCHECK_LE(result + compare_offset_, static_cast<size_t>(len_to_write_)); + // Premature EOF while reading the service worker script cache data to // compare. Fail the comparison. if (result == 0 && len_to_write_ != 0) { @@ -302,6 +305,7 @@ } int ServiceWorkerCacheWriter::DoReadHeadersForCopy(int result) { + DCHECK_GE(result, 0); bytes_copied_ = 0; copy_reader_ = reader_creator_.Run(); headers_to_read_ = new HttpResponseInfoIOBuffer; @@ -325,6 +329,7 @@ // Also note that this *discards* the read headers and replaces them with the // net headers. int ServiceWorkerCacheWriter::DoWriteHeadersForCopy(int result) { + DCHECK_GE(result, 0); DCHECK(!writer_); writer_ = writer_creator_.Run(); state_ = STATE_WRITE_HEADERS_FOR_COPY_DONE; @@ -341,6 +346,7 @@ } int ServiceWorkerCacheWriter::DoReadDataForCopy(int result) { + DCHECK_GE(result, 0); size_t to_read = std::min(kCopyBufferSize, bytes_compared_ - bytes_copied_); // At this point, all compared bytes have been read. Currently // |data_to_write_| and |len_to_write_| hold the chunk of network input that @@ -381,6 +387,7 @@ } int ServiceWorkerCacheWriter::DoWriteHeadersForPassthrough(int result) { + DCHECK_GE(result, 0); writer_ = writer_creator_.Run(); state_ = STATE_WRITE_HEADERS_FOR_PASSTHROUGH_DONE; return WriteInfoHelper(writer_, headers_to_write_.get()); @@ -388,10 +395,11 @@ int ServiceWorkerCacheWriter::DoWriteHeadersForPassthroughDone(int result) { state_ = STATE_DONE; - return net::OK; + return result; } int ServiceWorkerCacheWriter::DoWriteDataForPassthrough(int result) { + DCHECK_GE(result, 0); state_ = STATE_WRITE_DATA_FOR_PASSTHROUGH_DONE; if (len_to_write_ > 0) result = WriteDataHelper(writer_, data_to_write_.get(), len_to_write_); @@ -410,7 +418,7 @@ int ServiceWorkerCacheWriter::DoDone(int result) { state_ = STATE_DONE; - return net::OK; + return result; } // These helpers adapt the AppCache "always use the callback" pattern to the
diff --git a/content/browser/service_worker/service_worker_cache_writer_unittest.cc b/content/browser/service_worker/service_worker_cache_writer_unittest.cc index c58b57b5..887cedc 100644 --- a/content/browser/service_worker/service_worker_cache_writer_unittest.cc +++ b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
@@ -205,7 +205,9 @@ // Enqueue expected writes. void ExpectWriteInfoOk(size_t len, bool async); + void ExpectWriteInfo(size_t len, bool async, int result); void ExpectWriteDataOk(size_t len, bool async); + void ExpectWriteData(size_t len, bool async, int result); // Complete a pending asynchronous write. This method DCHECKs unless there is // a pending write (a write for which WriteInfo() or WriteData() has been @@ -239,8 +241,10 @@ DCHECK(!expected_writes_.empty()); ExpectedWrite write = expected_writes_.front(); EXPECT_TRUE(write.is_info); - EXPECT_EQ(write.length, static_cast<size_t>(info_buf->response_data_size)); - info_written_ += info_buf->response_data_size; + if (write.result > 0) { + EXPECT_EQ(write.length, static_cast<size_t>(info_buf->response_data_size)); + info_written_ += info_buf->response_data_size; + } if (!write.async) { expected_writes_.pop(); callback.Run(write.result); @@ -256,8 +260,10 @@ DCHECK(!expected_writes_.empty()); ExpectedWrite write = expected_writes_.front(); EXPECT_FALSE(write.is_info); - EXPECT_EQ(write.length, static_cast<size_t>(buf_len)); - data_written_ += buf_len; + if (write.result > 0) { + EXPECT_EQ(write.length, static_cast<size_t>(buf_len)); + data_written_ += buf_len; + } if (!write.async) { expected_writes_.pop(); callback.Run(write.result); @@ -268,13 +274,27 @@ void MockServiceWorkerResponseWriter::ExpectWriteInfoOk(size_t length, bool async) { - ExpectedWrite expected(true, length, async, length); - expected_writes_.push(expected); + ExpectWriteInfo(length, async, length); } void MockServiceWorkerResponseWriter::ExpectWriteDataOk(size_t length, bool async) { - ExpectedWrite expected(false, length, async, length); + ExpectWriteData(length, async, length); +} + +void MockServiceWorkerResponseWriter::ExpectWriteInfo(size_t length, + bool async, + int result) { + DCHECK_NE(net::ERR_IO_PENDING, result); + ExpectedWrite expected(true, length, async, result); + expected_writes_.push(expected); +} + +void MockServiceWorkerResponseWriter::ExpectWriteData(size_t length, + bool async, + int result) { + DCHECK_NE(net::ERR_IO_PENDING, result); + ExpectedWrite expected(false, length, async, result); expected_writes_.push(expected); } @@ -399,6 +419,7 @@ EXPECT_FALSE(write_complete_); writer->CompletePendingWrite(); EXPECT_TRUE(write_complete_); + EXPECT_EQ(net::OK, last_error_); EXPECT_TRUE(writer->AllExpectedWritesDone()); EXPECT_EQ(0U, cache_writer_->bytes_written()); } @@ -447,6 +468,62 @@ EXPECT_EQ(net::ERR_IO_PENDING, error); writer->CompletePendingWrite(); EXPECT_TRUE(write_complete_); + EXPECT_EQ(net::OK, last_error_); + EXPECT_TRUE(writer->AllExpectedWritesDone()); +} + +TEST_F(ServiceWorkerCacheWriterTest, PassthroughHeadersFailSync) { + const size_t kHeaderSize = 16; + MockServiceWorkerResponseWriter* writer = ExpectWriter(); + writer->ExpectWriteInfo(kHeaderSize, false, net::ERR_FAILED); + + net::Error error = WriteHeaders(kHeaderSize); + EXPECT_EQ(net::ERR_FAILED, error); + EXPECT_FALSE(write_complete_); + EXPECT_TRUE(writer->AllExpectedWritesDone()); + EXPECT_EQ(0U, cache_writer_->bytes_written()); +} + +TEST_F(ServiceWorkerCacheWriterTest, PassthroughHeadersFailAsync) { + size_t kHeaderSize = 16; + MockServiceWorkerResponseWriter* writer = ExpectWriter(); + writer->ExpectWriteInfo(kHeaderSize, true, net::ERR_FAILED); + + net::Error error = WriteHeaders(kHeaderSize); + EXPECT_EQ(net::ERR_IO_PENDING, error); + EXPECT_FALSE(write_complete_); + writer->CompletePendingWrite(); + EXPECT_TRUE(write_complete_); + EXPECT_EQ(net::ERR_FAILED, last_error_); + EXPECT_TRUE(writer->AllExpectedWritesDone()); + EXPECT_EQ(0U, cache_writer_->bytes_written()); +} + +TEST_F(ServiceWorkerCacheWriterTest, PassthroughDataFailSync) { + const std::string data = "abcdef"; + + MockServiceWorkerResponseWriter* writer = ExpectWriter(); + writer->ExpectWriteInfoOk(data.size(), false); + writer->ExpectWriteData(data.size(), false, net::ERR_FAILED); + + EXPECT_EQ(net::OK, WriteHeaders(data.size())); + EXPECT_EQ(net::ERR_FAILED, WriteData(data)); + EXPECT_TRUE(writer->AllExpectedWritesDone()); +} + +TEST_F(ServiceWorkerCacheWriterTest, PassthroughDataFailAsync) { + const std::string data = "abcdef"; + + MockServiceWorkerResponseWriter* writer = ExpectWriter(); + writer->ExpectWriteInfoOk(data.size(), false); + writer->ExpectWriteData(data.size(), true, net::ERR_FAILED); + + EXPECT_EQ(net::OK, WriteHeaders(data.size())); + + EXPECT_EQ(net::ERR_IO_PENDING, WriteData(data)); + writer->CompletePendingWrite(); + EXPECT_EQ(net::ERR_FAILED, last_error_); + EXPECT_TRUE(write_complete_); EXPECT_TRUE(writer->AllExpectedWritesDone()); } @@ -489,6 +566,38 @@ EXPECT_EQ(0U, cache_writer_->bytes_written()); } +TEST_F(ServiceWorkerCacheWriterTest, CompareHeadersFailSync) { + size_t response_size = 3; + MockServiceWorkerResponseWriter* writer = ExpectWriter(); + MockServiceWorkerResponseReader* reader = ExpectReader(); + + reader->ExpectReadInfo(response_size, false, net::ERR_FAILED); + + EXPECT_EQ(net::ERR_FAILED, WriteHeaders(response_size)); + EXPECT_TRUE(writer->AllExpectedWritesDone()); + EXPECT_TRUE(reader->AllExpectedReadsDone()); +} + +TEST_F(ServiceWorkerCacheWriterTest, CompareDataFailSync) { + const std::string data1 = "abcdef"; + size_t response_size = data1.size(); + + MockServiceWorkerResponseWriter* writer = ExpectWriter(); + MockServiceWorkerResponseReader* reader = ExpectReader(); + + reader->ExpectReadInfoOk(response_size, false); + reader->ExpectReadData(data1.c_str(), data1.length(), false, net::ERR_FAILED); + + net::Error error = WriteHeaders(response_size); + EXPECT_EQ(net::OK, error); + + EXPECT_EQ(net::ERR_FAILED, WriteData(data1)); + + EXPECT_TRUE(writer->AllExpectedWritesDone()); + EXPECT_TRUE(reader->AllExpectedReadsDone()); + EXPECT_EQ(0U, cache_writer_->bytes_written()); +} + TEST_F(ServiceWorkerCacheWriterTest, CompareShortCacheReads) { const size_t kHeaderSize = 16; const std::string& data1 = "abcdef";
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc index 8e3752ef..141b83fb 100644 --- a/content/browser/service_worker/service_worker_context_core.cc +++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -76,7 +76,11 @@ const std::vector<ServiceWorkerRegistrationInfo>& registrations) { if (!context) return; - for (const auto& version_itr : context->GetLiveVersions()) { + // Make a copy of live versions map because StopWorker() removes the version + // from it when we were starting up and don't have a process yet. + const std::map<int64, ServiceWorkerVersion*> live_versions_copy = + context->GetLiveVersions(); + for (const auto& version_itr : live_versions_copy) { ServiceWorkerVersion* version(version_itr.second); if (version->running_status() == ServiceWorkerVersion::STARTING || version->running_status() == ServiceWorkerVersion::RUNNING) {
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc index b79894e..153be6e 100644 --- a/content/browser/service_worker/service_worker_database.cc +++ b/content/browser/service_worker/service_worker_database.cc
@@ -1025,12 +1025,12 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) { - return WriteResourceIds(kUncommittedResIdKeyPrefix, ids); -} - -ServiceWorkerDatabase::Status -ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) { - return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids); + leveldb::WriteBatch batch; + Status status = + WriteResourceIdsInBatch(kUncommittedResIdKeyPrefix, ids, &batch); + if (status != STATUS_OK) + return status; + return WriteBatch(&batch); } ServiceWorkerDatabase::Status @@ -1039,13 +1039,13 @@ } ServiceWorkerDatabase::Status -ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) { - return WriteResourceIds(kPurgeableResIdKeyPrefix, ids); -} - -ServiceWorkerDatabase::Status ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) { - return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids); + leveldb::WriteBatch batch; + Status status = + DeleteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch); + if (status != STATUS_OK) + return status; + return WriteBatch(&batch); } ServiceWorkerDatabase::Status @@ -1404,16 +1404,6 @@ return status; } -ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds( - const char* id_key_prefix, - const std::set<int64>& ids) { - leveldb::WriteBatch batch; - Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch); - if (status != STATUS_OK) - return status; - return WriteBatch(&batch); -} - ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch( const char* id_key_prefix, const std::set<int64>& ids, @@ -1437,16 +1427,6 @@ return STATUS_OK; } -ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds( - const char* id_key_prefix, - const std::set<int64>& ids) { - leveldb::WriteBatch batch; - Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch); - if (status != STATUS_OK) - return status; - return WriteBatch(&batch); -} - ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch( const char* id_key_prefix, const std::set<int64>& ids,
diff --git a/content/browser/service_worker/service_worker_database.h b/content/browser/service_worker/service_worker_database.h index 8dd0b62..6b7dea0 100644 --- a/content/browser/service_worker/service_worker_database.h +++ b/content/browser/service_worker/service_worker_database.h
@@ -200,39 +200,34 @@ const std::string& user_data_name, std::vector<std::pair<int64, std::string>>* user_data); - // As new resources are put into the diskcache, they go into an uncommitted - // list. When a registration is saved that refers to those ids, they're - // removed from that list. When a resource no longer has any registrations or + // Resources should belong to one of following resource lists: uncommitted, + // committed and purgeable. + // As new resources are put into the diskcache, they go into the uncommitted + // list. When a registration is saved that refers to those ids, they're moved + // to the committed list. When a resource no longer has any registrations or // caches referring to it, it's added to the purgeable list. Periodically, // the purgeable list can be purged from the diskcache. At system startup, all // uncommitted ids are moved to the purgeable list. - // Reads uncommitted resource ids from the database. Returns OK on success. + // Reads resource ids from the uncommitted list. Returns OK on success. // Otherwise clears |ids| and returns an error. Status GetUncommittedResourceIds(std::set<int64>* ids); - // Writes |ids| into the database as uncommitted resources. Returns OK on - // success. Otherwise writes nothing and returns an error. + // Writes resource ids into the uncommitted list. Returns OK on success. + // Otherwise writes nothing and returns an error. Status WriteUncommittedResourceIds(const std::set<int64>& ids); - // Deletes uncommitted resource ids specified by |ids| from the database. - // Returns OK on success. Otherwise deletes nothing and returns an error. - Status ClearUncommittedResourceIds(const std::set<int64>& ids); - - // Reads purgeable resource ids from the database. Returns OK on success. + // Reads resource ids from the purgeable list. Returns OK on success. // Otherwise clears |ids| and returns an error. Status GetPurgeableResourceIds(std::set<int64>* ids); - // Writes |ids| into the database as purgeable resources. Returns OK on - // success. Otherwise writes nothing and returns an error. - Status WritePurgeableResourceIds(const std::set<int64>& ids); - - // Deletes purgeable resource ids specified by |ids| from the database. - // Returns OK on success. Otherwise deletes nothing and returns an error. + // Deletes resource ids from the purgeable list. Returns OK on success. + // Otherwise deletes nothing and returns an error. Status ClearPurgeableResourceIds(const std::set<int64>& ids); - // Moves |ids| from the uncommitted list to the purgeable list. - // Returns OK on success. Otherwise deletes nothing and returns an error. + // Writes resource ids into the purgeable list and removes them from the + // uncommitted list. Returns OK on success. Otherwise writes nothing and + // returns an error. Status PurgeUncommittedResourceIds(const std::set<int64>& ids); // Deletes all data for |origins|, namely, unique origin, registrations and @@ -300,9 +295,6 @@ // Write resource ids for |id_key_prefix| into the database. Returns OK on // success. Otherwise, returns writes nothing and returns an error. - Status WriteResourceIds( - const char* id_key_prefix, - const std::set<int64>& ids); Status WriteResourceIdsInBatch( const char* id_key_prefix, const std::set<int64>& ids, @@ -311,9 +303,6 @@ // Deletes resource ids for |id_key_prefix| from the database. Returns OK if // it's successfully deleted or not found in the database. Otherwise, returns // an error. - Status DeleteResourceIds( - const char* id_key_prefix, - const std::set<int64>& ids); Status DeleteResourceIdsInBatch( const char* id_key_prefix, const std::set<int64>& ids,
diff --git a/content/browser/service_worker/service_worker_database_unittest.cc b/content/browser/service_worker/service_worker_database_unittest.cc index 1d19f22..b63b2ac 100644 --- a/content/browser/service_worker/service_worker_database_unittest.cc +++ b/content/browser/service_worker/service_worker_database_unittest.cc
@@ -1396,10 +1396,10 @@ data.registration_id, origin, base::Time::Now())); } -TEST(ServiceWorkerDatabaseTest, UncommittedResourceIds) { +TEST(ServiceWorkerDatabaseTest, UncommittedAndPurgeableResourceIds) { scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); - // Write {1, 2, 3}. + // Write {1, 2, 3} into the uncommitted list. std::set<int64> ids1; ids1.insert(1); ids1.insert(2); @@ -1412,7 +1412,7 @@ database->GetUncommittedResourceIds(&ids_out)); EXPECT_EQ(ids1, ids_out); - // Write {2, 4}. + // Write {2, 4} into the uncommitted list. std::set<int64> ids2; ids2.insert(2); ids2.insert(4); @@ -1422,63 +1422,30 @@ ids_out.clear(); EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->GetUncommittedResourceIds(&ids_out)); - std::set<int64> expected = base::STLSetUnion<std::set<int64> >(ids1, ids2); + std::set<int64> expected = base::STLSetUnion<std::set<int64>>(ids1, ids2); EXPECT_EQ(expected, ids_out); - // Delete {2, 3}. - std::set<int64> ids3; - ids3.insert(2); - ids3.insert(3); + // Move {2, 4} from the uncommitted list to the purgeable list. EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, - database->ClearUncommittedResourceIds(ids3)); + database->PurgeUncommittedResourceIds(ids2)); + ids_out.clear(); + EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, + database->GetPurgeableResourceIds(&ids_out)); + EXPECT_EQ(ids2, ids_out); + // Delete {2, 4} from the purgeable list. + EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, + database->ClearPurgeableResourceIds(ids2)); + ids_out.clear(); + EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, + database->GetPurgeableResourceIds(&ids_out)); + EXPECT_TRUE(ids_out.empty()); + + // {1, 3} should be still in the uncommitted list. ids_out.clear(); EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->GetUncommittedResourceIds(&ids_out)); - expected = base::STLSetDifference<std::set<int64> >(expected, ids3); - EXPECT_EQ(expected, ids_out); -} - -TEST(ServiceWorkerDatabaseTest, PurgeableResourceIds) { - scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); - - // Write {1, 2, 3}. - std::set<int64> ids1; - ids1.insert(1); - ids1.insert(2); - ids1.insert(3); - EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, - database->WritePurgeableResourceIds(ids1)); - - std::set<int64> ids_out; - EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, - database->GetPurgeableResourceIds(&ids_out)); - EXPECT_EQ(ids1, ids_out); - - // Write {2, 4}. - std::set<int64> ids2; - ids2.insert(2); - ids2.insert(4); - EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, - database->WritePurgeableResourceIds(ids2)); - - ids_out.clear(); - EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, - database->GetPurgeableResourceIds(&ids_out)); - std::set<int64> expected = base::STLSetUnion<std::set<int64> >(ids1, ids2); - EXPECT_EQ(expected, ids_out); - - // Delete {2, 3}. - std::set<int64> ids3; - ids3.insert(2); - ids3.insert(3); - EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, - database->ClearPurgeableResourceIds(ids3)); - - ids_out.clear(); - EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, - database->GetPurgeableResourceIds(&ids_out)); - expected = base::STLSetDifference<std::set<int64> >(expected, ids3); + expected = base::STLSetDifference<std::set<int64>>(ids1, ids2); EXPECT_EQ(expected, ids_out); }
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.cc b/content/browser/service_worker/service_worker_write_to_cache_job.cc index 7a960d0..95d6074 100644 --- a/content/browser/service_worker/service_worker_write_to_cache_job.cc +++ b/content/browser/service_worker/service_worker_write_to_cache_job.cc
@@ -40,6 +40,15 @@ "The script resource is behind a redirect, which is disallowed."; const char kServiceWorkerAllowed[] = "Service-Worker-Allowed"; +// The net error code used when the job fails the update attempt because the new +// script is byte-by-byte identical to the incumbent script. This error is shown +// in DevTools and in netlog, so we want something obscure enough that it won't +// conflict with a legitimate network error, and not too alarming if seen by +// developers. +// TODO(falken): Redesign this class so we don't have to fail at the network +// stack layer just to cancel the update. +const int kIdenticalScriptError = net::ERR_FILE_EXISTS; + } // namespace ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob( @@ -170,7 +179,7 @@ // to match the failure this function is about to return. if (status.status() == net::URLRequestStatus::SUCCESS && *bytes_read == 0 && !cache_writer_->did_replace()) { - status = net::URLRequestStatus::FromError(net::ERR_FAILED); + status = net::URLRequestStatus::FromError(kIdenticalScriptError); } if (!status.is_success()) { @@ -352,13 +361,18 @@ info_buffer.get(), base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete, weak_factory_.GetWeakPtr())); - SetStatus(net::URLRequestStatus::FromError(error)); - if (error != net::ERR_IO_PENDING) - NotifyHeadersComplete(); + if (error == net::ERR_IO_PENDING) + return; + OnWriteHeadersComplete(error); } void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(net::Error error) { - SetStatus(net::URLRequestStatus::FromError(error)); + DCHECK_NE(net::ERR_IO_PENDING, error); + if (error != net::OK) { + NotifyStartError(net::URLRequestStatus::FromError(error)); + return; + } + SetStatus(net::URLRequestStatus()); NotifyHeadersComplete(); } @@ -446,7 +460,7 @@ // ServiceWorker and write it back, but which did not replace the incumbent // script because the new script was identical, are considered to have failed. if (status.is_success() && !cache_writer_->did_replace()) { - reported_status = net::URLRequestStatus::FromError(net::ERR_FAILED); + reported_status = net::URLRequestStatus::FromError(kIdenticalScriptError); reported_status_message = ""; } @@ -466,8 +480,7 @@ // exists. if (status.status() == net::URLRequestStatus::SUCCESS && !cache_writer_->did_replace()) { - status = - net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED); + status = net::URLRequestStatus::FromError(kIdenticalScriptError); version_->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS); }
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc index 18115d9..fb5295e 100644 --- a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc +++ b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
@@ -20,7 +20,9 @@ #include "content/public/test/test_browser_thread_bundle.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" +#include "net/base/net_errors.h" #include "net/http/http_response_headers.h" +#include "net/test/url_request/url_request_failed_job.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_job_factory_impl.h" #include "net/url_request/url_request_test_job.h" @@ -50,6 +52,7 @@ std::string GenerateLongResponse() { return std::string(kNumBlocks * kBlockSize, 'a'); } + net::URLRequestJob* CreateNormalURLRequestJob( net::URLRequest* request, net::NetworkDelegate* network_delegate) { @@ -68,6 +71,14 @@ response_data, true); } +net::URLRequestJob* CreateFailedURLRequestJob( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) { + return new net::URLRequestFailedJob(request, network_delegate, + net::URLRequestFailedJob::START, + net::ERR_FAILED); +} + net::URLRequestJob* CreateInvalidMimeTypeJob( net::URLRequest* request, net::NetworkDelegate* network_delegate) { @@ -393,6 +404,9 @@ return helper_->context_wrapper(); } + // Disables the cache to simulate cache errors. + void DisableCache() { context()->storage()->disk_cache()->Disable(); } + protected: TestBrowserThreadBundle browser_thread_bundle_; scoped_ptr<EmbeddedWorkerTestHelper> helper_; @@ -573,4 +587,25 @@ EXPECT_EQ(kInvalidServiceWorkerResponseId, GetResourceId(version.get())); } +TEST_F(ServiceWorkerWriteToCacheJobTest, Error) { + mock_protocol_handler_->SetCreateJobCallback( + base::Bind(&CreateFailedURLRequestJob)); + request_->Start(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(net::URLRequestStatus::FAILED, request_->status().status()); + EXPECT_EQ(net::ERR_FAILED, request_->status().error()); + EXPECT_EQ(kInvalidServiceWorkerResponseId, + version_->script_cache_map()->LookupResourceId(script_url_)); +} + +TEST_F(ServiceWorkerWriteToCacheJobTest, FailedWriteHeadersToCache) { + mock_protocol_handler_->SetCreateJobCallback( + base::Bind(&CreateNormalURLRequestJob)); + DisableCache(); + request_->Start(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(net::URLRequestStatus::FAILED, request_->status().status()); + EXPECT_EQ(net::ERR_FAILED, request_->status().error()); +} + } // namespace content
diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 574f72c2..e659f72b 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi
@@ -2183,6 +2183,19 @@ 'content_shell_test_apk_run.isolate', ], }, + { + 'target_name': 'content_unittests_apk_run', + 'type': 'none', + 'dependencies': [ + 'content_unittests_apk', + ], + 'includes': [ + '../build/isolate.gypi', + ], + 'sources': [ + 'content_unittests_apk.isolate', + ], + }, ], }, ],
diff --git a/content/content_unittests_apk.isolate b/content/content_unittests_apk.isolate new file mode 100644 index 0000000..6d94fd7c --- /dev/null +++ b/content/content_unittests_apk.isolate
@@ -0,0 +1,27 @@ +# 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. +{ + 'includes': [ + '../build/android/android.isolate', + '../net/tools/testserver/testserver.isolate', + 'content_unittests.isolate', + ], + 'variables': { + 'command': [ + '<(PRODUCT_DIR)/bin/run_content_unittests', + ], + 'files': [ + '../base/base.isolate', + '../build/config/', + '../gin/v8.isolate', + '../third_party/angle/angle.isolate', + '../third_party/icu/icu.isolate', + '../third_party/instrumented_libraries/instrumented_libraries.isolate', + '../ui/gl/gl.isolate', + '<(PRODUCT_DIR)/bin/run_content_unittests', + '<(PRODUCT_DIR)/content_unittests_apk/', + 'content_unittests.isolate', + ] + }, +}
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java index 10bf9fd6..2ebe2bb 100644 --- a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
@@ -48,19 +48,14 @@ private int mLastUpdateCompositionEnd = INVALID_COMPOSITION; @VisibleForTesting - AdapterInputConnection(View view, ImeAdapter imeAdapter, Editable editable, - EditorInfo outAttrs) { + AdapterInputConnection( + View view, ImeAdapter imeAdapter, Editable editable, EditorInfo outAttrs) { super(view, true); mInternalView = view; mImeAdapter = imeAdapter; mImeAdapter.setInputConnection(this); mEditable = editable; - // The editable passed in might have been in use by a prior keyboard and could have had - // prior composition spans set. To avoid keyboard conflicts, remove all composing spans - // when taking ownership of an existing Editable. - finishComposingText(); - mSingleLine = true; outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FLAG_NO_EXTRACT_UI; @@ -142,7 +137,6 @@ Log.d(TAG, "Constructor called with outAttrs: %s", dumpEditorInfo(outAttrs)); Selection.setSelection(mEditable, outAttrs.initialSelStart, outAttrs.initialSelEnd); - updateSelectionIfRequired(); } private String dumpEditorInfo(EditorInfo editorInfo) {
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java index 861e9bd..cfeebd9 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java
@@ -11,7 +11,6 @@ import android.test.suitebuilder.annotation.SmallTest; import android.text.Editable; import android.view.View; -import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import org.chromium.base.test.util.Feature; @@ -39,10 +38,9 @@ mWrapper = new TestInputMethodManagerWrapper(getActivity()); ImeAdapterDelegate delegate = new TestImeAdapterDelegate(); mImeAdapter = new TestImeAdapter(mWrapper, delegate); - EditorInfo info = new EditorInfo(); mEditable = Editable.Factory.getInstance().newEditable(""); mConnection = new AdapterInputConnection( - getContentViewCore().getContainerView(), mImeAdapter, mEditable, info); + getContentViewCore().getContainerView(), mImeAdapter, mEditable, new EditorInfo()); } @SmallTest @@ -102,54 +100,17 @@ mWrapper.verifyUpdateSelectionCall(0, 4, 4, 0, 4); } - @MediumTest - @Feature({"TextInput", "Main"}) - public void testNewConnectionFinishesComposingText() throws Throwable { - mConnection.setComposingText("abc", 1); - assertEquals(0, BaseInputConnection.getComposingSpanStart(mEditable)); - assertEquals(3, BaseInputConnection.getComposingSpanEnd(mEditable)); - - mConnection = new AdapterInputConnection( - getContentViewCore().getContainerView(), mImeAdapter, mEditable, new EditorInfo()); - - assertEquals(1, mImeAdapter.mFinishComposingTextCounter); - assertEquals(-1, BaseInputConnection.getComposingSpanStart(mEditable)); - assertEquals(-1, BaseInputConnection.getComposingSpanEnd(mEditable)); - } - private static class TestImeAdapter extends ImeAdapter { private final ArrayList<Integer> mKeyEventQueue = new ArrayList<Integer>(); - private int mDeleteSurroundingTextCounter; - private int mFinishComposingTextCounter; public TestImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embedder) { super(wrapper, embedder); } @Override - public boolean deleteSurroundingText(int beforeLength, int afterLength) { - ++mDeleteSurroundingTextCounter; - return true; - } - - @Override public void sendSyntheticKeyPress(int keyCode, int flags) { mKeyEventQueue.add(keyCode); } - - @Override - protected void finishComposingText() { - ++mFinishComposingTextCounter; - super.finishComposingText(); - } - - public int getDeleteSurroundingTextCallCount() { - return mDeleteSurroundingTextCounter; - } - - public Integer[] getKeyEvents() { - return mKeyEventQueue.toArray(new Integer[mKeyEventQueue.size()]); - } } private static class TestInputMethodManagerWrapper extends InputMethodManagerWrapper { @@ -228,4 +189,4 @@ assertEquals("Composition start did not match", compositionStart, actual.compositionStart); assertEquals("Composition end did not match", compositionEnd, actual.compositionEnd); } -} +} \ No newline at end of file
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java index 4ad8513..bf4c10a 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -31,6 +31,7 @@ import org.chromium.content_shell_apk.ContentShellTestBase; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeoutException; /** @@ -49,9 +50,9 @@ + "</textarea>" + "<br/><p><span id=\"plain_text\">This is Plain Text One</span></p>" + "</form></body></html>"); - private static final int COMPOSITION_KEY_CODE = 229; private TestAdapterInputConnection mConnection; + private TestAdapterInputConnectionFactory mConnectionFactory; private ImeAdapter mImeAdapter; private ContentViewCore mContentViewCore; @@ -71,8 +72,8 @@ mInputMethodManagerWrapper = new TestInputMethodManagerWrapper(mContentViewCore); getImeAdapter().setInputMethodManagerWrapper(mInputMethodManagerWrapper); assertEquals(0, mInputMethodManagerWrapper.getShowSoftInputCounter()); - mContentViewCore.setAdapterInputConnectionFactory( - new TestAdapterInputConnectionFactory()); + mConnectionFactory = new TestAdapterInputConnectionFactory(); + mContentViewCore.setAdapterInputConnectionFactory(mConnectionFactory); mCallbackContainer = new TestCallbackHelperContainer(mContentViewCore); // TODO(aurimas) remove this wait once crbug.com/179511 is fixed. @@ -85,21 +86,42 @@ mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); mImeAdapter = getImeAdapter(); + // Two state updates from focus change and GestureTap. waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1); + waitAndVerifyStatesAndCalls(1, "", 0, 0, -1, -1); + assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelStart); assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelEnd); + + resetUpdateStateList(); + } + + private void assertNoFurtherStateUpdate(final int index) throws InterruptedException { + final List<TestImeState> states = mConnectionFactory.getImeStateList(); + assertFalse(CriteriaHelper.pollForCriteria(new Criteria() { + @Override + public boolean isSatisfied() { + return states.size() > index; + } + })); + } + + @MediumTest + @Feature({"TextInput", "Main"}) + public void testSetUpGeneratesNoFurtherStateUpdate() throws Throwable { + assertNoFurtherStateUpdate(0); } @MediumTest @Feature({"TextInput", "Main"}) public void testKeyboardDismissedAfterClickingGo() throws Throwable { setComposingText("hello", 1); - waitAndVerifyStatesAndCalls(1, "hello", 5, 5, 0, 5); + waitAndVerifyStatesAndCalls(0, "hello", 5, 5, 0, 5); performGo(mCallbackContainer); - waitAndVerifyStatesAndCalls(2, "", 0, 0, -1, -1); + waitAndVerifyStatesAndCalls(1, "", 0, 0, -1, -1); assertWaitForKeyboardStatus(false); } @@ -108,19 +130,19 @@ @RerunWithUpdatedContainerView public void testGetTextUpdatesAfterEnteringText() throws Throwable { setComposingText("h", 1); - waitAndVerifyStates(1, "h", 1, 1, 0, 1); + waitAndVerifyStates(0, "h", 1, 1, 0, 1); assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); setComposingText("he", 1); - waitAndVerifyStates(2, "he", 2, 2, 0, 2); + waitAndVerifyStates(1, "he", 2, 2, 0, 2); assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); setComposingText("hel", 1); - waitAndVerifyStates(3, "hel", 3, 3, 0, 3); + waitAndVerifyStates(2, "hel", 3, 3, 0, 3); assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); commitText("hel", 1); - waitAndVerifyStates(4, "hel", 3, 3, -1, -1); + waitAndVerifyStates(3, "hel", 3, 3, -1, -1); assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); } @@ -129,10 +151,10 @@ @RerunWithUpdatedContainerView public void testImeCopy() throws Exception { commitText("hello", 1); - waitAndVerifyStates(1, "hello", 5, 5, -1, -1); + waitAndVerifyStates(0, "hello", 5, 5, -1, -1); setSelection(2, 5); - waitAndVerifyStates(2, "hello", 2, 5, -1, -1); + waitAndVerifyStates(1, "hello", 2, 5, -1, -1); copy(); assertClipboardContents(getActivity(), "llo"); @@ -142,7 +164,7 @@ @Feature({"TextInput"}) public void testEnterTextAndRefocus() throws Exception { commitText("hello", 1); - waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1); + waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); DOMUtils.clickNode(this, mContentViewCore, "input_radio"); assertWaitForKeyboardStatus(false); @@ -157,7 +179,7 @@ @Feature({"TextInput"}) public void testKeyboardNotDismissedAfterCopySelection() throws Exception { commitText("Sample Text", 1); - waitAndVerifyStatesAndCalls(1, "Sample Text", 11, 11, -1, -1); + waitAndVerifyStatesAndCalls(0, "Sample Text", 11, 11, -1, -1); DOMUtils.clickNode(this, mContentViewCore, "input_text"); assertWaitForKeyboardStatus(true); DOMUtils.longPressNode(this, mContentViewCore, "input_text"); @@ -171,7 +193,7 @@ @Feature({"TextInput"}) public void testImeNotDismissedAfterCutSelection() throws Exception { commitText("Sample Text", 1); - waitAndVerifyStatesAndCalls(1, "Sample Text", 11, 11, -1, -1); + waitAndVerifyStatesAndCalls(0, "Sample Text", 11, 11, -1, -1); DOMUtils.longPressNode(this, mContentViewCore, "input_text"); assertWaitForSelectActionBarStatus(true); assertWaitForKeyboardStatus(true); @@ -206,21 +228,21 @@ public void testLongPressInputWhileComposingText() throws Exception { assertWaitForSelectActionBarStatus(false); setComposingText("Sample Text", 1); - waitAndVerifyStatesAndCalls(1, "Sample Text", 11, 11, 0, 11); + waitAndVerifyStatesAndCalls(0, "Sample Text", 11, 11, 0, 11); DOMUtils.longPressNode(this, mContentViewCore, "input_text"); assertWaitForSelectActionBarStatus(true); // Long press will first change selection region, and then trigger IME app to show up. // See RenderFrameImpl::didChangeSelection() and RenderWidget::didHandleGestureEvent(). + waitAndVerifyStatesAndCalls(1, "Sample Text", 7, 11, 0, 11); waitAndVerifyStatesAndCalls(2, "Sample Text", 7, 11, 0, 11); - waitAndVerifyStatesAndCalls(3, "Sample Text", 7, 11, 0, 11); // Now IME app wants to finish composing text because an external selection // change has been detected. At least Google Latin IME and Samsung IME // behave this way. finishComposingText(); - waitAndVerifyStatesAndCalls(4, "Sample Text", 7, 11, -1, -1); + waitAndVerifyStatesAndCalls(3, "Sample Text", 7, 11, -1, -1); } @SmallTest @@ -298,13 +320,13 @@ @Feature({"TextInput"}) public void testImeCut() throws Exception { commitText("snarful", 1); - waitAndVerifyStatesAndCalls(1, "snarful", 7, 7, -1, -1); + waitAndVerifyStatesAndCalls(0, "snarful", 7, 7, -1, -1); setSelection(1, 5); - waitAndVerifyStatesAndCalls(2, "snarful", 1, 5, -1, -1); + waitAndVerifyStatesAndCalls(1, "snarful", 1, 5, -1, -1); cut(); - waitAndVerifyStatesAndCalls(3, "sul", 1, 1, -1, -1); + waitAndVerifyStatesAndCalls(2, "sul", 1, 1, -1, -1); assertClipboardContents(getActivity(), "narf"); } @@ -323,31 +345,31 @@ }); paste(); - waitAndVerifyStatesAndCalls(1, "blarg", 5, 5, -1, -1); + waitAndVerifyStatesAndCalls(0, "blarg", 5, 5, -1, -1); setSelection(3, 5); - waitAndVerifyStatesAndCalls(2, "blarg", 3, 5, -1, -1); + waitAndVerifyStatesAndCalls(1, "blarg", 3, 5, -1, -1); paste(); // Paste is a two step process when there is a non-zero selection. - waitAndVerifyStates(3, "bla", 3, 3, -1, -1); - waitAndVerifyStatesAndCalls(4, "blablarg", 8, 8, -1, -1); + waitAndVerifyStates(2, "bla", 3, 3, -1, -1); + waitAndVerifyStatesAndCalls(3, "blablarg", 8, 8, -1, -1); paste(); - waitAndVerifyStatesAndCalls(5, "blablargblarg", 13, 13, -1, -1); + waitAndVerifyStatesAndCalls(4, "blablargblarg", 13, 13, -1, -1); } @SmallTest @Feature({"TextInput"}) public void testImeSelectAndUnSelectAll() throws Exception { commitText("hello", 1); - waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1); + waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); selectAll(); - waitAndVerifyStatesAndCalls(2, "hello", 0, 5, -1, -1); + waitAndVerifyStatesAndCalls(1, "hello", 0, 5, -1, -1); unselect(); - waitAndVerifyStatesAndCalls(3, "", 0, 0, -1, -1); + waitAndVerifyStatesAndCalls(2, "", 0, 0, -1, -1); assertWaitForKeyboardStatus(false); } @@ -367,28 +389,25 @@ @SmallTest @Feature({"TextInput", "Main"}) public void testFinishComposingText() throws Throwable { - DOMUtils.focusNode(mWebContents, "input_radio"); - assertWaitForKeyboardStatus(false); - - focusElement("textarea"); + focusElementAndWaitForStateUpdate("textarea"); commitText("hllo", 1); - waitAndVerifyStatesAndCalls(1, "hllo", 4, 4, -1, -1); + waitAndVerifyStatesAndCalls(0, "hllo", 4, 4, -1, -1); commitText(" ", 1); - waitAndVerifyStatesAndCalls(2, "hllo ", 5, 5, -1, -1); + waitAndVerifyStatesAndCalls(1, "hllo ", 5, 5, -1, -1); setSelection(1, 1); - waitAndVerifyStatesAndCalls(3, "hllo ", 1, 1, -1, -1); + waitAndVerifyStatesAndCalls(2, "hllo ", 1, 1, -1, -1); setComposingRegion(0, 4); - waitAndVerifyStatesAndCalls(4, "hllo ", 1, 1, 0, 4); + waitAndVerifyStatesAndCalls(3, "hllo ", 1, 1, 0, 4); finishComposingText(); - waitAndVerifyStatesAndCalls(5, "hllo ", 1, 1, -1, -1); + waitAndVerifyStatesAndCalls(4, "hllo ", 1, 1, -1, -1); commitText("\n", 1); - waitAndVerifyStatesAndCalls(6, "h\nllo ", 2, 2, -1, -1); + waitAndVerifyStatesAndCalls(5, "h\nllo ", 2, 2, -1, -1); } /* @@ -402,18 +421,18 @@ // The calls below are a reflection of what the stock Google Keyboard (Andr // when the noted key is touched on screen. // H - expectUpdateStateCall(); + resetUpdateStateList(); setComposingText("h", 1); assertUpdateStateCall(1000); assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); // O - expectUpdateStateCall(); + resetUpdateStateList(); setComposingText("ho", 1); assertUpdateStateCall(1000); assertEquals("ho", mConnection.getTextBeforeCursor(9, 0)); - expectUpdateStateCall(); + resetUpdateStateList(); setComposingText("h", 1); assertUpdateStateCall(1000); setComposingRegion(0, 1); @@ -458,7 +477,7 @@ // that the test reflects reality. If this test breaks, it's possible that code has // changed and different calls need to be made instead. // "three" - expectUpdateStateCall(); + resetUpdateStateList(); setComposingText("three", 1); assertUpdateStateCall(1000); assertEquals("three", mConnection.getTextBeforeCursor(99, 0)); @@ -467,14 +486,14 @@ commitText("three", 1); commitText(" ", 1); setComposingText("word", 1); - expectUpdateStateCall(); + resetUpdateStateList(); assertUpdateStateCall(1000); assertEquals("three word", mConnection.getTextBeforeCursor(99, 0)); // "test" commitText("word", 1); commitText(" ", 1); - expectUpdateStateCall(); + resetUpdateStateList(); setComposingText("test", 1); assertUpdateStateCall(1000); assertEquals("three word test", mConnection.getTextBeforeCursor(99, 0)); @@ -487,19 +506,19 @@ final String smiley = "\uD83D\uDE0A"; commitText(smiley, 1); - waitAndVerifyStatesAndCalls(1, smiley, 2, 2, -1, -1); + waitAndVerifyStatesAndCalls(0, smiley, 2, 2, -1, -1); // DEL, sent via dispatchKeyEvent like it is in Android WebView or a physical keyboard. dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); - waitAndVerifyStatesAndCalls(2, "", 0, 0, -1, -1); + waitAndVerifyStatesAndCalls(1, "", 0, 0, -1, -1); // Make sure that we accept further typing after deleting the smiley. setComposingText("s", 1); - waitAndVerifyStatesAndCalls(3, "s", 1, 1, 0, 1); + waitAndVerifyStatesAndCalls(2, "s", 1, 1, 0, 1); setComposingText("sm", 1); - waitAndVerifyStatesAndCalls(4, "sm", 2, 2, 0, 2); + waitAndVerifyStatesAndCalls(3, "sm", 2, 2, 0, 2); } @SmallTest @@ -508,14 +527,14 @@ focusElement("textarea"); // H - expectUpdateStateCall(); + resetUpdateStateList(); commitText("h", 1); assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); assertUpdateStateCall(1000); assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); // O - expectUpdateStateCall(); + resetUpdateStateList(); commitText("o", 1); assertEquals("ho", mConnection.getTextBeforeCursor(9, 0)); assertUpdateStateCall(1000); @@ -526,7 +545,7 @@ dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); // DEL - expectUpdateStateCall(); + resetUpdateStateList(); assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); assertUpdateStateCall(1000); assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); @@ -538,14 +557,14 @@ focusElement("textarea"); // H - expectUpdateStateCall(); + resetUpdateStateList(); commitText("h", 1); assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); assertUpdateStateCall(1000); assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); // O - expectUpdateStateCall(); + resetUpdateStateList(); commitText("o", 1); assertEquals("ho", mConnection.getTextBeforeCursor(9, 0)); assertUpdateStateCall(1000); @@ -558,7 +577,7 @@ dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); // DEL - expectUpdateStateCall(); + resetUpdateStateList(); assertEquals("", mConnection.getTextBeforeCursor(9, 0)); assertUpdateStateCall(1000); assertEquals("", mConnection.getTextBeforeCursor(9, 0)); @@ -567,33 +586,34 @@ @SmallTest @Feature({"TextInput", "Main"}) public void testPhysicalKeyboard() throws Throwable { - focusElement("textarea"); + focusElementAndWaitForStateUpdate("textarea"); + // Type 'a' using a physical keyboard. dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A)); dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_A)); - waitAndVerifyStatesAndCalls(1, "a", 1, 1, -1, -1); + waitAndVerifyStatesAndCalls(0, "a", 1, 1, -1, -1); // Type 'enter' key. dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)); - waitAndVerifyStatesAndCalls(2, "a\n\n", 2, 2, -1, -1); + waitAndVerifyStatesAndCalls(1, "a\n\n", 2, 2, -1, -1); // Type 'b'. dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)); dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B)); - waitAndVerifyStatesAndCalls(3, "a\nb", 3, 3, -1, -1); + waitAndVerifyStatesAndCalls(2, "a\nb", 3, 3, -1, -1); } @SmallTest @Feature({"TextInput", "Main"}) public void testPhysicalKeyboard_AccentKeyCodes() throws Throwable { - focusElement("textarea"); + focusElementAndWaitForStateUpdate("textarea"); // h dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_H)); dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_H)); assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); - waitAndVerifyStatesAndCalls(1, "h", 1, 1, -1, -1); + waitAndVerifyStatesAndCalls(0, "h", 1, 1, -1, -1); // ALT-i (circumflex accent key on virtual keyboard) dispatchKeyEvent(new KeyEvent( @@ -603,7 +623,7 @@ dispatchKeyEvent(new KeyEvent( 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON)); assertEquals("hˆ", mConnection.getTextBeforeCursor(9, 0)); - waitAndVerifyStatesAndCalls(2, "hˆ", 2, 2, 1, 2); + waitAndVerifyStatesAndCalls(1, "hˆ", 2, 2, 1, 2); // o dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_O)); @@ -611,7 +631,7 @@ assertEquals("hô", mConnection.getTextBeforeCursor(9, 0)); dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_O)); assertEquals("hô", mConnection.getTextBeforeCursor(9, 0)); - waitAndVerifyStatesAndCalls(3, "hô", 2, 2, -1, -1); + waitAndVerifyStatesAndCalls(2, "hô", 2, 2, -1, -1); // ALT-i dispatchKeyEvent(new KeyEvent( @@ -620,7 +640,7 @@ dispatchKeyEvent(new KeyEvent( 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON)); assertEquals("hôˆ", mConnection.getTextBeforeCursor(9, 0)); - waitAndVerifyStatesAndCalls(4, "hôˆ", 3, 3, 2, 3); + waitAndVerifyStatesAndCalls(3, "hôˆ", 3, 3, 2, 3); // ALT-i again should have no effect dispatchKeyEvent(new KeyEvent( @@ -637,8 +657,8 @@ dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B)); assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0)); // A transitional state due to finishComposingText. - waitAndVerifyStates(5, "hôˆ", 3, 3, -1, -1); - waitAndVerifyStatesAndCalls(6, "hôˆb", 4, 4, -1, -1); + waitAndVerifyStates(4, "hôˆ", 3, 3, -1, -1); + waitAndVerifyStatesAndCalls(5, "hôˆb", 4, 4, -1, -1); // ALT-i dispatchKeyEvent(new KeyEvent( @@ -647,7 +667,7 @@ dispatchKeyEvent(new KeyEvent( 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON)); assertEquals("hôˆbˆ", mConnection.getTextBeforeCursor(9, 0)); - waitAndVerifyStatesAndCalls(7, "hôˆbˆ", 5, 5, 4, 5); + waitAndVerifyStatesAndCalls(6, "hôˆbˆ", 5, 5, 4, 5); // Backspace dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); @@ -656,8 +676,8 @@ dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0)); // A transitional state due to finishComposingText in deleteSurroundingTextImpl. - waitAndVerifyStates(8, "hôˆbˆ", 5, 5, -1, -1); - waitAndVerifyStatesAndCalls(9, "hôˆb", 4, 4, -1, -1); + waitAndVerifyStates(7, "hôˆbˆ", 5, 5, -1, -1); + waitAndVerifyStatesAndCalls(8, "hôˆb", 4, 4, -1, -1); } @SmallTest @@ -674,16 +694,38 @@ @SmallTest @Feature({"TextInput", "Main"}) public void testEnterKey_AfterCommitText() throws Throwable { - focusElement("textarea"); + focusElementAndWaitForStateUpdate("textarea"); commitText("hello", 1); - waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1); + waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)); // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed. // The second new line is not a user visible/editable one, it is a side-effect of Blink // using <br> internally. This only happens when \n is at the end. + waitAndVerifyStatesAndCalls(1, "hello\n\n", 6, 6, -1, -1); + + commitText("world", 1); + waitAndVerifyStatesAndCalls(2, "hello\nworld", 11, 11, -1, -1); + } + + @SmallTest + @Feature({"TextInput", "Main"}) + public void testEnterKey_WhileComposingText() throws Throwable { + focusElementAndWaitForStateUpdate("textarea"); + + setComposingText("hello", 1); + waitAndVerifyStatesAndCalls(0, "hello", 5, 5, 0, 5); + + dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); + dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)); + + // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed. + // A transitional state due to finishComposingText. + waitAndVerifyStates(1, "hello", 5, 5, -1, -1); + // The second new line is not a user visible/editable one, it is a side-effect of Blink + // using <br> internally. This only happens when \n is at the end. waitAndVerifyStatesAndCalls(2, "hello\n\n", 6, 6, -1, -1); commitText("world", 1); @@ -692,33 +734,11 @@ @SmallTest @Feature({"TextInput", "Main"}) - public void testEnterKey_WhileComposingText() throws Throwable { - focusElement("textarea"); - - setComposingText("hello", 1); - waitAndVerifyStatesAndCalls(1, "hello", 5, 5, 0, 5); - - dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); - dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)); - - // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed. - // A transitional state due to finishComposingText. - waitAndVerifyStates(2, "hello", 5, 5, -1, -1); - // The second new line is not a user visible/editable one, it is a side-effect of Blink - // using <br> internally. This only happens when \n is at the end. - waitAndVerifyStatesAndCalls(3, "hello\n\n", 6, 6, -1, -1); - - commitText("world", 1); - waitAndVerifyStatesAndCalls(4, "hello\nworld", 11, 11, -1, -1); - } - - @SmallTest - @Feature({"TextInput", "Main"}) public void testDpadKeyCodesWhileSwipingText() throws Throwable { focusElement("textarea"); // DPAD_CENTER should cause keyboard to appear - expectUpdateStateCall(); + resetUpdateStateList(); dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER)); assertUpdateStateCall(1000); } @@ -727,13 +747,13 @@ @Feature({"TextInput"}) public void testPastePopupShowAndHide() throws Throwable { commitText("hello", 1); - waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1); + waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); selectAll(); - waitAndVerifyStatesAndCalls(2, "hello", 0, 5, -1, -1); + waitAndVerifyStatesAndCalls(1, "hello", 0, 5, -1, -1); cut(); - waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1); + waitAndVerifyStatesAndCalls(2, "", 0, 0, -1, -1); DOMUtils.longPressNode(this, mContentViewCore, "input_text"); assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() { @@ -760,7 +780,7 @@ @Feature({"TextInput"}) public void testSelectionClearedOnKeyEvent() throws Throwable { commitText("hello", 1); - waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1); + waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); DOMUtils.clickNode(this, mContentViewCore, "input_text"); assertWaitForKeyboardStatus(true); @@ -785,6 +805,23 @@ assertTrue(mContentViewCore.hasSelection()); } + @MediumTest + @Feature({"TextInput"}) + public void testRestartInputWhileComposingText() throws Throwable { + setComposingText("abc", 1); + waitAndVerifyStatesAndCalls(0, "abc", 3, 3, 0, 3); + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + mConnection.restartInput(); + } + }); + // We don't do anything when input gets restarted. But we depend on Android's + // InputMethodManager and/or input methods to call finishComposingText() in setting + // current input connection as active or finishing the current input connection. + assertNoFurtherStateUpdate(1); + } + private void performGo(TestCallbackHelperContainer testCallbackHelperContainer) throws Throwable { final AdapterInputConnection inputConnection = mConnection; @@ -821,7 +858,7 @@ private void waitAndVerifyStates(final int index, String text, final int selectionStart, final int selectionEnd, final int compositionStart, final int compositionEnd) throws InterruptedException { - final ArrayList<TestImeState> states = mConnection.mImeUpdateQueue; + final List<TestImeState> states = mConnectionFactory.getImeStateList(); assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { @Override public boolean isSatisfied() { @@ -853,14 +890,12 @@ })); } - private void expectUpdateStateCall() { - final TestAdapterInputConnection connection = mConnection; - connection.mImeUpdateQueue.clear(); + private void resetUpdateStateList() { + mConnectionFactory.getImeStateList().clear(); } private void assertUpdateStateCall(int maxms) throws Exception { - final TestAdapterInputConnection connection = mConnection; - while (connection.mImeUpdateQueue.size() == 0 && maxms > 0) { + while (mConnectionFactory.getImeStateList().size() == 0 && maxms > 0) { try { Thread.sleep(50); } catch (Exception e) { @@ -868,7 +903,7 @@ } maxms -= 50; } - assertTrue(connection.mImeUpdateQueue.size() > 0); + assertTrue(mConnectionFactory.getImeStateList().size() > 0); } private void assertClipboardContents(final Activity activity, final String expectedContents) @@ -1012,6 +1047,17 @@ }); } + /** + * Focus element, wait for a single state update, reset state update list. + * @param id ID of the element to focus. + */ + private void focusElementAndWaitForStateUpdate(String id) + throws InterruptedException, TimeoutException { + focusElement(id); + waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1); + resetUpdateStateList(); + } + private void focusElement(final String id) throws InterruptedException, TimeoutException { DOMUtils.focusNode(mWebContents, id); assertWaitForKeyboardStatus(true); @@ -1027,31 +1073,38 @@ })); // When we focus another element, the connection may be recreated. mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); - waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1); } private static class TestAdapterInputConnectionFactory extends ImeAdapter.AdapterInputConnectionFactory { + private final List<TestImeState> mImeStateList = new ArrayList<>(); + @Override public AdapterInputConnection get(View view, ImeAdapter imeAdapter, Editable editable, EditorInfo outAttrs) { - return new TestAdapterInputConnection(view, imeAdapter, editable, outAttrs); + return new TestAdapterInputConnection( + mImeStateList, view, imeAdapter, editable, outAttrs); + } + + public List<TestImeState> getImeStateList() { + return mImeStateList; } } private static class TestAdapterInputConnection extends AdapterInputConnection { - private final ArrayList<TestImeState> mImeUpdateQueue = new ArrayList<TestImeState>(); + private final List<TestImeState> mImeStateList; - public TestAdapterInputConnection(View view, ImeAdapter imeAdapter, - Editable editable, EditorInfo outAttrs) { + public TestAdapterInputConnection(List<TestImeState> imeStateList, View view, + ImeAdapter imeAdapter, Editable editable, EditorInfo outAttrs) { super(view, imeAdapter, editable, outAttrs); + mImeStateList = imeStateList; } @Override public void updateState(String text, int selectionStart, int selectionEnd, int compositionStart, int compositionEnd, boolean requiredAck) { - mImeUpdateQueue.add(new TestImeState(text, selectionStart, selectionEnd, - compositionStart, compositionEnd)); + mImeStateList.add(new TestImeState( + text, selectionStart, selectionEnd, compositionStart, compositionEnd)); super.updateState(text, selectionStart, selectionEnd, compositionStart, compositionEnd, requiredAck); }
diff --git a/content/public/browser/native_web_keyboard_event.h b/content/public/browser/native_web_keyboard_event.h index 8c34c708..4bbb119 100644 --- a/content/public/browser/native_web_keyboard_event.h +++ b/content/public/browser/native_web_keyboard_event.h
@@ -7,10 +7,10 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/strings/string16.h" #include "build/build_config.h" #include "content/common/content_export.h" #include "third_party/WebKit/public/web/WebInputEvent.h" -#include "ui/events/event_constants.h" #include "ui/gfx/native_widget_types.h" namespace ui { @@ -46,11 +46,8 @@ #else explicit NativeWebKeyboardEvent(const ui::KeyEvent& key_event); #if defined(USE_AURA) - NativeWebKeyboardEvent(ui::EventType type, - bool is_char, - wchar_t character, - int state, - double time_stamp_seconds); + // Create a legacy keypress event specified by |character|. + NativeWebKeyboardEvent(const ui::KeyEvent& key_event, base::char16 character); #endif #endif
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc index 5ab4629..b59bb46 100644 --- a/content/renderer/gpu/render_widget_compositor.cc +++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -66,7 +66,6 @@ class Layer; } -using blink::WebBeginFrameArgs; using blink::WebFloatPoint; using blink::WebRect; using blink::WebSelection; @@ -859,13 +858,9 @@ } void RenderWidgetCompositor::BeginMainFrame(const cc::BeginFrameArgs& args) { - double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF(); - double deadline_sec = (args.deadline - base::TimeTicks()).InSecondsF(); - double interval_sec = args.interval.InSecondsF(); - WebBeginFrameArgs web_begin_frame_args = - WebBeginFrameArgs(frame_time_sec, deadline_sec, interval_sec); compositor_deps_->GetRendererScheduler()->WillBeginFrame(args); - widget_->webwidget()->beginFrame(web_begin_frame_args); + double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF(); + widget_->webwidget()->beginFrame(frame_time_sec); } void RenderWidgetCompositor::BeginMainFrameNotExpectedSoon() {
diff --git a/content/renderer/gpu/render_widget_compositor_unittest.cc b/content/renderer/gpu/render_widget_compositor_unittest.cc index f252c11..18e48a5a 100644 --- a/content/renderer/gpu/render_widget_compositor_unittest.cc +++ b/content/renderer/gpu/render_widget_compositor_unittest.cc
@@ -27,7 +27,7 @@ class MockWebWidget : public blink::WebWidget { public: - MOCK_METHOD1(beginFrame, void(const blink::WebBeginFrameArgs& args)); + MOCK_METHOD1(beginFrame, void(double lastFrameTimeMonotonic)); }; class TestRenderWidget : public RenderWidget { @@ -80,11 +80,7 @@ cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, interval, cc::BeginFrameArgs::NORMAL)); - EXPECT_CALL(render_widget_->mock_webwidget_, - beginFrame(AllOf( - Field(&blink::WebBeginFrameArgs::lastFrameTimeMonotonic, 1), - Field(&blink::WebBeginFrameArgs::deadline, 2), - Field(&blink::WebBeginFrameArgs::interval, 3)))); + EXPECT_CALL(render_widget_->mock_webwidget_, beginFrame(1)); render_widget_compositor_->BeginMainFrame(args); }
diff --git a/extensions/renderer/file_system_natives.cc b/extensions/renderer/file_system_natives.cc index 67b5d13..d3b23af 100644 --- a/extensions/renderer/file_system_natives.cc +++ b/extensions/renderer/file_system_natives.cc
@@ -11,7 +11,6 @@ #include "storage/common/fileapi/file_system_types.h" #include "storage/common/fileapi/file_system_util.h" #include "third_party/WebKit/public/platform/WebString.h" -#include "third_party/WebKit/public/web/WebDOMError.h" #include "third_party/WebKit/public/web/WebDOMFileSystem.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" @@ -28,9 +27,6 @@ RouteFunction("CrackIsolatedFileSystemName", base::Bind(&FileSystemNatives::CrackIsolatedFileSystemName, base::Unretained(this))); - RouteFunction( - "GetDOMError", - base::Bind(&FileSystemNatives::GetDOMError, base::Unretained(this))); } void FileSystemNatives::GetIsolatedFileSystem( @@ -124,34 +120,4 @@ filesystem_id.size())); } -void FileSystemNatives::GetDOMError( - const v8::FunctionCallbackInfo<v8::Value>& args) { - if (args.Length() != 2) { - NOTREACHED(); - return; - } - if (!args[0]->IsString()) { - NOTREACHED(); - return; - } - if (!args[1]->IsString()) { - NOTREACHED(); - return; - } - - std::string name(*v8::String::Utf8Value(args[0])); - if (name.empty()) { - NOTREACHED(); - return; - } - std::string message(*v8::String::Utf8Value(args[1])); - // message is optional hence empty is fine. - - blink::WebDOMError dom_error = blink::WebDOMError::create( - blink::WebString::fromUTF8(name), blink::WebString::fromUTF8(message)); - args.GetReturnValue().Set( - dom_error.toV8Value(context()->v8_context()->Global(), - args.GetIsolate())); -} - } // namespace extensions
diff --git a/extensions/renderer/file_system_natives.h b/extensions/renderer/file_system_natives.h index 8637326..ed6970c 100644 --- a/extensions/renderer/file_system_natives.h +++ b/extensions/renderer/file_system_natives.h
@@ -21,8 +21,6 @@ void GetIsolatedFileSystem(const v8::FunctionCallbackInfo<v8::Value>& args); void CrackIsolatedFileSystemName( const v8::FunctionCallbackInfo<v8::Value>& args); - // Constructs a DOMError object to be used in JavaScript. - void GetDOMError(const v8::FunctionCallbackInfo<v8::Value>& args); DISALLOW_COPY_AND_ASSIGN(FileSystemNatives); };
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc index b6533a2..0f4fe4b 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.cc +++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -1210,6 +1210,23 @@ IsSignedIntegerFormat(internal_format)); } +// static +bool GLES2Util::IsFloatFormat(uint32_t internal_format) { + switch (internal_format) { + case GL_R16F: + case GL_R32F: + case GL_RG16F: + case GL_RG32F: + case GL_R11F_G11F_B10F: + case GL_RGB16F: + case GL_RGB32F: + case GL_RGBA16F: + case GL_RGBA32F: + return true; + default: + return false; + } +} namespace {
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h index b29f96b..aea4c40 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.h +++ b/gpu/command_buffer/common/gles2_cmd_utils.h
@@ -190,6 +190,7 @@ static bool IsUnsignedIntegerFormat(uint32_t internal_format); static bool IsSignedIntegerFormat(uint32_t internal_format); static bool IsIntegerFormat(uint32_t internal_format); + static bool IsFloatFormat(uint32_t internal_format); #include "../common/gles2_cmd_utils_autogen.h"
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 10550fa5..c8ffe3a 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -1567,6 +1567,9 @@ // attached. Generates GL error if not. bool CheckBoundReadFramebufferColorAttachment(const char* func_name); + // Infer color encoding from internalformat + static GLint GetColorEncodingFromInternalFormat(GLenum internalformat); + // Check that the currently bound read framebuffer's color image // isn't the target texture of the glCopyTex{Sub}Image2D. bool FormsTextureCopyingFeedbackLoop(TextureRef* texture, GLint level); @@ -4019,6 +4022,19 @@ return true; } +GLint GLES2DecoderImpl::GetColorEncodingFromInternalFormat( + GLenum internalformat) { + switch (internalformat) { + case GL_SRGB_EXT: + case GL_SRGB_ALPHA_EXT: + case GL_SRGB8: + case GL_SRGB8_ALPHA8: + return GL_SRGB; + default: + return GL_LINEAR; + } +} + bool GLES2DecoderImpl::FormsTextureCopyingFeedbackLoop( TextureRef* texture, GLint level) { Framebuffer* framebuffer = features().chromium_framebuffer_multisample ? @@ -11003,6 +11019,20 @@ return; } + if (feature_info_->IsES3Enabled()) { + GLint color_encoding = GetColorEncodingFromInternalFormat(read_format); + if (color_encoding != GetColorEncodingFromInternalFormat(internal_format) || + GLES2Util::IsFloatFormat(internal_format) || + (GLES2Util::IsSignedIntegerFormat(internal_format) != + GLES2Util::IsSignedIntegerFormat(read_format)) || + (GLES2Util::IsUnsignedIntegerFormat(internal_format) != + GLES2Util::IsUnsignedIntegerFormat(read_format))) { + LOCAL_SET_GL_ERROR( + GL_INVALID_OPERATION, "glCopyTexImage2D", "incompatible format"); + return; + } + } + if ((channels_needed & (GLES2Util::kDepth | GLES2Util::kStencil)) != 0) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc index e1acdebe..f50e030 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
@@ -1982,6 +1982,153 @@ EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); } +TEST_P(GLES3DecoderTest, CopyTexImage2DInvalidInternalFormat_Float) { + const GLuint kFBOClientTextureId = 4100; + const GLuint kFBOServiceTextureId = 4101; + + GLenum target = GL_TEXTURE_2D; + GLint level = 0; + GLenum internal_format = GL_RG16F; + GLenum format = GL_RG; + GLenum type = GL_HALF_FLOAT; + GLsizei width = 16; + GLsizei height = 8; + GLint border = 0; + + EXPECT_CALL(*gl_, GenTextures(_, _)) + .WillOnce(SetArgPointee<1>(kFBOServiceTextureId)) + .RetiresOnSaturation(); + GenHelper<GenTexturesImmediate>(kFBOClientTextureId); + + DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId); + DoTexImage2D(GL_TEXTURE_2D, + level, + internal_format, + width, + height, + 0, + format, + type, + kSharedMemoryId, + kSharedMemoryOffset); + DoBindFramebuffer( + GL_READ_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D(GL_READ_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + kFBOClientTextureId, + kFBOServiceTextureId, + 0, + GL_NO_ERROR); + EXPECT_CALL(*gl_, + CopyTexImage2D( + target, level, internal_format, 0, 0, width, height, border)) + .Times(0); + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + CopyTexImage2D cmd; + cmd.Init(target, level, internal_format, 0, 0, width, height); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +TEST_P(GLES3DecoderTest, CopyTexImage2DInvalidInternalFormat_Integer) { + const GLuint kFBOClientTextureId = 4100; + const GLuint kFBOServiceTextureId = 4101; + + GLenum target = GL_TEXTURE_2D; + GLint level = 0; + GLenum internal_format = GL_RG8I; + GLenum format = GL_RG_INTEGER; + GLenum type = GL_UNSIGNED_BYTE; + GLsizei width = 16; + GLsizei height = 8; + GLint border = 0; + + EXPECT_CALL(*gl_, GenTextures(_, _)) + .WillOnce(SetArgPointee<1>(kFBOServiceTextureId)) + .RetiresOnSaturation(); + GenHelper<GenTexturesImmediate>(kFBOClientTextureId); + + DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId); + DoTexImage2D(GL_TEXTURE_2D, + level, + GL_RG8UI, + width, + height, + 0, + format, + type, + kSharedMemoryId, + kSharedMemoryOffset); + DoBindFramebuffer( + GL_READ_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D(GL_READ_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + kFBOClientTextureId, + kFBOServiceTextureId, + 0, + GL_NO_ERROR); + EXPECT_CALL(*gl_, + CopyTexImage2D( + target, level, internal_format, 0, 0, width, height, border)) + .Times(0); + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + CopyTexImage2D cmd; + cmd.Init(target, level, internal_format, 0, 0, width, height); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +TEST_P(GLES3DecoderTest, CopyTexImage2DInvalidInternalFormat_sRGB) { + const GLuint kFBOClientTextureId = 4100; + const GLuint kFBOServiceTextureId = 4101; + + GLenum target = GL_TEXTURE_2D; + GLint level = 0; + GLenum internal_format = GL_SRGB8; + GLenum format = GL_RGB; + GLenum type = GL_UNSIGNED_BYTE; + GLsizei width = 16; + GLsizei height = 8; + GLint border = 0; + + EXPECT_CALL(*gl_, GenTextures(_, _)) + .WillOnce(SetArgPointee<1>(kFBOServiceTextureId)) + .RetiresOnSaturation(); + GenHelper<GenTexturesImmediate>(kFBOClientTextureId); + + DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId); + DoTexImage2D(GL_TEXTURE_2D, + level, + GL_RGB, + width, + height, + 0, + format, + type, + kSharedMemoryId, + kSharedMemoryOffset); + DoBindFramebuffer( + GL_READ_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D(GL_READ_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + kFBOClientTextureId, + kFBOServiceTextureId, + 0, + GL_NO_ERROR); + EXPECT_CALL(*gl_, + CopyTexImage2D( + target, level, internal_format, 0, 0, width, height, border)) + .Times(0); + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + CopyTexImage2D cmd; + cmd.Init(target, level, internal_format, 0, 0, width, height); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + TEST_P(GLES2DecoderManualInitTest, UnClearedAttachmentsGetClearedOnReadPixelsAndDrawBufferGetsRestored) { InitState init;
diff --git a/gpu/perftests/measurements.cc b/gpu/perftests/measurements.cc index b2dac913305..645784d 100644 --- a/gpu/perftests/measurements.cc +++ b/gpu/perftests/measurements.cc
@@ -57,6 +57,7 @@ DCHECK(gpu_timing_client); wall_time_start_ = base::TraceTicks::Now(); if (base::ThreadTicks::IsSupported()) { + base::ThreadTicks::WaitUntilInitialized(); cpu_time_start_ = base::ThreadTicks::Now(); } else { static bool logged_once = false;
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index cf1d7a2..0549198 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg
@@ -44,7 +44,10 @@ builders { name: "linux_chromium_chromeos_compile_dbg_ng" } builders { name: "linux_chromium_chromeos_ozone_rel_ng" } builders { name: "linux_chromium_chromeos_rel_ng" } - builders { name: "linux_chromium_clobber_rel_ng" } + builders { + name: "linux_chromium_clobber_rel_ng" + experiment_percentage: 100 + } builders { name: "linux_chromium_compile_dbg_32_ng" } builders { name: "linux_chromium_compile_dbg_ng" } builders { name: "linux_chromium_gn_chromeos_rel" }
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests index 3cac00f..982b17ac 100644 --- a/third_party/WebKit/LayoutTests/NeverFixTests +++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -191,6 +191,7 @@ [ XP ] imported/csswg-test/css-writing-modes-3/text-orientation-script-001m.html [ WontFix ] [ XP ] imported/csswg-test/css-writing-modes-3/text-orientation-script-001n.html [ WontFix ] [ XP ] imported/csswg-test/css-writing-modes-3/text-orientation-script-001o.html [ WontFix ] +[ XP ] imported/csswg-test/css-writing-modes-3/vertical-alignment-vrl-026.xht [ WontFix ] # Relies on precise timing which isn't reliable on XP. [ XP ] imported/web-platform-tests/user-timing/test_user_timing_mark.html [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 46f5c52..eccd88ac 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -61,6 +61,16 @@ crbug.com/542643 fast/text/zero-font-size-2.html [ Pass Crash ] +crbug.com/541598 fast/repaint/selected-replaced.html [ NeedsRebaseline ] +crbug.com/541598 svg/custom/mouse-move-on-svg-container.xhtml [ NeedsRebaseline ] +crbug.com/541598 svg/custom/mouse-move-on-svg-root.xhtml [ NeedsRebaseline ] +crbug.com/541598 svg/custom/pattern-userSpaceOnUse-userToBaseTransform.xhtml [ NeedsRebaseline ] +crbug.com/541598 svg/hixie/mixed/010.xml [ NeedsRebaseline ] +crbug.com/541598 svg/repaint/add-background-property-on-root.html [ NeedsRebaseline ] +crbug.com/541598 svg/repaint/change-background-color.html [ NeedsRebaseline ] +crbug.com/541598 svg/repaint/remove-background-property-on-root.html [ NeedsRebaseline ] +crbug.com/541598 svg/text/small-fonts-in-html5.html [ NeedsRebaseline ] + crbug.com/539226 [ Win7 ] http/tests/xmlhttprequest/open-in-body-onload-sync-to-invalid-cross-origin-response-handling.html [ Timeout ] crbug.com/539226 [ Win7 ] http/tests/xmlhttprequest/open-in-body-onload-sync-to-invalid-redirect-response-handling.html [ Timeout ] crbug.com/539226 [ Win7 ] http/tests/xmlhttprequest/open-in-body-onload-sync-to-invalid-preflight-handling.html [ Timeout ] @@ -623,8 +633,6 @@ crbug.com/492664 imported/csswg-test/css-writing-modes-3/text-combine-upright-value-all-002.html [ ImageOnlyFailure ] crbug.com/492664 imported/csswg-test/css-writing-modes-3/text-combine-upright-value-all-003.html [ ImageOnlyFailure ] -crbug.com/543008 [ XP ] imported/csswg-test/css-writing-modes-3/vertical-alignment-vrl-026.xht [ ImageOnlyFailure ] - # These CSS Writing Modes Level 3 tests pass but causes 1px diff on images, notified to the submitter. crbug.com/492664 [ Mac ] imported/csswg-test/css-writing-modes-3/block-flow-direction-001.xht [ ImageOnlyFailure ] crbug.com/492664 [ Mac ] imported/csswg-test/css-writing-modes-3/block-flow-direction-002.xht [ ImageOnlyFailure ] @@ -1656,6 +1664,10 @@ crbug.com/492511 [ Mac ] fast/text/atsui-spacing-features.html [ Failure ImageOnlyFailure ] crbug.com/492511 [ Mac ] fast/text/international/arabic-justify.html [ Failure ImageOnlyFailure ] +crbug.com/541544 fast/layers/scroll-descendant-with-cached-cliprects.html [ NeedsRebaseline ] +crbug.com/541544 fast/repaint/create-layer-repaint.html [ NeedsRebaseline ] +crbug.com/541544 fast/repaint/static-to-positioned.html [ NeedsRebaseline ] + # Ref tests that fail due to differences in inline box structure, even though they contain the same text. # This happens because inline box layout uses fixed-point measurements, which can cause rounding differences. crbug.com/321237 [ Mac ] fast/dom/shadow/shadow-insertion-point-rendering-multiple-shadow-roots.html [ ImageOnlyFailure ] @@ -1835,6 +1847,12 @@ crbug.com/509025 [ Yosemite ] virtual/rootlayerscrolls/scrollbars/scrollbars-on-positioned-content.html [ ImageOnlyFailure ] crbug.com/509025 [ Yosemite ] virtual/rootlayerscrolls/scrollbars/short-scrollbar.html [ ImageOnlyFailure ] +crbug.com/472084 editing/execCommand/5136770.html [ NeedsRebaseline ] +crbug.com/472084 editing/execCommand/5190926.html [ NeedsRebaseline ] +crbug.com/472084 editing/execCommand/indent-selection.html [ NeedsRebaseline ] +crbug.com/472084 fast/backgrounds/selection-background-color-of-list-style.html [ NeedsRebaseline ] +crbug.com/472084 fast/lists/markers-in-selection.html [ NeedsRebaseline ] + crbug.com/443596 media/sources-fallback-codecs.html [ Pass Failure ] crbug.com/464736 http/tests/xmlhttprequest/ontimeout-event-override-after-failure.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/compositing/repaint/should-not-repaint-composited-descendants-on-overflow-change-expected.txt b/third_party/WebKit/LayoutTests/compositing/repaint/should-not-repaint-composited-descendants-on-overflow-change-expected.txt index 4fa25150..2e2b15a 100644 --- a/third_party/WebKit/LayoutTests/compositing/repaint/should-not-repaint-composited-descendants-on-overflow-change-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/repaint/should-not-repaint-composited-descendants-on-overflow-change-expected.txt
@@ -7,7 +7,6 @@ "drawsContent": true, "repaintRects": [ [0, 200, 200, 200], - [0, 200, 200, 200], [0, 0, 200, 200] ], "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/css3/flexbox/repaint-opacity-change-expected.txt b/third_party/WebKit/LayoutTests/css3/flexbox/repaint-opacity-change-expected.txt index 90a384b..d89acd27 100644 --- a/third_party/WebKit/LayoutTests/css3/flexbox/repaint-opacity-change-expected.txt +++ b/third_party/WebKit/LayoutTests/css3/flexbox/repaint-opacity-change-expected.txt
@@ -7,7 +7,6 @@ "drawsContent": true, "repaintRects": [ [18, 18, 764, 20], - [18, 18, 764, 20], [18, 18, 764, 20] ], "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/copy-cut-paste-supported.html b/third_party/WebKit/LayoutTests/editing/pasteboard/copy-cut-paste-supported.html new file mode 100644 index 0000000..dc1f15b --- /dev/null +++ b/third_party/WebKit/LayoutTests/editing/pasteboard/copy-cut-paste-supported.html
@@ -0,0 +1,10 @@ +<!doctype html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script> +test(function() { + assert_true(document.queryCommandSupported('copy')); + assert_true(document.queryCommandSupported('cut')); + assert_true(document.queryCommandSupported('paste')); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-z-index-change-repaint-expected.txt b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-z-index-change-repaint-expected.txt index c5cd006..8b7c25e 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-z-index-change-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-z-index-change-repaint-expected.txt
@@ -7,7 +7,6 @@ "drawsContent": true, "repaintRects": [ [8, 236, 200, 100], - [8, 236, 200, 100], [8, 126, 200, 100] ], "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-transform-value.html b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-transform-value.html new file mode 100644 index 0000000..c7c6746b --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-transform-value.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> + +<style> +div { + height: 200px; + width: 200px; + position: absolute; +} +</style> + +<div id="box1" style="left: 100px; top: 200px; transform: scale(1.5, 0.75)"></div> +<div id="box2" style="left: 400px; top: 200px; transform: rotate(30deg)"></div> +<div id="box3" style="left: 100px; top: 400px; transform: translate(50px, 80px)"></div> +<div id="box4" style="left: 400px; top: 400px; transform: translate(10px, 50px) scale(0.8) rotate(-10deg)"></div> + +<script> +test(function() { + assert_equals(getComputedStyle(box1).webkitTransform, "matrix(1.5, 0, 0, 0.75, 0, 0)"); + assert_equals(getComputedStyle(box2).webkitTransform, "matrix(0.866025, 0.5, -0.5, 0.866025, 0, 0)"); + assert_equals(getComputedStyle(box3).webkitTransform, "matrix(1, 0, 0, 1, 50, 80)"); + assert_equals(getComputedStyle(box4).webkitTransform, "matrix(0.787846, -0.138919, 0.138919, 0.787846, 10, 50)"); +}, "Testing getComputedStyle(element).webkitTransform"); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all-expected.html b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all-expected.html index dcf8e0c8..3eeb465 100644 --- a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all-expected.html +++ b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all-expected.html
@@ -15,4 +15,5 @@ <p>MM0<br>0=== <p>M 0<br>0=== <p lang="ja">漢漢漢<br>漢。 + <p>======= </div>
diff --git a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all.html b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all.html index 2a2f52c..05223d1 100644 --- a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all.html +++ b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all.html
@@ -30,4 +30,5 @@ <p>MM00=== <p>M 00=== <p lang="ja">漢漢漢漢。 + <p>======= </div>
diff --git a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl-expected.txt b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl-expected.txt new file mode 100644 index 0000000..78a2d15 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl-expected.txt
@@ -0,0 +1,4 @@ +There should be only one line of arabic below. + +اب و عدم وابستگی. +PASS
diff --git a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl.html b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl.html new file mode 100644 index 0000000..c3ed15d --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl.html
@@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<style> +div { + direction: rtl; + line-height: 20px; + border: 1px solid black; + display: inline-block; +} +</style> +<p>There should be only one line of arabic below.</p> +<div id="test" style="word-break: break-all" data-expected-height=22>اب و عدم وابستگی.</div> +<script src="../../../resources/check-layout.js"></script> +<script> + checkLayout("#test"); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/select/menulist-remove-option-onchange.html b/third_party/WebKit/LayoutTests/fast/forms/select/menulist-remove-option-onchange.html index d10dff1..4110f2d 100644 --- a/third_party/WebKit/LayoutTests/fast/forms/select/menulist-remove-option-onchange.html +++ b/third_party/WebKit/LayoutTests/fast/forms/select/menulist-remove-option-onchange.html
@@ -8,6 +8,12 @@ <option>b</option> <option>c</option> </select> +<select id="select2"> +<option value="">Placeholder</option> +<option>a</option> +<option>b</option> +<option>c</option> +</select> <script> var select = document.querySelector('select'); var placeholderOption = document.querySelector('option'); @@ -32,5 +38,32 @@ eventSender.keyDown('c'); assert_equals(selectedValue, 'c'); }, 'Change event should be dispatched after the option removal.'); + +selectedValue = null; +var select2 = document.querySelector('#select2'); +select2.focus(); +select2.addEventListener('change', function() { + selectedValue = select2.value; + if (select2.options[0].value == '') { + var options = select2.options; + for (var i = 0; i < options.length - 1; ++i) { + options[i].label = options[i + 1].label; + options[i].value = options[i + 1].value; + options[i].textContent = options[i + 1].textContent; + } + select2.removeChild(options[options.length - 1]); + select2.value = selectedValue; + } +}); +test(function() { + eventSender.keyDown('a'); + assert_equals(select2.selectedIndex, 0); + assert_equals(selectedValue, 'a'); + internals.resetTypeAheadSession(select2); + + eventSender.keyDown('b'); + assert_equals(select2.selectedIndex, 1); + assert_equals(selectedValue, 'b'); +}, 'Change event should be dispatched after updating selection programatically.'); </script> </body>
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/change-transform-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/change-transform-expected.txt index c9ee0ff3..3d5fe310 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/change-transform-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/repaint/change-transform-expected.txt
@@ -7,7 +7,6 @@ "drawsContent": true, "repaintRects": [ [35, 35, 50, 50], - [10, 10, 100, 100], [10, 10, 100, 100] ], "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/erase-overflow-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/erase-overflow-expected.txt index 00a5f7db..032f086 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/erase-overflow-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/repaint/erase-overflow-expected.txt
@@ -8,7 +8,6 @@ "repaintRects": [ [8, 108, 100, 100], [8, 8, 100, 200], - [8, 8, 100, 200], [8, 8, 100, 100] ], "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/mix-blend-mode-separate-stacking-context-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/mix-blend-mode-separate-stacking-context-expected.txt index 8837d253..dc21d3b 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/mix-blend-mode-separate-stacking-context-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/repaint/mix-blend-mode-separate-stacking-context-expected.txt
@@ -8,7 +8,6 @@ "repaintRects": [ [348, 48, 60, 60], [328, 28, 60, 60], - [328, 28, 60, 60], [248, 48, 60, 60], [188, 28, 20, 80], [148, 48, 60, 60], @@ -16,7 +15,6 @@ [128, 88, 80, 20], [88, 28, 20, 80], [48, 48, 60, 60], - [48, 48, 60, 60], [28, 88, 80, 20] ], "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/paint-invalidation-with-opacity-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/paint-invalidation-with-opacity-expected.txt index af3f2ac7..7596237 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/paint-invalidation-with-opacity-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/repaint/paint-invalidation-with-opacity-expected.txt
@@ -6,7 +6,6 @@ "contentsOpaque": true, "drawsContent": true, "repaintRects": [ - [8, 8, 100, 100], [8, 8, 100, 100] ], "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt new file mode 100644 index 0000000..7b8f437 --- /dev/null +++ b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt
@@ -0,0 +1,57 @@ +{ + "bounds": [800, 600], + "children": [ + { + "bounds": [800, 600], + "contentsOpaque": true, + "drawsContent": true, + "repaintRects": [ + [1, 1, 100, 100], + [1, 1, 100, 100] + ], + "paintInvalidationClients": [ + "LayoutBlockFlow DIV id='target'", + "LayoutBlockFlow DIV id='child'" + ], + "children": [ + { + "position": [8, 8] + }, + { + "shouldFlattenTransform": false, + "children": [ + { + "position": [8, 8], + "bounds": [784, 52], + "drawsContent": true, + "repaintRects": [ + [1, 1, 100, 100], + [1, 1, 100, 100], + [0, 52, 784, 49] + ], + "paintInvalidationClients": [ + "LayoutBlockFlow DIV id='target'", + "LayoutBlockFlow DIV id='child'", + "LayoutBlockFlow (relative positioned) DIV" + ] + }, + { + "position": [9, 9], + "bounds": [100, 100], + "drawsContent": true, + "repaintRects": [ + [0, 0, 100, 100], + [0, 0, 100, 100] + ], + "paintInvalidationClients": [ + "LayoutBlockFlow DIV id='target'", + "LayoutBlockFlow DIV id='child'" + ] + } + ] + } + ] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer.html b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer.html new file mode 100644 index 0000000..69ca613 --- /dev/null +++ b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<script src="../../fast/repaint/resources/text-based-repaint.js"></script> +<script> +function repaintTest() { + target.style.opacity = 0.2; +} +onload = runRepaintTest; +</script> +<!--Tests paint invalidation on a non-stacking-context squashing layer when a sublayer is created +and the descendants of the sublayer changes paint invalidation container. Passes if we invalidate +the descendants of the layer on the squashing layer.--> +<div style="will-change: transform; position: fixed"></div> +<div style="position: relative; border: 1px solid black;"> + <div id="target" style="opacity: 1; height: 50px; width: 50px;"> + <div id="child" style="width: 100px; height: 100px"></div> + </div> +</div>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/layers/scroll-descendant-with-cached-cliprects-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/layers/scroll-descendant-with-cached-cliprects-expected.txt index c41e0bb..5165e030 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/layers/scroll-descendant-with-cached-cliprects-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/layers/scroll-descendant-with-cached-cliprects-expected.txt
@@ -13,8 +13,7 @@ [677, 52, 100, 100], [677, 52, 100, 100], [677, 52, 100, 100], - [677, 52, 100, 100], - [0, 0, 100, 100] + [677, 52, 100, 100] ], "paintInvalidationClients": [ "LayoutBlockFlow (positioned) DIV id='scrollpanel'",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/create-layer-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/create-layer-repaint-expected.txt index 2d60bf0..4045257 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/create-layer-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/create-layer-repaint-expected.txt
@@ -7,7 +7,6 @@ "drawsContent": true, "repaintRects": [ [28, 56, 500, 50], - [28, 56, 500, 50], [28, 56, 250, 50] ], "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/static-to-positioned-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/static-to-positioned-expected.txt index 998e1428..b9472ba 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/static-to-positioned-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/static-to-positioned-expected.txt
@@ -8,9 +8,7 @@ "repaintRects": [ [8, 332, 100, 100], [8, 282, 100, 100], - [8, 282, 100, 100], [8, 132, 100, 100], - [8, 82, 100, 100], [8, 82, 100, 100] ], "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/virtual/spv2/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt b/third_party/WebKit/LayoutTests/virtual/spv2/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt new file mode 100644 index 0000000..1b0274b --- /dev/null +++ b/third_party/WebKit/LayoutTests/virtual/spv2/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt
@@ -0,0 +1,22 @@ +{ + "bounds": [800, 600], + "children": [ + { + "bounds": [800, 600], + "contentsOpaque": true, + "drawsContent": true, + "repaintRects": [ + [9, 9, 100, 100], + [9, 9, 100, 100], + [8, 8, 784, 101], + [8, 8, 784, 52] + ], + "paintInvalidationClients": [ + "LayoutBlockFlow (relative positioned) DIV", + "LayoutBlockFlow DIV id='target'", + "LayoutBlockFlow DIV id='child'" + ] + } + ] +} +
diff --git a/third_party/WebKit/ManualTests/computed-transform-value.html b/third_party/WebKit/ManualTests/computed-transform-value.html deleted file mode 100644 index cea435c..0000000 --- a/third_party/WebKit/ManualTests/computed-transform-value.html +++ /dev/null
@@ -1,49 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"> - -<html lang="en"> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - <title>Get Computed Transform</title> - <style type="text/css" media="screen"> - .box { - height: 200px; - width: 200px; - background-color: #00a0a0; - } - </style> - <script type="text/javascript" charset="utf-8"> - function printTransform(event) - { - var box = event.target; - var computedTransform = window.getComputedStyle(box).webkitTransform; - document.getElementById("output").innerHTML = 'Computed transform is ' + computedTransform; - } - - </script> -</head> -<body onclick="printTransform(event)"> - -<h1>Testing transform computed style</h1> -<p>All boxes are 200x200 pixels. When you click on an element, the computed transform style will be displayed below</p> -<p id="output">Computed style for transform displayed here</p> - - -<div class="box" style="position: absolute; left: 100px; top: 200px; transform: scale(1.5, 0.75)"> - scale(1.5, 0.75) -</div> - -<div class="box" style="position: absolute; left: 400px; top: 200px; transform: rotate(30deg)"> - rotate(30deg) -</div> - -<div class="box" style="position: absolute; left: 100px; top: 400px; transform: translate(50px, 80px)"> - translate(50px, 80px) -</div> - -<div class="box" style="position: absolute; left: 400px; top: 400px; transform: translate(10px, 50px) scale(0.8) rotate(-10deg)"> - translate(10px, 50px) scale(0.8) rotate(-10deg) -</div> - -</body> -</html>
diff --git a/third_party/WebKit/ManualTests/scroll-after-layer-removal.html b/third_party/WebKit/ManualTests/scroll-after-layer-removal.html new file mode 100644 index 0000000..9ba5dfbe --- /dev/null +++ b/third_party/WebKit/ManualTests/scroll-after-layer-removal.html
@@ -0,0 +1,18 @@ +<!DOCTYPE html> +<style> +#scroll { + width: 100px; + height: 100px; + overflow: scroll; +} +.trans { will-change: transform; } +#space { height: 1000px; } +</style> +<p>This test verifies that cc does not intercept scroll events after a scroller is un-promoted.</p> +<p>It passes if the box can be scrolled with the mouse wheel.</p> +<p>On high DPI, pass --disable-prefer-compositing-to-lcd-text for a meaningful test.</p> +<div id="scroll" class="trans"><div id="space"></div> +</div> +<script> +setTimeout(function() { document.querySelector('#scroll').classList.remove('trans'); }, 1000); +</script>
diff --git a/third_party/WebKit/Source/core/css/CSSStyleRule.cpp b/third_party/WebKit/Source/core/css/CSSStyleRule.cpp index 7b3d39c..7f638c6 100644 --- a/third_party/WebKit/Source/core/css/CSSStyleRule.cpp +++ b/third_party/WebKit/Source/core/css/CSSStyleRule.cpp
@@ -32,7 +32,8 @@ namespace blink { -using SelectorTextCache = HashMap<const CSSStyleRule*, String>; +using SelectorTextCache = WillBePersistentHeapHashMap<RawPtrWillBeWeakMember<const CSSStyleRule>, String>; + static SelectorTextCache& selectorTextCache() { DEFINE_STATIC_LOCAL(SelectorTextCache, cache, ()); @@ -52,7 +53,9 @@ m_propertiesCSSOMWrapper->clearParentRule(); #endif if (hasCachedSelectorText()) { +#if !ENABLE(OILPAN) selectorTextCache().remove(this); +#endif setHasCachedSelectorText(false); } }
diff --git a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp index 0675191..29125bd 100644 --- a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp +++ b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
@@ -79,6 +79,8 @@ TriState (*state)(LocalFrame&, Event*); String (*value)(LocalFrame&, Event*); bool isTextInsertion; + // TODO(yosin) We should have |canExecute()|, which checks clipboard + // accessibility to simplify |Editor::Command::execute()|. bool allowExecutionWhenDisabled; }; @@ -295,8 +297,24 @@ return executeApplyStyle(frame, source, EditActionSetBackgroundColor, CSSPropertyBackgroundColor, value); } -static bool executeCopy(LocalFrame& frame, Event*, EditorCommandSource, const String&) +static bool canWriteClipboard(LocalFrame& frame, EditorCommandSource source) { + if (source == CommandFromMenuOrKeyBinding) + return true; + Settings* settings = frame.settings(); + bool defaultValue = (settings && settings->javaScriptCanAccessClipboard()) || UserGestureIndicator::processingUserGesture(); + return frame.editor().client().canCopyCut(&frame, defaultValue); +} + +static bool executeCopy(LocalFrame& frame, Event*, EditorCommandSource source, const String&) +{ + // To support |allowExecutionWhenDisabled|, we need to check clipboard + // accessibility here rather than |Editor::Command::execute()|. + // TODO(yosin) We should move checking |canWriteClipboard()| to + // |Editor::Command::execute()| with introducing appropriate predicate, e.g. + // |canExecute()|. See also "Cut", and "Paste" command. + if (!canWriteClipboard(frame, source)) + return false; frame.editor().copy(); return true; } @@ -310,8 +328,15 @@ return true; } -static bool executeCut(LocalFrame& frame, Event*, EditorCommandSource, const String&) +static bool executeCut(LocalFrame& frame, Event*, EditorCommandSource source, const String&) { + // To support |allowExecutionWhenDisabled|, we need to check clipboard + // accessibility here rather than |Editor::Command::execute()|. + // TODO(yosin) We should move checking |canWriteClipboard()| to + // |Editor::Command::execute()| with introducing appropriate predicate, e.g. + // |canExecute()|. See also "Copy", and "Paste" command. + if (!canWriteClipboard(frame, source)) + return false; frame.editor().cut(); return true; } @@ -941,14 +966,37 @@ return true; } -static bool executePaste(LocalFrame& frame, Event*, EditorCommandSource, const String&) +static bool canReadClipboard(LocalFrame& frame, EditorCommandSource source) { + if (source == CommandFromMenuOrKeyBinding) + return true; + Settings* settings = frame.settings(); + bool defaultValue = settings && settings->javaScriptCanAccessClipboard() && settings->DOMPasteAllowed(); + return frame.editor().client().canPaste(&frame, defaultValue); +} + +static bool executePaste(LocalFrame& frame, Event*, EditorCommandSource source, const String&) +{ + // To support |allowExecutionWhenDisabled|, we need to check clipboard + // accessibility here rather than |Editor::Command::execute()|. + // TODO(yosin) We should move checking |canReadClipboard()| to + // |Editor::Command::execute()| with introducing appropriate predicate, e.g. + // |canExecute()|. See also "Copy", and "Cut" command. + if (!canReadClipboard(frame, source)) + return false; frame.editor().paste(); return true; } static bool executePasteGlobalSelection(LocalFrame& frame, Event*, EditorCommandSource source, const String&) { + // To support |allowExecutionWhenDisabled|, we need to check clipboard + // accessibility here rather than |Editor::Command::execute()|. + // TODO(yosin) We should move checking |canReadClipboard()| to + // |Editor::Command::execute()| with introducing appropriate predicate, e.g. + // |canExecute()|. See also "Copy", and "Cut" command. + if (!canReadClipboard(frame, source)) + return false; if (!frame.editor().behavior().supportsGlobalSelection()) return false; ASSERT_UNUSED(source, source == CommandFromMenuOrKeyBinding); @@ -1170,26 +1218,6 @@ return false; } -static bool supportedCopyCut(LocalFrame* frame) -{ - if (!frame) - return false; - - Settings* settings = frame->settings(); - bool defaultValue = (settings && settings->javaScriptCanAccessClipboard()) || UserGestureIndicator::processingUserGesture(); - return frame->editor().client().canCopyCut(frame, defaultValue); -} - -static bool supportedPaste(LocalFrame* frame) -{ - if (!frame) - return false; - - Settings* settings = frame->settings(); - bool defaultValue = settings && settings->javaScriptCanAccessClipboard() && settings->DOMPasteAllowed(); - return frame->editor().client().canPaste(frame, defaultValue); -} - // Enabled functions static bool enabled(LocalFrame&, Event*, EditorCommandSource) @@ -1230,13 +1258,17 @@ return selection.isCaret() && selection.isContentEditable(); } -static bool enabledCopy(LocalFrame& frame, Event*, EditorCommandSource) +static bool enabledCopy(LocalFrame& frame, Event*, EditorCommandSource source) { + if (!canWriteClipboard(frame, source)) + return false; return frame.editor().canDHTMLCopy() || frame.editor().canCopy(); } -static bool enabledCut(LocalFrame& frame, Event*, EditorCommandSource) +static bool enabledCut(LocalFrame& frame, Event*, EditorCommandSource source) { + if (!canWriteClipboard(frame, source)) + return false; return frame.editor().canDHTMLCut() || frame.editor().canCut(); } @@ -1270,8 +1302,10 @@ return frame.selection().isCaretOrRange() && frame.selection().isContentRichlyEditable() && frame.selection().rootEditableElement(); } -static bool enabledPaste(LocalFrame& frame, Event*, EditorCommandSource) +static bool enabledPaste(LocalFrame& frame, Event*, EditorCommandSource source) { + if (!canReadClipboard(frame, source)) + return false; return frame.editor().canPaste(); } @@ -1457,9 +1491,9 @@ { "BackColor", {4, executeBackColor, supported, enabledInRichlyEditableText, stateNone, valueBackColor, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "BackwardDelete", {5, executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, // FIXME: remove BackwardDelete when Safari for Windows stops using it. { "Bold", {6, executeToggleBold, supported, enabledInRichlyEditableText, stateBold, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Copy", {7, executeCopy, supportedCopyCut, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, + { "Copy", {7, executeCopy, supported, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, { "CreateLink", {8, executeCreateLink, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Cut", {9, executeCut, supportedCopyCut, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, + { "Cut", {9, executeCut, supported, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, { "DefaultParagraphSeparator", {10, executeDefaultParagraphSeparator, supported, enabled, stateNone, valueDefaultParagraphSeparator, notTextInsertion, doNotAllowExecutionWhenDisabled} }, { "Delete", {11, executeDelete, supported, enabledDelete, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "DeleteBackward", {12, executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, @@ -1553,8 +1587,8 @@ { "MoveWordRightAndModifySelection", {100, executeMoveWordRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "Outdent", {101, executeOutdent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "OverWrite", {102, executeToggleOverwrite, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Paste", {103, executePaste, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, - { "PasteAndMatchStyle", {104, executePasteAndMatchStyle, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, + { "Paste", {103, executePaste, supported, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, + { "PasteAndMatchStyle", {104, executePasteAndMatchStyle, supported, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, { "PasteGlobalSelection", {105, executePasteGlobalSelection, supportedFromMenuOrKeyBinding, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, { "Print", {106, executePrint, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "Redo", {107, executeRedo, supported, enabledRedo, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, @@ -1731,6 +1765,10 @@ bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) const { + // TODO(yosin) We should move this logic into |canExecute()| member function + // in |EditorInternalCommand| to replace |allowExecutionWhenDisabled|. + // |allowExecutionWhenDisabled| is for "Copy", "Cut" and "Paste" commands + // only. if (!isEnabled(triggeringEvent)) { // Let certain commands be executed when performed explicitly even if they are disabled. if (!isSupported() || !m_frame || !m_command->allowExecutionWhenDisabled)
diff --git a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp index 8c30986..79bfdbe1 100644 --- a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp +++ b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp
@@ -235,7 +235,7 @@ void EventHandlerRegistry::clearWeakMembers(Visitor* visitor) { - Vector<EventTarget*> deadTargets; + Vector<RawPtrWillBeUntracedMember<EventTarget>> deadTargets; for (size_t i = 0; i < EventHandlerClassCount; ++i) { EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i); const EventTargetSet* targets = &m_targets[handlerClass]; @@ -258,7 +258,7 @@ // Remove all event targets under the detached document. for (size_t handlerClassIndex = 0; handlerClassIndex < EventHandlerClassCount; ++handlerClassIndex) { EventHandlerClass handlerClass = static_cast<EventHandlerClass>(handlerClassIndex); - Vector<EventTarget*> targetsToRemove; + Vector<RawPtrWillBeUntracedMember<EventTarget>> targetsToRemove; const EventTargetSet* targets = &m_targets[handlerClass]; for (const auto& eventTarget : *targets) { if (Node* node = eventTarget.key->toNode()) {
diff --git a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h index b89ea927..987eecf 100644 --- a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h +++ b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h
@@ -14,7 +14,7 @@ class Document; class EventTarget; -typedef HashCountedSet<EventTarget*> EventTargetSet; +typedef HashCountedSet<RawPtrWillBeUntracedMember<EventTarget>> EventTargetSet; // Registry for keeping track of event handlers. Note that only handlers on // documents that can be rendered or can receive input (i.e., are attached to a
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp index c0b3b8d..41409ff 100644 --- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp +++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -821,7 +821,7 @@ void LocalFrame::clearWeakMembers(Visitor* visitor) { - Vector<HTMLPlugInElement*> deadPlugins; + Vector<UntracedMember<HTMLPlugInElement>> deadPlugins; for (const auto& pluginElement : m_pluginElements) { if (!Heap::isHeapObjectAlive(pluginElement)) { pluginElement->shouldDisposePlugin();
diff --git a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp index 02debc6..e6cdcd7 100644 --- a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
@@ -278,14 +278,13 @@ setSuggestedIndex(-1); if (m_isAutofilledByPreview) setAutofilled(false); - setSelectedIndex(optionIndex); + SelectOptionFlags flags = DeselectOtherOptions; + if (sendEvents) + flags |= DispatchInputAndChangeEvent | UserDriven; + selectOption(optionIndex, flags); - if (sendEvents && previousSelectedIndex != selectedIndex()) { - if (usesMenuList()) - dispatchInputAndChangeEventForMenuList(false); - else - listBoxOnChange(); - } + if (sendEvents && previousSelectedIndex != selectedIndex() && !usesMenuList()) + listBoxOnChange(); } String HTMLSelectElement::suggestedValue() const @@ -717,12 +716,12 @@ } } -void HTMLSelectElement::dispatchInputAndChangeEventForMenuList(bool requiresUserGesture) +void HTMLSelectElement::dispatchInputAndChangeEventForMenuList() { ASSERT(usesMenuList()); HTMLOptionElement* selectedOption = this->selectedOption(); - if (m_lastOnChangeOption.get() != selectedOption && (!requiresUserGesture || m_isProcessingUserDrivenChange)) { + if (m_lastOnChangeOption.get() != selectedOption && m_isProcessingUserDrivenChange) { m_lastOnChangeOption = selectedOption; m_isProcessingUserDrivenChange = false; RefPtrWillBeRawPtr<HTMLSelectElement> protector(this); @@ -971,17 +970,17 @@ if (selectedIndex() != optionIndex && isAutofilled()) setAutofilled(false); - HTMLElement* element = 0; + HTMLOptionElement* element = nullptr; if (listIndex >= 0) { - element = items[listIndex]; - if (isHTMLOptionElement(*element)) { - // setActiveSelectionAnchorIndex is O(N). - if (m_activeSelectionAnchorIndex < 0 || shouldDeselect) - setActiveSelectionAnchorIndex(listIndex); - if (m_activeSelectionEndIndex < 0 || shouldDeselect) - setActiveSelectionEndIndex(listIndex); - toHTMLOptionElement(*element).setSelectedState(true); - } + // listIndex must point an HTMLOptionElement if listIndex is not -1 + // because optionToListIndex() returned it. + element = toHTMLOptionElement(items[listIndex]); + // setActiveSelectionAnchorIndex is O(N). + if (m_activeSelectionAnchorIndex < 0 || shouldDeselect) + setActiveSelectionAnchorIndex(listIndex); + if (m_activeSelectionEndIndex < 0 || shouldDeselect) + setActiveSelectionEndIndex(listIndex); + element->setSelectedState(true); } // deselectItemsWithoutValidation() is O(N). @@ -1002,6 +1001,8 @@ m_isProcessingUserDrivenChange = flags & UserDriven; if (flags & DispatchInputAndChangeEvent) dispatchInputAndChangeEventForMenuList(); + else + m_lastOnChangeOption = element; if (LayoutObject* layoutObject = this->layoutObject()) { if (usesMenuList()) { // didSetSelectedIndex() is O(N) because of listToOptionIndex
diff --git a/third_party/WebKit/Source/core/html/HTMLSelectElement.h b/third_party/WebKit/Source/core/html/HTMLSelectElement.h index 0f8fdb7..420273a 100644 --- a/third_party/WebKit/Source/core/html/HTMLSelectElement.h +++ b/third_party/WebKit/Source/core/html/HTMLSelectElement.h
@@ -191,7 +191,7 @@ void defaultEventHandler(Event*) override; - void dispatchInputAndChangeEventForMenuList(bool requiresUserGesture = true); + void dispatchInputAndChangeEventForMenuList(); void recalcListItems(bool updateSelectedStates = true) const;
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp b/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp index fd027931..cf18359 100644 --- a/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp +++ b/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp
@@ -517,13 +517,13 @@ continue; Element* childElement = toElement(child); if (childElement->isDateTimeFieldElement()) { - // We need to pass the Font of this element because child elements - // can't resolve inherited style at this timing. - width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(style->font()); + // We need to pass the ComputedStyle of this element because child + // elements can't resolve inherited style at this timing. + width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(*style); } else { // ::-webkit-datetime-edit-text case. It has no // border/padding/margin in html.css. - width += style->font().width(childElement->textContent()); + width += DateTimeFieldElement::computeTextWidth(*style, childElement->textContent()); } } style->setWidth(Length(ceilf(width), Fixed));
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.cpp b/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.cpp index 5f5dca0..d28e676d 100644 --- a/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.cpp +++ b/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.cpp
@@ -31,6 +31,8 @@ #include "core/dom/Document.h" #include "core/dom/Text.h" #include "core/events/KeyboardEvent.h" +#include "core/layout/TextRunConstructor.h" +#include "core/style/ComputedStyle.h" #include "platform/text/PlatformLocale.h" #include "wtf/text/WTFString.h" @@ -59,6 +61,11 @@ HTMLSpanElement::trace(visitor); } +float DateTimeFieldElement::computeTextWidth(const ComputedStyle& style, const String& text) +{ + return style.font().width(constructTextRun(style.font(), text, style)); +} + void DateTimeFieldElement::defaultEventHandler(Event* event) { if (event->isKeyboardEvent()) { @@ -192,7 +199,7 @@ return m_fieldOwner ? m_fieldOwner->localeIdentifier() : nullAtom; } -float DateTimeFieldElement::maximumWidth(const Font&) +float DateTimeFieldElement::maximumWidth(const ComputedStyle&) { const float paddingLeftAndRight = 2; // This should match to html.css. return paddingLeftAndRight;
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.h b/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.h index ad2821a..a0239df 100644 --- a/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.h +++ b/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.h
@@ -34,7 +34,6 @@ class DateComponents; class DateTimeFieldsState; -class Font; // DateTimeFieldElement is base class of date time field element. class DateTimeFieldElement : public HTMLSpanElement { @@ -65,7 +64,7 @@ void defaultEventHandler(Event*) override; virtual bool hasValue() const = 0; bool isDisabled() const; - virtual float maximumWidth(const Font&); + virtual float maximumWidth(const ComputedStyle&); virtual void populateDateTimeFieldsState(DateTimeFieldsState&) = 0; void removeEventHandler() { m_fieldOwner = nullptr; } void setDisabled(); @@ -79,6 +78,8 @@ virtual String visibleValue() const = 0; DECLARE_VIRTUAL_TRACE(); + static float computeTextWidth(const ComputedStyle&, const String&); + protected: DateTimeFieldElement(Document&, FieldOwner&); void focusOnNextField();
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.cpp b/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.cpp index c3b79678..71365a2c 100644 --- a/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.cpp +++ b/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.cpp
@@ -74,12 +74,12 @@ } } -float DateTimeNumericFieldElement::maximumWidth(const Font& font) +float DateTimeNumericFieldElement::maximumWidth(const ComputedStyle& style) { - float maximumWidth = font.width(m_placeholder); - maximumWidth = std::max(maximumWidth, font.width(formatValue(maximum()))); - maximumWidth = std::max(maximumWidth, font.width(value())); - return maximumWidth + DateTimeFieldElement::maximumWidth(font); + float maximumWidth = computeTextWidth(style, m_placeholder); + maximumWidth = std::max(maximumWidth, computeTextWidth(style, formatValue(maximum()))); + maximumWidth = std::max(maximumWidth, computeTextWidth(style, value())); + return maximumWidth + DateTimeFieldElement::maximumWidth(style); } int DateTimeNumericFieldElement::defaultValueForStepDown() const
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.h b/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.h index fa5c0324..88c0d09 100644 --- a/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.h +++ b/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.h
@@ -84,7 +84,7 @@ private: // DateTimeFieldElement functions. void handleKeyboardEvent(KeyboardEvent*) final; - float maximumWidth(const Font&) override; + float maximumWidth(const ComputedStyle&) override; void stepDown() final; void stepUp() final; String value() const final;
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp b/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp index 984a774b7..58cb90047 100644 --- a/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp +++ b/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp
@@ -63,12 +63,12 @@ ASSERT(m_minimumIndex <= m_maximumIndex); } -float DateTimeSymbolicFieldElement::maximumWidth(const Font& font) +float DateTimeSymbolicFieldElement::maximumWidth(const ComputedStyle& style) { - float maximumWidth = font.width(visibleEmptyValue()); + float maximumWidth = computeTextWidth(style, visibleEmptyValue()); for (unsigned index = 0; index < m_symbols.size(); ++index) - maximumWidth = std::max(maximumWidth, font.width(m_symbols[index])); - return maximumWidth + DateTimeFieldElement::maximumWidth(font); + maximumWidth = std::max(maximumWidth, computeTextWidth(style, m_symbols[index])); + return maximumWidth + DateTimeFieldElement::maximumWidth(style); } void DateTimeSymbolicFieldElement::handleKeyboardEvent(KeyboardEvent* keyboardEvent)
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.h b/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.h index 9c1ec88..51124264 100644 --- a/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.h +++ b/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.h
@@ -54,7 +54,7 @@ // DateTimeFieldElement functions. void handleKeyboardEvent(KeyboardEvent*) final; - float maximumWidth(const Font&) override; + float maximumWidth(const ComputedStyle&) override; void stepDown() final; void stepUp() final; String value() const final;
diff --git a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp index 1794f1c2..dcf30ed 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
@@ -280,20 +280,15 @@ void LayoutBoxModelObject::createLayer(PaintLayerType type) { - - // Acquiring a PaintLayer may change the paint invalidation container. Therefore we must eagerly - // invalidate paint for this object before creating the layer. - if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && isRooted()) { - DisablePaintInvalidationStateAsserts invalidationDisabler; - DisableCompositingQueryAsserts compositingDisabler; - - // It would be more correct to call invalidatePaintIncludingNonCompositingDescendants, but - // we do this instead to avoid performance issues when creating large numbers of layers. - const LayoutBoxModelObject& paintInvalidationContainer = containerForPaintInvalidationOnRootedTree(); - invalidatePaintUsingContainer( - paintInvalidationContainer, - boundsRectForPaintInvalidation(&paintInvalidationContainer), - PaintInvalidationLayer); + // If the current paint invalidation container is not a stacking context and this object is + // a or treated as a stacking context, creating this layer may cause this object and its + // descendants to change paint invalidation container. Therefore we must eagerly invalidate + // them on the original paint invalidation container before creating the layer. + if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && isRooted() && styleRef().isTreatedAsOrStackingContext()) { + if (const LayoutBoxModelObject* currentPaintInvalidationContainer = containerForPaintInvalidation()) { + if (!currentPaintInvalidationContainer->styleRef().isStackingContext()) + invalidatePaintIncludingNonSelfPaintingLayerDescendants(*currentPaintInvalidationContainer); + } } ASSERT(!m_layer);
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp index 2499b03..f4b615a 100644 --- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -3388,6 +3388,18 @@ traverseNonCompositingDescendants(const_cast<LayoutObject&>(object), Functor(paintInvalidationContainer)); } +void LayoutObject::invalidatePaintOfPreviousPaintInvalidationRect(const LayoutBoxModelObject& paintInvalidationContainer, PaintInvalidationReason reason) const +{ + // These disablers are valid because we want to use the current compositing/invalidation status. + DisablePaintInvalidationStateAsserts invalidationDisabler; + DisableCompositingQueryAsserts compositingDisabler; + + LayoutRect invalidationRect = previousPaintInvalidationRect(); + adjustInvalidationRectForCompositedScrolling(invalidationRect, paintInvalidationContainer); + invalidatePaintUsingContainer(paintInvalidationContainer, invalidationRect, PaintInvalidationLayer); + invalidateDisplayItemClients(paintInvalidationContainer, PaintInvalidationLayer, invalidationRect, invalidationRect); +} + void LayoutObject::invalidatePaintIncludingNonCompositingDescendants() { class Functor : public LayoutObjectTraversalFunctor { @@ -3395,10 +3407,7 @@ explicit Functor(const LayoutBoxModelObject& paintInvalidationContainer) : m_paintInvalidationContainer(paintInvalidationContainer) { } void operator()(LayoutObject& object) const override { - LayoutRect invalidationRect = object.previousPaintInvalidationRect(); - object.adjustInvalidationRectForCompositedScrolling(invalidationRect, m_paintInvalidationContainer); - object.invalidatePaintUsingContainer(m_paintInvalidationContainer, invalidationRect, PaintInvalidationLayer); - object.invalidateDisplayItemClients(m_paintInvalidationContainer, PaintInvalidationLayer, invalidationRect, invalidationRect); + object.invalidatePaintOfPreviousPaintInvalidationRect(m_paintInvalidationContainer, PaintInvalidationLayer); } private: const LayoutBoxModelObject& m_paintInvalidationContainer; @@ -3425,6 +3434,15 @@ traverseNonCompositingDescendants(*this, Functor()); } +void LayoutObject::invalidatePaintIncludingNonSelfPaintingLayerDescendants(const LayoutBoxModelObject& paintInvalidationContainer) +{ + invalidatePaintOfPreviousPaintInvalidationRect(paintInvalidationContainer, PaintInvalidationLayer); + for (LayoutObject* child = slowFirstChild(); child; child = child->nextSibling()) { + if (!child->hasLayer() || !toLayoutBoxModelObject(child)->layer()->isSelfPaintingLayer()) + child->invalidatePaintIncludingNonSelfPaintingLayerDescendants(paintInvalidationContainer); + } +} + void LayoutObject::setIsSlowRepaintObject(bool isSlowRepaintObject) { ASSERT(frameView());
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h index 339ee246..11c46fa6 100644 --- a/third_party/WebKit/Source/core/layout/LayoutObject.h +++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -1029,6 +1029,7 @@ void invalidatePaintForOverflowIfNeeded(); void invalidatePaintIncludingNonCompositingDescendants(); + void invalidatePaintIncludingNonSelfPaintingLayerDescendants(const LayoutBoxModelObject& paintInvalidationContainer); void setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); // Returns the rect that should have paint invalidated whenever this object changes. The rect is in the view's @@ -1464,7 +1465,7 @@ inline void invalidateContainerPreferredLogicalWidths(); - void invalidatePaintIncludingNonCompositingDescendantsInternal(const LayoutBoxModelObject& repaintContainer); + void invalidatePaintOfPreviousPaintInvalidationRect(const LayoutBoxModelObject& paintInvalidationContainer, PaintInvalidationReason) const; LayoutRect previousSelectionRectForPaintInvalidation() const; void setPreviousSelectionRectForPaintInvalidation(const LayoutRect&);
diff --git a/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp b/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp index 55f3d07..0dae1bd 100644 --- a/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp
@@ -186,14 +186,7 @@ oldChild.view()->setShouldDoFullPaintInvalidation(); return; } - - DisableCompositingQueryAsserts disabler; - // FIXME: We should not allow paint invalidation out of paint invalidation state. crbug.com/457415 - DisablePaintInvalidationStateAsserts paintInvalidationAssertDisabler; - const LayoutBoxModelObject& paintInvalidationContainer = *oldChild.containerForPaintInvalidation(); - const LayoutRect& invalidationRect = oldChild.previousPaintInvalidationRectIncludingCompositedScrolling(paintInvalidationContainer); - oldChild.invalidatePaintUsingContainer(paintInvalidationContainer, invalidationRect, PaintInvalidationLayoutObjectRemoval); - oldChild.invalidateDisplayItemClients(paintInvalidationContainer, PaintInvalidationLayoutObjectRemoval, invalidationRect, invalidationRect); + oldChild.invalidatePaintOfPreviousPaintInvalidationRect(*oldChild.containerForPaintInvalidation(), PaintInvalidationLayoutObjectRemoval); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp b/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp index 06003e4..53a2b872 100644 --- a/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp
@@ -130,12 +130,6 @@ // Early exit if the element touches the edges. LayoutUnit top = paintRect.y(); LayoutUnit bottom = paintRect.maxY(); - if (isSelected() && inlineBoxWrapper()) { - LayoutUnit selTop = paintOffset.y() + inlineBoxWrapper()->root().selectionTop(); - LayoutUnit selBottom = paintOffset.y() + selTop + inlineBoxWrapper()->root().selectionHeight(); - top = std::min(selTop, top); - bottom = std::max(selBottom, bottom); - } if (paintRect.x() >= paintInfo.rect.maxX() || paintRect.maxX() <= paintInfo.rect.x()) return false; @@ -438,10 +432,10 @@ { ASSERT(!needsLayout()); - if (!isSelected()) - return LayoutRect(); - LayoutRect rect = localSelectionRect(); + if (rect.isEmpty()) + return rect; + mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, 0); // FIXME: groupedMapping() leaks the squashing abstraction. if (paintInvalidationContainer->layer()->groupedMapping()) @@ -449,9 +443,9 @@ return rect; } -LayoutRect LayoutReplaced::localSelectionRect(bool checkWhetherSelected) const +LayoutRect LayoutReplaced::localSelectionRect() const { - if (checkWhetherSelected && !isSelected()) + if (selectionState() == SelectionNone) return LayoutRect(); if (!inlineBoxWrapper()) { @@ -480,41 +474,7 @@ setPreviousPaintInvalidationRect(boundsRectForPaintInvalidation(containerForPaintInvalidation())); if (canUpdateSelectionOnRootLineBoxes()) - inlineBoxWrapper()->root().setHasSelectedChildren(isSelected()); -} - -bool LayoutReplaced::isSelected() const -{ - SelectionState s = selectionState(); - if (s == SelectionNone) - return false; - if (s == SelectionInside) - return true; - - int selectionStart, selectionEnd; - selectionStartEnd(selectionStart, selectionEnd); - if (s == SelectionStart) - return selectionStart == 0; - - int end = node()->hasChildren() ? node()->countChildren() : 1; - if (s == SelectionEnd) - return selectionEnd == end; - if (s == SelectionBoth) - return selectionStart == 0 && selectionEnd == end; - - ASSERT(0); - return false; -} -LayoutRect LayoutReplaced::clippedOverflowRectForPaintInvalidation(const LayoutBoxModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) const -{ - if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) - return LayoutRect(); - - // The selectionRect can project outside of the overflowRect, so take their union - // for paint invalidation to avoid selection painting glitches. - LayoutRect r = isSelected() ? localSelectionRect() : visualOverflowRect(); - mapRectToPaintInvalidationBacking(paintInvalidationContainer, r, paintInvalidationState); - return r; + inlineBoxWrapper()->root().setHasSelectedChildren(state != SelectionNone); } }
diff --git a/third_party/WebKit/Source/core/layout/LayoutReplaced.h b/third_party/WebKit/Source/core/layout/LayoutReplaced.h index 84002e1..1d61071 100644 --- a/third_party/WebKit/Source/core/layout/LayoutReplaced.h +++ b/third_party/WebKit/Source/core/layout/LayoutReplaced.h
@@ -67,12 +67,10 @@ bool canHaveChildren() const override { return false; } bool shouldPaint(const PaintInfo&, const LayoutPoint&) const; virtual void paintReplaced(const PaintInfo&, const LayoutPoint&) const { } - LayoutRect localSelectionRect(bool checkWhetherSelected = true) const; // This is in local coordinates, but it's a physical rect (so the top left corner is physical top left). + LayoutRect localSelectionRect() const; // This is in local coordinates, but it's a physical rect (so the top left corner is physical top left). void paint(const PaintInfo&, const LayoutPoint&) const override; - bool isSelected() const; - protected: void willBeDestroyed() override; @@ -105,8 +103,6 @@ private: void computePreferredLogicalWidths() final; - LayoutRect clippedOverflowRectForPaintInvalidation(const LayoutBoxModelObject* paintInvalidationContainer, const PaintInvalidationState* = nullptr) const override; - PositionWithAffinity positionForPoint(const LayoutPoint&) final; bool canBeSelectionLeaf() const override { return true; }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp index 506e2f1..eabb068 100644 --- a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
@@ -144,13 +144,13 @@ LayoutTableCell* cell = toLayoutTableCell(child); + ASSERT(!beforeChild || beforeChild->isTableCell()); + LayoutBox::addChild(cell, beforeChild); + // Generated content can result in us having a null section so make sure to null check our parent. if (parent()) section()->addCell(cell, this); - ASSERT(!beforeChild || beforeChild->isTableCell()); - LayoutBox::addChild(cell, beforeChild); - if (beforeChild || nextRow()) section()->setNeedsCellRecalc(); }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp index 420b909..332dc177 100644 --- a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
@@ -200,6 +200,32 @@ m_grid[row].row.grow(effectiveColumnCount); } +static inline void checkThatVectorIsDOMOrdered(const Vector<LayoutTableCell*, 1>& cells) +{ +#ifndef NDEBUG + // This function should be called on a non-empty vector. + ASSERT(cells.size() > 0); + + const LayoutTableCell* previousCell = cells[0]; + for (size_t i = 1; i < cells.size(); ++i) { + const LayoutTableCell* currentCell = cells[i]; + // The check assumes that all cells belong to the same row group. + ASSERT(previousCell->section() == currentCell->section()); + + // 2 overlapping cells can't be on the same row. + ASSERT(currentCell->row() != previousCell->row()); + + // Look backwards in the tree for the previousCell's row. If we are + // DOM ordered, we should find it. + const LayoutTableRow* row = currentCell->row(); + for (; row && row != previousCell->row(); row = row->previousRow()) { } + ASSERT(row == previousCell->row()); + + previousCell = currentCell; + } +#endif // NDEBUG +} + void LayoutTableSection::addCell(LayoutTableCell* cell, LayoutTableRow* row) { // We don't insert the cell if we need cell recalc as our internal columns' representation @@ -246,6 +272,7 @@ CellStruct& c = cellAt(insertionRow + r, m_cCol); ASSERT(cell); c.cells.append(cell); + checkThatVectorIsDOMOrdered(c.cells); // If cells overlap then we take the slow path for painting. if (c.cells.size() > 1) m_hasMultipleCellLevels = true;
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.h b/third_party/WebKit/Source/core/layout/LayoutTableSection.h index a4cdcfd..f3bbd6a 100644 --- a/third_party/WebKit/Source/core/layout/LayoutTableSection.h +++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.h
@@ -128,9 +128,15 @@ typedef Vector<LayoutTableCell*, 2> SpanningLayoutTableCells; + // CellStruct represents the cells that occupy an (N, M) position in the + // table grid. struct CellStruct { ALLOW_ONLY_INLINE_ALLOCATION(); public: + // All the cells that fills this grid "slot". + // Due to colspan / rowpsan, it is possible to have overlapping cells + // (see class comment about an example). + // This Vector is sorted in DOM order. Vector<LayoutTableCell*, 1> cells; bool inColSpan; // true for columns after the first in a colspan @@ -139,6 +145,17 @@ { } + // This is the cell in the grid "slot" that is on top of the others + // (aka the last cell in DOM order for this slot). + // + // This is the cell originating from this slot if it exists. + // + // The concept of a primary cell is dubious at most as it doesn't + // correspond to a DOM or rendering concept. Also callers should be + // careful about assumptions about it. For example, even though the + // primary cell is visibly the top most, it is not guaranteed to be + // the only one visible for this slot due to different visual + // overflow rectangles. LayoutTableCell* primaryCell() { return hasCells() ? cells[cells.size() - 1] : 0; @@ -380,6 +397,11 @@ HashSet<LayoutTableCell*> m_overflowingCells; bool m_forceSlowPaintPathWithOverflowingCell; + // This boolean tracks if we have cells overlapping due to rowspan / colspan + // (see class comment above about when it could appear). + // + // The use is to disable a painting optimization where we just paint the + // invalidated cells. bool m_hasMultipleCellLevels; // This map holds the collapsed border values for cells with collapsed borders.
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp index bdb983a..46f51bb1 100644 --- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp +++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -1581,16 +1581,20 @@ } layerChanged = true; - if (scrollingCoordinator) + if (scrollingCoordinator) { scrollingCoordinator->scrollableAreaScrollLayerDidChange(m_owningLayer.scrollableArea()); + scrollingCoordinator->scrollableAreasDidChange(); + } } } else if (m_scrollingLayer) { m_scrollingLayer = nullptr; m_scrollingContentsLayer = nullptr; m_scrollingBlockSelectionLayer = nullptr; layerChanged = true; - if (scrollingCoordinator) + if (scrollingCoordinator) { scrollingCoordinator->scrollableAreaScrollLayerDidChange(m_owningLayer.scrollableArea()); + scrollingCoordinator->scrollableAreasDidChange(); + } } return layerChanged;
diff --git a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h index 0fce6b65..59c68cbe 100644 --- a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h +++ b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
@@ -334,7 +334,7 @@ return checkSide; } -inline LayoutUnit inlineLogicalWidth(LineLayoutItem child, bool start = true, bool end = true) +inline LayoutUnit inlineLogicalWidthFromAncestorsIfNeeded(LineLayoutItem child, bool start = true, bool end = true) { unsigned lineDepth = 1; LayoutUnit extraWidth = 0; @@ -378,7 +378,7 @@ } else { positionedObjects.append(box); } - m_width.addUncommittedWidth(inlineLogicalWidth(box).toFloat()); + m_width.addUncommittedWidth(inlineLogicalWidthFromAncestorsIfNeeded(box).toFloat()); // Reset prior line break context characters. m_layoutTextInfo.m_lineBreakIterator.resetPriorContext(); } @@ -455,7 +455,7 @@ } } - m_width.addUncommittedWidth((inlineLogicalWidth(m_current.object()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)).toFloat()); + m_width.addUncommittedWidth((inlineLogicalWidthFromAncestorsIfNeeded(m_current.object()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)).toFloat()); } inline void BreakingContext::handleReplaced() @@ -483,7 +483,7 @@ // Optimize for a common case. If we can't find whitespace after the list // item, then this is all moot. - LayoutUnit replacedLogicalWidth = m_block.logicalWidthForChild(replacedBox) + m_block.marginStartForChild(replacedBox) + m_block.marginEndForChild(replacedBox) + inlineLogicalWidth(m_current.object()); + LayoutUnit replacedLogicalWidth = m_block.logicalWidthForChild(replacedBox) + m_block.marginStartForChild(replacedBox) + m_block.marginEndForChild(replacedBox) + inlineLogicalWidthFromAncestorsIfNeeded(m_current.object()); if (m_current.object().isListMarker()) { if (m_blockStyle->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) { // Like with inline flows, we start ignoring spaces to make sure that any @@ -559,7 +559,7 @@ float lastSpaceWordSpacing = 0; float wordSpacingForWordMeasurement = 0; - float wrapW = m_width.uncommittedWidth(); + float widthFromLastBreakingOpportunity = m_width.uncommittedWidth(); float charWidth = 0; // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word, // which is only possible if the word is the first thing on the line, that is, if |w| is zero. @@ -615,10 +615,10 @@ bool applyWordSpacing = false; if (breakWords && !midWordBreak) { - wrapW += charWidth; + widthFromLastBreakingOpportunity += charWidth; bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.offset() + 1 < layoutText.textLength() && U16_IS_TRAIL(layoutText.uncheckedCharacterAt(m_current.offset() + 1)); - charWidth = textWidth(layoutText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, m_collapseWhiteSpace); - midWordBreak = m_width.committedWidth() + wrapW + charWidth > m_width.availableWidth(); + charWidth = textWidth(layoutText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + widthFromLastBreakingOpportunity, m_collapseWhiteSpace); + midWordBreak = m_width.committedWidth() + widthFromLastBreakingOpportunity + charWidth > m_width.availableWidth(); } int nextBreakablePosition = m_current.nextBreakablePosition(); @@ -651,22 +651,22 @@ wordMeasurement.endOffset = m_current.offset(); wordMeasurement.startOffset = lastSpace; - float additionalTempWidth; + float lastWidthMeasurement; if (wordTrailingSpaceWidth && c == spaceCharacter) - additionalTempWidth = textWidth(layoutText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds) - wordTrailingSpaceWidth; + lastWidthMeasurement = textWidth(layoutText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds) - wordTrailingSpaceWidth; else - additionalTempWidth = textWidth(layoutText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds); + lastWidthMeasurement = textWidth(layoutText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds); - wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement; + wordMeasurement.width = lastWidthMeasurement + wordSpacingForWordMeasurement; wordMeasurement.glyphBounds.move(wordSpacingForWordMeasurement, 0); - additionalTempWidth += lastSpaceWordSpacing; - m_width.addUncommittedWidth(additionalTempWidth); + lastWidthMeasurement += lastSpaceWordSpacing; + m_width.addUncommittedWidth(lastWidthMeasurement); - if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && additionalTempWidth) - m_width.setTrailingWhitespaceWidth(additionalTempWidth); + if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && lastWidthMeasurement) + m_width.setTrailingWhitespaceWidth(lastWidthMeasurement); if (!m_appliedStartWidth) { - m_width.addUncommittedWidth(inlineLogicalWidth(m_current.object(), true, false).toFloat()); + m_width.addUncommittedWidth(inlineLogicalWidthFromAncestorsIfNeeded(m_current.object(), true, false).toFloat()); m_appliedStartWidth = true; } @@ -714,8 +714,8 @@ return false; } } else { - if (!betweenWords || (midWordBreak && !m_autoWrap)) - m_width.addUncommittedWidth(-additionalTempWidth); + if (!betweenWords || (midWordBreak && !m_autoWrap) || (breakAll && !m_currentCharacterIsSpace)) + m_width.addUncommittedWidth(-lastWidthMeasurement); if (hyphenWidth) { // Subtract the width of the soft hyphen out since we fit on a line. m_width.addUncommittedWidth(-hyphenWidth); @@ -735,7 +735,7 @@ if (m_autoWrap && betweenWords) { m_width.commit(); - wrapW = 0; + widthFromLastBreakingOpportunity = 0; m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition()); // Auto-wrapping text should not wrap in the middle of a word once it has had an // opportunity to break after a word. @@ -752,7 +752,7 @@ if (betweenWords) { lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0; - lastSpace = m_current.offset(); + lastSpace = !breakAll || m_currentCharacterIsSpace ? m_current.offset() : lastSpace; } if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) { @@ -816,21 +816,21 @@ wordMeasurement.layoutText = layoutText; // IMPORTANT: current.offset() is > layoutText.textLength() here! - float additionalTempWidth = 0; + float lastWidthMeasurement = 0; wordMeasurement.startOffset = lastSpace; wordMeasurement.endOffset = m_current.offset(); if (!m_ignoringSpaces) { - additionalTempWidth = textWidth(layoutText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds); - wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement; + lastWidthMeasurement = textWidth(layoutText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds); + wordMeasurement.width = lastWidthMeasurement + wordSpacingForWordMeasurement; wordMeasurement.glyphBounds.move(wordSpacingForWordMeasurement, 0); } - additionalTempWidth += lastSpaceWordSpacing; + lastWidthMeasurement += lastSpaceWordSpacing; - LayoutUnit inlineLogicalTempWidth = inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, m_includeEndWidth); - m_width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth); + LayoutUnit additionalWidthFromAncestors = inlineLogicalWidthFromAncestorsIfNeeded(m_current.object(), !m_appliedStartWidth, m_includeEndWidth); + m_width.addUncommittedWidth(lastWidthMeasurement + additionalWidthFromAncestors); - if (m_collapseWhiteSpace && m_currentCharacterIsSpace && additionalTempWidth) - m_width.setTrailingWhitespaceWidth(additionalTempWidth + inlineLogicalTempWidth); + if (m_collapseWhiteSpace && m_currentCharacterIsSpace && lastWidthMeasurement) + m_width.setTrailingWhitespaceWidth(lastWidthMeasurement + additionalWidthFromAncestors); m_includeEndWidth = false;
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp index a36d8aa..2750c93 100644 --- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp +++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
@@ -333,7 +333,7 @@ if (m_hasBoxDecorationBackground || hasOverflowModel()) { // The selectionRect can project outside of the overflowRect, so take their union // for paint invalidation to avoid selection painting glitches. - LayoutRect decoratedPaintInvalidationRect = unionRect(localSelectionRect(false), visualOverflowRect()); + LayoutRect decoratedPaintInvalidationRect = unionRect(localSelectionRect(), visualOverflowRect()); paintInvalidationRect.unite(decoratedPaintInvalidationRect); }
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h index e292c4d..ab9e7dca 100644 --- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h +++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
@@ -74,7 +74,7 @@ void updateHaveWheelEventHandlers(); void updateHaveScrollEventHandlers(); - // Should be called whenever scrollable area set changes. + // Should be called whenever a scrollable area is added or removed, or gains/loses a composited layer. void scrollableAreasDidChange(); // Should be called whenever the slow repaint objects counter changes between zero and one.
diff --git a/third_party/WebKit/Source/core/paint/ListMarkerPainter.cpp b/third_party/WebKit/Source/core/paint/ListMarkerPainter.cpp index ced95df..3d7e8a4 100644 --- a/third_party/WebKit/Source/core/paint/ListMarkerPainter.cpp +++ b/third_party/WebKit/Source/core/paint/ListMarkerPainter.cpp
@@ -12,6 +12,7 @@ #include "core/paint/BlockPainter.h" #include "core/paint/LayoutObjectDrawingRecorder.h" #include "core/paint/PaintInfo.h" +#include "platform/RuntimeEnabledFeatures.h" #include "platform/geometry/LayoutPoint.h" #include "platform/graphics/GraphicsContextStateSaver.h" #include "wtf/text/CharacterNames.h" @@ -53,7 +54,8 @@ LayoutPoint boxOrigin(paintOffset + m_layoutListMarker.location()); LayoutRect overflowRect(m_layoutListMarker.visualOverflowRect()); - if (m_layoutListMarker.selectionState() != SelectionNone) + if (!RuntimeEnabledFeatures::selectionPaintingWithoutSelectionGapsEnabled() + && m_layoutListMarker.selectionState() != SelectionNone) overflowRect.unite(m_layoutListMarker.localSelectionRect()); overflowRect.moveBy(boxOrigin); @@ -80,7 +82,8 @@ return; } - if (m_layoutListMarker.selectionState() != SelectionNone) { + if (!RuntimeEnabledFeatures::selectionPaintingWithoutSelectionGapsEnabled() + && m_layoutListMarker.selectionState() != SelectionNone) { LayoutRect selRect = m_layoutListMarker.localSelectionRect(); selRect.moveBy(boxOrigin); context->fillRect(pixelSnappedIntRect(selRect), m_layoutListMarker.listItem()->selectionBackgroundColor());
diff --git a/third_party/WebKit/Source/core/paint/PartPainter.cpp b/third_party/WebKit/Source/core/paint/PartPainter.cpp index 8b4327d..fbe7456b 100644 --- a/third_party/WebKit/Source/core/paint/PartPainter.cpp +++ b/third_party/WebKit/Source/core/paint/PartPainter.cpp
@@ -17,6 +17,29 @@ namespace blink { +bool PartPainter::isSelected() const +{ + SelectionState s = m_layoutPart.selectionState(); + if (s == SelectionNone) + return false; + if (s == SelectionInside) + return true; + + int selectionStart, selectionEnd; + m_layoutPart.selectionStartEnd(selectionStart, selectionEnd); + if (s == SelectionStart) + return selectionStart == 0; + + int end = m_layoutPart.node()->hasChildren() ? m_layoutPart.node()->countChildren() : 1; + if (s == SelectionEnd) + return selectionEnd == end; + if (s == SelectionBoth) + return selectionStart == 0 && selectionEnd == end; + + ASSERT(0); + return false; +} + void PartPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!m_layoutPart.shouldPaint(paintInfo, paintOffset)) @@ -60,7 +83,7 @@ } // Paint a partially transparent wash over selected widgets. - if (m_layoutPart.isSelected() && !paintInfo.isPrinting() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutPart, paintInfo.phase, adjustedPaintOffset)) { + if (isSelected() && !paintInfo.isPrinting() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutPart, paintInfo.phase, adjustedPaintOffset)) { LayoutRect rect = m_layoutPart.localSelectionRect(); rect.moveBy(adjustedPaintOffset); IntRect selectionRect = pixelSnappedIntRect(rect);
diff --git a/third_party/WebKit/Source/core/paint/PartPainter.h b/third_party/WebKit/Source/core/paint/PartPainter.h index a8595b5..eac057c 100644 --- a/third_party/WebKit/Source/core/paint/PartPainter.h +++ b/third_party/WebKit/Source/core/paint/PartPainter.h
@@ -22,6 +22,8 @@ void paintContents(const PaintInfo&, const LayoutPoint&); private: + bool isSelected() const; + const LayoutPart& m_layoutPart; };
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/elementsTreeOutline.css b/third_party/WebKit/Source/devtools/front_end/elements/elementsTreeOutline.css index 7ea8ca1..a4967ea 100644 --- a/third_party/WebKit/Source/devtools/front_end/elements/elementsTreeOutline.css +++ b/third_party/WebKit/Source/devtools/front_end/elements/elementsTreeOutline.css
@@ -337,7 +337,7 @@ } @media (-webkit-min-device-pixel-ratio: 1.5) { -.elements-disclosure li.selected .gutter-container { +.elements-disclosure li.selected .gutter-container:not(.has-decorations) { -webkit-mask-image: url(Images/toolbarButtonGlyphs_2x.png); } } /* media */
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js index d7d4c16..357bdb1 100644 --- a/third_party/WebKit/Source/devtools/front_end/main/Main.js +++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -390,10 +390,8 @@ _registerForwardedShortcuts: function() { - /** @const */ var forwardedActions = ["main.reload", "main.hard-reload", "main.toggle-dock", "debugger.toggle-breakpoints-active", "debugger.toggle-pause"]; + /** @const */ var forwardedActions = ["main.toggle-dock", "debugger.toggle-breakpoints-active", "debugger.toggle-pause"]; var actionKeys = WebInspector.shortcutRegistry.keysForActions(forwardedActions).map(WebInspector.KeyboardShortcut.keyCodeAndModifiersFromKey); - - actionKeys.push({keyCode: WebInspector.KeyboardShortcut.Keys.F8.code}); InspectorFrontendHost.setWhitelistedShortcuts(JSON.stringify(actionKeys)); },
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js b/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js index f56780e..eae8e71 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js
@@ -146,7 +146,7 @@ menuItemElement._subItems = item.subItems; // Occupy the same space on the left in all items. - var checkMarkElement = menuItemElement.createChild("span", "soft-context-menu-item-checkmark"); + var checkMarkElement = menuItemElement.createChild("span", "soft-context-menu-item-checkmark checkmark"); checkMarkElement.textContent = "\u2713 "; // Checkmark Unicode symbol checkMarkElement.style.opacity = "0"; @@ -357,11 +357,10 @@ if (event) event.consume(true); - } else if (this._parentMenu && this._contextMenuElement.parentElement) { + } else if (this._parentMenu && this._contextMenuElement.parentElementOrShadowHost()) { this._discardSubMenus(); if (closeParentMenus) this._parentMenu._discardMenu(closeParentMenus, event); - if (event) event.consume(true); } @@ -375,7 +374,7 @@ { if (this._subMenu) this._subMenu._discardSubMenus(); - this._contextMenuElement.remove(); + this.element.remove(); if (this._parentMenu) delete this._parentMenu._subMenu; }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/softContextMenu.css b/third_party/WebKit/Source/devtools/front_end/ui/softContextMenu.css index 76f8e23..755c1c3 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/softContextMenu.css +++ b/third_party/WebKit/Source/devtools/front_end/ui/softContextMenu.css
@@ -59,9 +59,10 @@ .soft-context-menu-item-submenu-arrow { color: #222; - float: right; pointer-events: none; font-size: 11px; + flex: 1 1 auto; + text-align: right; } .soft-context-menu-item-mouse-over .soft-context-menu-item-checkmark {
diff --git a/third_party/WebKit/Source/platform/heap/Handle.h b/third_party/WebKit/Source/platform/heap/Handle.h index fd9415a..d087caa 100644 --- a/third_party/WebKit/Source/platform/heap/Handle.h +++ b/third_party/WebKit/Source/platform/heap/Handle.h
@@ -869,15 +869,83 @@ template<typename Derived> friend class VisitorHelper; }; -// Comparison operators between (Weak)Members and Persistents +// UntracedMember is a pointer to an on-heap object that is not traced for some +// reason. Please don't use this unless you understand what you're doing. +// Basically, all pointers to on-heap objects must be stored in either of +// Persistent, Member or WeakMember. It is not allowed to leave raw pointers to +// on-heap objects. However, there can be scenarios where you have to use raw +// pointers for some reason, and in that case you can use UntracedMember. Of +// course, it must be guaranteed that the pointing on-heap object is kept alive +// while the raw pointer is pointing to the object. +template<typename T> +class UntracedMember final : public Member<T> { +public: + UntracedMember() : Member<T>() { } + + UntracedMember(std::nullptr_t) : Member<T>(nullptr) { } + + UntracedMember(T* raw) : Member<T>(raw) { } + + template<typename U> + UntracedMember(const RawPtr<U>& other) : Member<T>(other) { } + + template<typename U> + UntracedMember(const Persistent<U>& other) : Member<T>(other) { } + + template<typename U> + UntracedMember(const Member<U>& other) : Member<T>(other) { } + + UntracedMember(WTF::HashTableDeletedValueType x) : Member<T>(x) { } + + template<typename U> + UntracedMember& operator=(const Persistent<U>& other) + { + this->m_raw = other; + this->checkPointer(); + return *this; + } + + template<typename U> + UntracedMember& operator=(const Member<U>& other) + { + this->m_raw = other; + this->checkPointer(); + return *this; + } + + template<typename U> + UntracedMember& operator=(U* other) + { + this->m_raw = other; + this->checkPointer(); + return *this; + } + + template<typename U> + UntracedMember& operator=(const RawPtr<U>& other) + { + this->m_raw = other; + this->checkPointer(); + return *this; + } + + UntracedMember& operator=(std::nullptr_t) + { + this->m_raw = nullptr; + return *this; + } +}; + +// Comparison operators between (Weak)Members, Persistents, and UntracedMembers. template<typename T, typename U> inline bool operator==(const Member<T>& a, const Member<U>& b) { return a.get() == b.get(); } template<typename T, typename U> inline bool operator!=(const Member<T>& a, const Member<U>& b) { return a.get() != b.get(); } +template<typename T, typename U> inline bool operator==(const Persistent<T>& a, const Persistent<U>& b) { return a.get() == b.get(); } +template<typename T, typename U> inline bool operator!=(const Persistent<T>& a, const Persistent<U>& b) { return a.get() != b.get(); } + template<typename T, typename U> inline bool operator==(const Member<T>& a, const Persistent<U>& b) { return a.get() == b.get(); } template<typename T, typename U> inline bool operator!=(const Member<T>& a, const Persistent<U>& b) { return a.get() != b.get(); } template<typename T, typename U> inline bool operator==(const Persistent<T>& a, const Member<U>& b) { return a.get() == b.get(); } template<typename T, typename U> inline bool operator!=(const Persistent<T>& a, const Member<U>& b) { return a.get() != b.get(); } -template<typename T, typename U> inline bool operator==(const Persistent<T>& a, const Persistent<U>& b) { return a.get() == b.get(); } -template<typename T, typename U> inline bool operator!=(const Persistent<T>& a, const Persistent<U>& b) { return a.get() != b.get(); } template<typename T> class DummyBase { @@ -925,6 +993,7 @@ #define RawPtrWillBePersistent blink::Persistent #define RawPtrWillBeWeakMember blink::WeakMember #define RawPtrWillBeWeakPersistent blink::WeakPersistent +#define RawPtrWillBeUntracedMember blink::UntracedMember #define OwnPtrWillBeCrossThreadPersistent blink::CrossThreadPersistent #define OwnPtrWillBeMember blink::Member #define OwnPtrWillBePersistent blink::Persistent @@ -1008,6 +1077,7 @@ #define RawPtrWillBePersistent WTF::RawPtr #define RawPtrWillBeWeakMember WTF::RawPtr #define RawPtrWillBeWeakPersistent WTF::RawPtr +#define RawPtrWillBeUntracedMember WTF::RawPtr #define OwnPtrWillBeCrossThreadPersistent WTF::OwnPtr #define OwnPtrWillBeMember WTF::OwnPtr #define OwnPtrWillBePersistent WTF::OwnPtr @@ -1198,6 +1268,13 @@ static const bool canMoveWithMemcpy = true; }; +template <typename T> struct VectorTraits<blink::UntracedMember<T>> : VectorTraitsBase<blink::UntracedMember<T>> { + static const bool needsDestruction = false; + static const bool canInitializeWithMemset = true; + static const bool canClearUnusedSlotsWithMemset = true; + static const bool canMoveWithMemcpy = true; +}; + template <typename T> struct VectorTraits<blink::HeapVector<T, 0>> : VectorTraitsBase<blink::HeapVector<T, 0>> { static const bool needsDestruction = false; static const bool canInitializeWithMemset = true; @@ -1289,6 +1366,32 @@ } }; +template<typename T> struct HashTraits<blink::UntracedMember<T>> : SimpleClassHashTraits<blink::UntracedMember<T>> { + static const bool needsDestruction = false; + // FIXME: The distinction between PeekInType and PassInType is there for + // the sake of the reference counting handles. When they are gone the two + // types can be merged into PassInType. + // FIXME: Implement proper const'ness for iterator types. + using PeekInType = RawPtr<T>; + using PassInType = RawPtr<T>; + using IteratorGetType = blink::UntracedMember<T>*; + using IteratorConstGetType = const blink::UntracedMember<T>*; + using IteratorReferenceType = blink::UntracedMember<T>&; + using IteratorConstReferenceType = const blink::UntracedMember<T>&; + static IteratorReferenceType getToReferenceConversion(IteratorGetType x) { return *x; } + static IteratorConstReferenceType getToReferenceConstConversion(IteratorConstGetType x) { return *x; } + // FIXME: Similarly, there is no need for a distinction between PeekOutType + // and PassOutType without reference counting. + using PeekOutType = T*; + using PassOutType = T*; + + template<typename U> + static void store(const U& value, blink::UntracedMember<T>& storage) { storage = value; } + + static PeekOutType peek(const blink::UntracedMember<T>& value) { return value; } + static PassOutType passOut(const blink::UntracedMember<T>& value) { return value; } +}; + template<typename T> struct PtrHash<blink::Member<T>> : PtrHash<T*> { template<typename U> static unsigned hash(const U& key) { return PtrHash<T*>::hash(key); } @@ -1301,6 +1404,9 @@ template<typename T> struct PtrHash<blink::WeakMember<T>> : PtrHash<blink::Member<T>> { }; +template<typename T> struct PtrHash<blink::UntracedMember<T>> : PtrHash<blink::Member<T>> { +}; + // PtrHash is the default hash for hash tables with members. template<typename T> struct DefaultHash<blink::Member<T>> { using Hash = PtrHash<blink::Member<T>>; @@ -1310,6 +1416,10 @@ using Hash = PtrHash<blink::WeakMember<T>>; }; +template<typename T> struct DefaultHash<blink::UntracedMember<T>> { + using Hash = PtrHash<blink::UntracedMember<T>>; +}; + template<typename T> struct NeedsTracing<blink::Member<T>> { static const bool value = true;
diff --git a/third_party/WebKit/Source/platform/heap/Heap.h b/third_party/WebKit/Source/platform/heap/Heap.h index 5d45a3d..355b1719 100644 --- a/third_party/WebKit/Source/platform/heap/Heap.h +++ b/third_party/WebKit/Source/platform/heap/Heap.h
@@ -43,6 +43,10 @@ namespace blink { +template<typename T> class Member; +template<typename T> class WeakMember; +template<typename T> class UntracedMember; + template<typename T, bool = NeedsAdjustAndMark<T>::value> class ObjectAliveTrait; template<typename T> @@ -100,6 +104,11 @@ return isHeapObjectAlive(member.get()); } template<typename T> + static inline bool isHeapObjectAlive(const UntracedMember<T>& member) + { + return isHeapObjectAlive(member.get()); + } + template<typename T> static inline bool isHeapObjectAlive(const RawPtr<T>& ptr) { return isHeapObjectAlive(ptr.get());
diff --git a/third_party/WebKit/Source/platform/heap/HeapAllocator.cpp b/third_party/WebKit/Source/platform/heap/HeapAllocator.cpp index 7531234..8d27428 100644 --- a/third_party/WebKit/Source/platform/heap/HeapAllocator.cpp +++ b/third_party/WebKit/Source/platform/heap/HeapAllocator.cpp
@@ -88,6 +88,12 @@ bool HeapAllocator::backingShrink(void* address, size_t quantizedCurrentSize, size_t quantizedShrunkSize) { + // We shrink the object only if the shrinking will make a non-small + // prompt-free block. + // FIXME: Optimize the threshold size. + if (quantizedCurrentSize <= quantizedShrunkSize + sizeof(HeapObjectHeader) + sizeof(void*) * 32) + return true; + if (!address) return true; @@ -106,12 +112,6 @@ HeapObjectHeader* header = HeapObjectHeader::fromPayload(address); ASSERT(header->checkHeader()); NormalPageHeap* heap = static_cast<NormalPage*>(page)->heapForNormalPage(); - // We shrink the object only if the shrinking will make a non-small - // prompt-free block. - // FIXME: Optimize the threshold size. - if (quantizedCurrentSize <= quantizedShrunkSize + sizeof(HeapObjectHeader) + sizeof(void*) * 32 && !heap->isObjectAllocatedAtAllocationPoint(header)) - return true; - bool succeededAtAllocationPoint = heap->shrinkObject(header, quantizedShrunkSize); if (succeededAtAllocationPoint) state->allocationPointAdjusted(heap->heapIndex());
diff --git a/third_party/WebKit/Source/platform/heap/HeapPage.cpp b/third_party/WebKit/Source/platform/heap/HeapPage.cpp index 78c725e..a94679f 100644 --- a/third_party/WebKit/Source/platform/heap/HeapPage.cpp +++ b/third_party/WebKit/Source/platform/heap/HeapPage.cpp
@@ -699,7 +699,7 @@ size_t allocationSize = Heap::allocationSizeFromSize(newSize); ASSERT(allocationSize > header->size()); size_t expandSize = allocationSize - header->size(); - if (isObjectAllocatedAtAllocationPoint(header) && expandSize <= m_remainingAllocationSize) { + if (header->payloadEnd() == m_currentAllocationPoint && expandSize <= m_remainingAllocationSize) { m_currentAllocationPoint += expandSize; m_remainingAllocationSize -= expandSize; @@ -719,7 +719,7 @@ size_t allocationSize = Heap::allocationSizeFromSize(newSize); ASSERT(header->size() > allocationSize); size_t shrinkSize = header->size() - allocationSize; - if (isObjectAllocatedAtAllocationPoint(header)) { + if (header->payloadEnd() == m_currentAllocationPoint) { m_currentAllocationPoint -= shrinkSize; m_remainingAllocationSize += shrinkSize; SET_MEMORY_INACCESSIBLE(m_currentAllocationPoint, shrinkSize);
diff --git a/third_party/WebKit/Source/platform/heap/HeapPage.h b/third_party/WebKit/Source/platform/heap/HeapPage.h index cdbb50f..29ddbd30 100644 --- a/third_party/WebKit/Source/platform/heap/HeapPage.h +++ b/third_party/WebKit/Source/platform/heap/HeapPage.h
@@ -781,11 +781,6 @@ bool shrinkObject(HeapObjectHeader*, size_t); void decreasePromptlyFreedSize(size_t size) { m_promptlyFreedSize -= size; } - bool isObjectAllocatedAtAllocationPoint(HeapObjectHeader* header) - { - return header->payloadEnd() == m_currentAllocationPoint; - } - private: void allocatePage(); Address lazySweepPages(size_t, size_t gcInfoIndex) override;
diff --git a/third_party/WebKit/Source/web/WebDOMError.cpp b/third_party/WebKit/Source/web/WebDOMError.cpp deleted file mode 100644 index 3118e9c..0000000 --- a/third_party/WebKit/Source/web/WebDOMError.cpp +++ /dev/null
@@ -1,91 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "public/web/WebDOMError.h" - -#include "bindings/core/v8/V8Binding.h" -#include "bindings/core/v8/V8DOMError.h" -#include "core/dom/DOMError.h" -#include "wtf/PassOwnPtr.h" - -namespace blink { - -WebDOMError WebDOMError::create(const WebString& name, const WebString& message) -{ - return DOMError::create(name, message); -} - -void WebDOMError::reset() -{ - m_private.reset(); -} - -void WebDOMError::assign(const WebDOMError& other) -{ - m_private = other.m_private; -} - -WebString WebDOMError::name() const -{ - if (!m_private.get()) - return WebString(); - return m_private->name(); -} - -WebString WebDOMError::message() const -{ - if (!m_private.get()) - return WebString(); - return m_private->message(); -} - -v8::Local<v8::Value> WebDOMError::toV8Value(v8::Local<v8::Object> creationContext, v8::Isolate* isolate) -{ - // We no longer use |creationContext| because it's often misused and points - // to a context faked by user script. - ASSERT(creationContext->CreationContext() == isolate->GetCurrentContext()); - if (!m_private.get()) - return v8::Local<v8::Value>(); - return toV8(m_private.get(), isolate->GetCurrentContext()->Global(), isolate); -} - -WebDOMError::WebDOMError(DOMError* error) - : m_private(error) -{ -} - -WebDOMError& WebDOMError::operator=(DOMError* error) -{ - m_private = error; - return *this; -} - -} // namespace blink
diff --git a/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp b/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp index 05125f1..1f2c807 100644 --- a/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp +++ b/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp
@@ -160,7 +160,7 @@ agent->flushPendingProtocolNotifications(); Vector<WebViewImpl*> views; - Vector<WebFrameWidgetImpl*> widgets; + WillBeHeapVector<RawPtrWillBeMember<WebFrameWidgetImpl>> widgets; // 1. Disable input events. const HashSet<WebViewImpl*>& viewImpls = WebViewImpl::allInstances(); @@ -172,9 +172,9 @@ view->setIgnoreInputEvents(true); } - const HashSet<WebFrameWidgetImpl*>& widgetImpls = WebFrameWidgetImpl::allInstances(); - HashSet<WebFrameWidgetImpl*>::const_iterator widgetImplsEnd = widgetImpls.end(); - for (HashSet<WebFrameWidgetImpl*>::const_iterator it = widgetImpls.begin(); it != widgetImplsEnd; ++it) { + const WebFrameWidgetsSet& widgetImpls = WebFrameWidgetImpl::allInstances(); + WebFrameWidgetsSet::const_iterator widgetImplsEnd = widgetImpls.end(); + for (WebFrameWidgetsSet::const_iterator it = widgetImpls.begin(); it != widgetImplsEnd; ++it) { WebFrameWidgetImpl* widget = *it; m_frozenWidgets.add(widget); widgets.append(widget); @@ -200,7 +200,7 @@ (*it)->setIgnoreInputEvents(false); } } - for (Vector<WebFrameWidgetImpl*>::iterator it = widgets.begin(); it != widgets.end(); ++it) { + for (WillBeHeapVector<RawPtrWillBeMember<WebFrameWidgetImpl>>::iterator it = widgets.begin(); it != widgets.end(); ++it) { if (m_frozenWidgets.contains(*it)) { // The widget was not closed during the dispatch. (*it)->setIgnoreInputEvents(false); @@ -226,8 +226,7 @@ OwnPtr<WebDevToolsAgentClient::WebKitClientMessageLoop> m_messageLoop; typedef HashSet<WebViewImpl*> FrozenViewsSet; FrozenViewsSet m_frozenViews; - typedef HashSet<WebFrameWidgetImpl*> FrozenWidgetsSet; - FrozenWidgetsSet m_frozenWidgets; + WebFrameWidgetsSet m_frozenWidgets; static ClientMessageLoopAdapter* s_instance; };
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp index c2b98fd..984f2ef 100644 --- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp +++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -47,7 +47,6 @@ #include "core/page/Page.h" #include "platform/KeyboardCodes.h" #include "platform/NotImplemented.h" -#include "public/web/WebBeginFrameArgs.h" #include "public/web/WebWidgetClient.h" #include "web/ContextMenuAllowedScope.h" #include "web/WebDevToolsAgentImpl.h" @@ -83,9 +82,9 @@ } // static -HashSet<WebFrameWidgetImpl*>& WebFrameWidgetImpl::allInstances() +WebFrameWidgetsSet& WebFrameWidgetImpl::allInstances() { - DEFINE_STATIC_LOCAL(HashSet<WebFrameWidgetImpl*>, allInstances, ()); + DEFINE_STATIC_LOCAL(WebFrameWidgetsSet, allInstances, ()); return allInstances; } @@ -246,11 +245,11 @@ // FIXME: Implement full screen for out-of-process iframes. } -void WebFrameWidgetImpl::beginFrame(const WebBeginFrameArgs& frameTime) +void WebFrameWidgetImpl::beginFrame(double lastFrameTimeMonotonic) { - TRACE_EVENT1("blink", "WebFrameWidgetImpl::beginFrame", "frameTime", frameTime.lastFrameTimeMonotonic); - ASSERT(frameTime.lastFrameTimeMonotonic); - PageWidgetDelegate::animate(*page(), frameTime.lastFrameTimeMonotonic); + TRACE_EVENT1("blink", "WebFrameWidgetImpl::beginFrame", "frameTime", lastFrameTimeMonotonic); + ASSERT(lastFrameTimeMonotonic); + PageWidgetDelegate::animate(*page(), lastFrameTimeMonotonic); } void WebFrameWidgetImpl::layout()
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h index dfebc2a..f91e79d 100644 --- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h +++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
@@ -57,13 +57,16 @@ class WebLayerTreeView; class WebMouseEvent; class WebMouseWheelEvent; +class WebFrameWidgetImpl; + +using WebFrameWidgetsSet = WillBePersistentHeapHashSet<RawPtrWillBeWeakMember<WebFrameWidgetImpl>>; class WebFrameWidgetImpl final : public RefCountedWillBeGarbageCollectedFinalized<WebFrameWidgetImpl> , public WebFrameWidget , public PageWidgetEventHandler { public: static WebFrameWidgetImpl* create(WebWidgetClient*, WebLocalFrame*); - static HashSet<WebFrameWidgetImpl*>& allInstances(); + static WebFrameWidgetsSet& allInstances(); // WebWidget functions: void close() override; @@ -75,7 +78,7 @@ void willEndLiveResize() override; void didEnterFullScreen() override; void didExitFullScreen() override; - void beginFrame(const WebBeginFrameArgs&) override; + void beginFrame(double lastFrameTimeMonotonic) override; void layout() override; void paint(WebCanvas*, const WebRect&) override; void layoutAndPaintAsync(WebLayoutAndPaintAsyncCallback*) override;
diff --git a/third_party/WebKit/Source/web/WebPagePopupImpl.cpp b/third_party/WebKit/Source/web/WebPagePopupImpl.cpp index ed32068..6f14730 100644 --- a/third_party/WebKit/Source/web/WebPagePopupImpl.cpp +++ b/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
@@ -357,11 +357,11 @@ return m_popupClient->contentSize(); } -void WebPagePopupImpl::beginFrame(const WebBeginFrameArgs& frameTime) +void WebPagePopupImpl::beginFrame(double lastFrameTimeMonotonic) { if (!m_page) return; - // FIXME: This should use frameTime.lastFrameTimeMonotonic but doing so + // FIXME: This should use lastFrameTimeMonotonic but doing so // breaks tests. PageWidgetDelegate::animate(*m_page, monotonicallyIncreasingTime()); }
diff --git a/third_party/WebKit/Source/web/WebPagePopupImpl.h b/third_party/WebKit/Source/web/WebPagePopupImpl.h index d1e9163..4e434ab 100644 --- a/third_party/WebKit/Source/web/WebPagePopupImpl.h +++ b/third_party/WebKit/Source/web/WebPagePopupImpl.h
@@ -74,7 +74,7 @@ private: // WebWidget functions WebSize size() override; - void beginFrame(const WebBeginFrameArgs&) override; + void beginFrame(double lastFrameTimeMonotonic) override; void layout() override; void willCloseLayerTreeView() override; void paint(WebCanvas*, const WebRect&) override;
diff --git a/third_party/WebKit/Source/web/WebViewFrameWidget.cpp b/third_party/WebKit/Source/web/WebViewFrameWidget.cpp index 30de52e..e160d23 100644 --- a/third_party/WebKit/Source/web/WebViewFrameWidget.cpp +++ b/third_party/WebKit/Source/web/WebViewFrameWidget.cpp
@@ -72,9 +72,9 @@ return m_webView->didExitFullScreen(); } -void WebViewFrameWidget::beginFrame(const WebBeginFrameArgs& frameTime) +void WebViewFrameWidget::beginFrame(double lastFrameTimeMonotonic) { - return m_webView->beginFrame(frameTime); + return m_webView->beginFrame(lastFrameTimeMonotonic); } void WebViewFrameWidget::layout()
diff --git a/third_party/WebKit/Source/web/WebViewFrameWidget.h b/third_party/WebKit/Source/web/WebViewFrameWidget.h index 32b7f18..4c1db73 100644 --- a/third_party/WebKit/Source/web/WebViewFrameWidget.h +++ b/third_party/WebKit/Source/web/WebViewFrameWidget.h
@@ -46,7 +46,7 @@ void willEndLiveResize() override; void didEnterFullScreen() override; void didExitFullScreen() override; - void beginFrame(const WebBeginFrameArgs& frameTime) override; + void beginFrame(double lastFrameTimeMonotonic) override; void layout() override; void paint(WebCanvas*, const WebRect& viewPort) override; void paintCompositedDeprecated(WebCanvas*, const WebRect&) override;
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp index f508e81..0caf920 100644 --- a/third_party/WebKit/Source/web/WebViewImpl.cpp +++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -131,7 +131,6 @@ #include "public/web/WebAXObject.h" #include "public/web/WebActiveWheelFlingParameters.h" #include "public/web/WebAutofillClient.h" -#include "public/web/WebBeginFrameArgs.h" #include "public/web/WebElement.h" #include "public/web/WebFrame.h" #include "public/web/WebFrameClient.h" @@ -1864,14 +1863,14 @@ m_fullscreenController->didExitFullScreen(); } -void WebViewImpl::beginFrame(const WebBeginFrameArgs& frameTime) +void WebViewImpl::beginFrame(double lastFrameTimeMonotonic) { - TRACE_EVENT1("blink", "WebViewImpl::beginFrame", "frameTime", frameTime.lastFrameTimeMonotonic); - ASSERT(frameTime.lastFrameTimeMonotonic); + TRACE_EVENT1("blink", "WebViewImpl::beginFrame", "frameTime", lastFrameTimeMonotonic); + ASSERT(lastFrameTimeMonotonic); // Create synthetic wheel events as necessary for fling. if (m_gestureAnimation) { - if (m_gestureAnimation->animate(frameTime.lastFrameTimeMonotonic)) + if (m_gestureAnimation->animate(lastFrameTimeMonotonic)) scheduleAnimation(); else { endActiveFlingAnimation(); @@ -1888,7 +1887,7 @@ if (!m_page) return; - PageWidgetDelegate::animate(*m_page, frameTime.lastFrameTimeMonotonic); + PageWidgetDelegate::animate(*m_page, lastFrameTimeMonotonic); } void WebViewImpl::layout()
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h index 000d805a..febe2c0 100644 --- a/third_party/WebKit/Source/web/WebViewImpl.h +++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -109,7 +109,7 @@ void didEnterFullScreen() override; void didExitFullScreen() override; - void beginFrame(const WebBeginFrameArgs&) override; + void beginFrame(double lastFrameTimeMonotonic) override; void layout() override; void paint(WebCanvas*, const WebRect&) override;
diff --git a/third_party/WebKit/Source/web/tests/sim/SimCompositor.cpp b/third_party/WebKit/Source/web/tests/sim/SimCompositor.cpp index 3bdc736..59e393e2 100644 --- a/third_party/WebKit/Source/web/tests/sim/SimCompositor.cpp +++ b/third_party/WebKit/Source/web/tests/sim/SimCompositor.cpp
@@ -75,8 +75,7 @@ // Always advance the time as if the compositor was running at 60fps. m_lastFrameTimeMonotonic = monotonicallyIncreasingTime() + 0.016; - WebBeginFrameArgs args(m_lastFrameTimeMonotonic, 0, 0); - m_webViewImpl->beginFrame(args); + m_webViewImpl->beginFrame(m_lastFrameTimeMonotonic); m_webViewImpl->layout(); LocalFrame* root = m_webViewImpl->mainFrameImpl()->frame();
diff --git a/third_party/WebKit/Source/web/web.gypi b/third_party/WebKit/Source/web/web.gypi index ff62502..100c957 100644 --- a/third_party/WebKit/Source/web/web.gypi +++ b/third_party/WebKit/Source/web/web.gypi
@@ -118,7 +118,6 @@ 'WebCSSParser.cpp', 'WebDOMActivityLogger.cpp', 'WebDOMCustomEvent.cpp', - 'WebDOMError.cpp', 'WebDOMEvent.cpp', 'WebDOMFileSystem.cpp', 'WebDOMMediaStreamTrack.cpp',
diff --git a/third_party/WebKit/public/blink_headers.gypi b/third_party/WebKit/public/blink_headers.gypi index 87ba6ef4..55b7ffb 100644 --- a/third_party/WebKit/public/blink_headers.gypi +++ b/third_party/WebKit/public/blink_headers.gypi
@@ -337,7 +337,6 @@ "web/WebArrayBufferConverter.h", "web/WebArrayBufferView.h", "web/WebAutofillClient.h", - "web/WebBeginFrameArgs.h", "web/WebBindings.h", "web/WebBlob.h", "web/WebCSSParser.h", @@ -356,7 +355,6 @@ "web/WebCustomElement.h", "web/WebDOMActivityLogger.h", "web/WebDOMCustomEvent.h", - "web/WebDOMError.h", "web/WebDOMEvent.h", "web/WebDOMFileSystem.h", "web/WebDOMMediaStreamTrack.h",
diff --git a/third_party/WebKit/public/web/WebBeginFrameArgs.h b/third_party/WebKit/public/web/WebBeginFrameArgs.h deleted file mode 100644 index 5a31028..0000000 --- a/third_party/WebKit/public/web/WebBeginFrameArgs.h +++ /dev/null
@@ -1,35 +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 WebBeginFrameArgs_h -#define WebBeginFrameArgs_h - -namespace blink { - -struct WebBeginFrameArgs { - WebBeginFrameArgs(double lastFrameTimeMonotonic, double deadline, double interval) - : lastFrameTimeMonotonic(lastFrameTimeMonotonic) - , deadline(deadline) - , interval(interval) - { } - - // FIXME: Upgrade the time in CLOCK_MONOTONIC values to use a TimeTick like - // class rather than a bare double. - - // FIXME: Extend this class to include the fields from Chrome - // BeginFrameArgs structure. - - // Time in CLOCK_MONOTONIC that is the most recent vsync time. - double lastFrameTimeMonotonic; - - // Time in CLOCK_MONOTONIC by which the renderer should finish producing the current frame. 0 means a deadline wasn't set. - double deadline; - - // Expected delta between two successive frame times. 0 if a regular interval isn't available. - double interval; -}; - -} // namespace blink - -#endif
diff --git a/third_party/WebKit/public/web/WebDOMError.h b/third_party/WebKit/public/web/WebDOMError.h deleted file mode 100644 index 44c4c47..0000000 --- a/third_party/WebKit/public/web/WebDOMError.h +++ /dev/null
@@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WebDOMError_h -#define WebDOMError_h - -#include "public/platform/WebCommon.h" -#include "public/platform/WebPrivatePtr.h" -#include "public/platform/WebString.h" - -namespace v8 { -class Isolate; -class Object; -class Value; -template <class T> class Local; -} - -namespace blink { - -class DOMError; - -class WebDOMError { -public: - ~WebDOMError() { reset(); } - - WebDOMError() { } - WebDOMError(const WebDOMError& b) { assign(b); } - WebDOMError& operator=(const WebDOMError& b) - { - assign(b); - return *this; - } - - BLINK_EXPORT static WebDOMError create(const WebString& name, const WebString& message); - - BLINK_EXPORT void reset(); - BLINK_EXPORT void assign(const WebDOMError&); - - BLINK_EXPORT WebString name() const; - BLINK_EXPORT WebString message() const; - - BLINK_EXPORT v8::Local<v8::Value> toV8Value(v8::Local<v8::Object> creationContext, v8::Isolate*); - -#if BLINK_IMPLEMENTATION - WebDOMError(DOMError*); - WebDOMError& operator=(DOMError*); -#endif - -protected: - WebPrivatePtr<DOMError> m_private; -}; - -} // namespace blink - -#endif // WebDOMError_h
diff --git a/third_party/WebKit/public/web/WebWidget.h b/third_party/WebKit/public/web/WebWidget.h index e9f186a8..aacecf1 100644 --- a/third_party/WebKit/public/web/WebWidget.h +++ b/third_party/WebKit/public/web/WebWidget.h
@@ -40,7 +40,6 @@ #include "../platform/WebRect.h" #include "../platform/WebSize.h" #include "../platform/WebTopControlsState.h" -#include "WebBeginFrameArgs.h" #include "WebCompositionUnderline.h" #include "WebTextDirection.h" #include "WebTextInputInfo.h" @@ -93,7 +92,7 @@ // Called to update imperative animation state. This should be called before // paint, although the client can rate-limit these calls. - virtual void beginFrame(const WebBeginFrameArgs& frameTime) { } + virtual void beginFrame(double lastFrameTimeMonotonic) { } // Called to layout the WebWidget. This MUST be called before Paint, // and it may result in calls to WebWidgetClient::didInvalidateRect.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 75b4374..cf7be26 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -5820,6 +5820,20 @@ </summary> </histogram> +<histogram name="DataReductionProxy.EnabledState" + enum="DataReductionProxyEnabledState"> + <owner>bengr@chromium.org</owner> + <owner>rajendrant@chromium.org</owner> + <summary> + Samples of user interactions with the Data Saver settings menu across all + platforms (android, IOS, desktop extension). Data is collected whenever user + toggles the ON/OFF switch in the settings menu for reducing data usage. + + For desktop, Off-to-On/On-to-Off histogram count will increment when the + data saver extension is installed/uninstalled respectively as well. + </summary> +</histogram> + <histogram name="DataReductionProxy.HeaderTamperDetectionHTTP"> <owner>xingx@chromium.org</owner> <owner>bolian@chromium.org</owner> @@ -55090,6 +55104,11 @@ <int value="10" label="Bypass due to any network error"/> </enum> +<enum name="DataReductionProxyEnabledState" type="int"> + <int value="0" label="Off-to-On"/> + <int value="1" label="On-to-Off"/> +</enum> + <enum name="DataReductionProxyLoFiImplicitOptOutAction" type="int"> <int value="0" label="Lo-Fi disabled for the remainder of the session"/> <int value="1" label="Lo-Fi disabled until next opt out epoch"/>
diff --git a/tools/telemetry/telemetry/timeline/thread.py b/tools/telemetry/telemetry/timeline/thread.py index 23ed4ce..f04f9da 100644 --- a/tools/telemetry/telemetry/timeline/thread.py +++ b/tools/telemetry/telemetry/timeline/thread.py
@@ -136,11 +136,10 @@ raise ValueError( 'Slice %s end time is before its start.' % curr_slice.name) curr_slice.duration = end_timestamp - curr_slice.start - if end_thread_timestamp != None: - if curr_slice.thread_start == None: - raise ValueError( - 'EndSlice with thread_timestamp called on open slice without ' + - 'thread_timestamp') + # On Windows, it is possible to have a value for |end_thread_timestamp| + # but not for |curr_slice.thread_start|, because it takes some time to + # initialize the thread time timer. + if curr_slice.thread_start != None and end_thread_timestamp != None: curr_slice.thread_duration = (end_thread_timestamp - curr_slice.thread_start) curr_slice.did_not_finish = False
diff --git a/tools/valgrind/gtest_exclude/ipc_tests.gtest_mac.txt b/tools/valgrind/gtest_exclude/ipc_tests.gtest_mac.txt new file mode 100644 index 0000000..592197a --- /dev/null +++ b/tools/valgrind/gtest_exclude/ipc_tests.gtest_mac.txt
@@ -0,0 +1,2 @@ +# Flaky under Valgrind. See http://crbug.com/543429 +IPCAttachmentBrokerMacTest.SendSharedMemoryHandleToSelf
diff --git a/tools/valgrind/gtest_exclude/remoting_unittests.gtest_mac.txt b/tools/valgrind/gtest_exclude/remoting_unittests.gtest_mac.txt new file mode 100644 index 0000000..585b98401 --- /dev/null +++ b/tools/valgrind/gtest_exclude/remoting_unittests.gtest_mac.txt
@@ -0,0 +1,2 @@ +# Failing on Mac. https://crbug.com/543431 +JingleSessionTest.TestQuicStreamChannel
diff --git a/ui/base/ime/dummy_text_input_client.cc b/ui/base/ime/dummy_text_input_client.cc index c660bd92..dcc8fb4 100644 --- a/ui/base/ime/dummy_text_input_client.cc +++ b/ui/base/ime/dummy_text_input_client.cc
@@ -29,8 +29,7 @@ void DummyTextInputClient::InsertText(const base::string16& text) { } -void DummyTextInputClient::InsertChar(base::char16 ch, int flags) { -} +void DummyTextInputClient::InsertChar(const KeyEvent& event) {} TextInputType DummyTextInputClient::GetTextInputType() const { return text_input_type_;
diff --git a/ui/base/ime/dummy_text_input_client.h b/ui/base/ime/dummy_text_input_client.h index 2c26f89e..7f5f7f3 100644 --- a/ui/base/ime/dummy_text_input_client.h +++ b/ui/base/ime/dummy_text_input_client.h
@@ -21,7 +21,7 @@ void ConfirmCompositionText() override; void ClearCompositionText() override; void InsertText(const base::string16& text) override; - void InsertChar(base::char16 ch, int flags) override; + void InsertChar(const KeyEvent& event) override; TextInputType GetTextInputType() const override; TextInputMode GetTextInputMode() const override; int GetTextInputFlags() const override;
diff --git a/ui/base/ime/input_method_auralinux.cc b/ui/base/ime/input_method_auralinux.cc index ade6577..ca9931f 100644 --- a/ui/base/ime/input_method_auralinux.cc +++ b/ui/base/ime/input_method_auralinux.cc
@@ -97,8 +97,11 @@ // Processes the result text before composition for sync mode. if (client && !result_text_.empty()) { if (filtered && NeedInsertChar()) { - for (const auto ch : result_text_) - client->InsertChar(ch, event->flags()); + for (const auto ch : result_text_) { + ui::KeyEvent ch_event(*event); + ch_event.set_character(ch); + client->InsertChar(ch_event); + } } else { // If |filtered| is false, that means the IME wants to commit some text // but still release the key to the application. For example, Korean IME @@ -146,7 +149,7 @@ // DispatchKeyEventPostIME may cause the current text input client change. base::char16 ch = event->GetCharacter(); if (ch && GetTextInputClient()) - GetTextInputClient()->InsertChar(ch, event->flags()); + GetTextInputClient()->InsertChar(*event); should_stop_propagation = true; } }
diff --git a/ui/base/ime/input_method_auralinux_unittest.cc b/ui/base/ime/input_method_auralinux_unittest.cc index 7716e9ed..ba713416 100644 --- a/ui/base/ime/input_method_auralinux_unittest.cc +++ b/ui/base/ime/input_method_auralinux_unittest.cc
@@ -222,9 +222,9 @@ composition_text.clear(); } - void InsertChar(base::char16 ch, int flags) override { + void InsertChar(const ui::KeyEvent& event) override { std::stringstream ss; - ss << ch; + ss << event.GetCharacter(); TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16("keypress:") + base::ASCIIToUTF16(ss.str())); }
diff --git a/ui/base/ime/input_method_chromeos.cc b/ui/base/ime/input_method_chromeos.cc index 1665269..a2ee046f 100644 --- a/ui/base/ime/input_method_chromeos.cc +++ b/ui/base/ime/input_method_chromeos.cc
@@ -414,7 +414,7 @@ // to send corresponding character to the focused text input client. uint16 ch = event->GetCharacter(); if (ch) - client->InsertChar(ch, event->flags()); + client->InsertChar(*event); } void InputMethodChromeOS::ProcessInputMethodResult(ui::KeyEvent* event, @@ -426,7 +426,9 @@ if (handled && NeedInsertChar()) { for (base::string16::const_iterator i = result_text_.begin(); i != result_text_.end(); ++i) { - client->InsertChar(*i, event->flags()); + ui::KeyEvent ch_event(*event); + ch_event.set_character(*i); + client->InsertChar(ch_event); } } else { client->InsertText(result_text_);
diff --git a/ui/base/ime/input_method_chromeos_unittest.cc b/ui/base/ime/input_method_chromeos_unittest.cc index c1dcdf21..0ca3c32 100644 --- a/ui/base/ime/input_method_chromeos_unittest.cc +++ b/ui/base/ime/input_method_chromeos_unittest.cc
@@ -256,9 +256,9 @@ void InsertText(const base::string16& text) override { inserted_text_ = text; } - void InsertChar(base::char16 ch, int flags) override { - inserted_char_ = ch; - inserted_char_flags_ = flags; + void InsertChar(const KeyEvent& event) override { + inserted_char_ = event.GetCharacter(); + inserted_char_flags_ = event.flags(); } TextInputType GetTextInputType() const override { return input_type_; } TextInputMode GetTextInputMode() const override { return input_mode_; }
diff --git a/ui/base/ime/input_method_minimal.cc b/ui/base/ime/input_method_minimal.cc index cd007021..9cb61612 100644 --- a/ui/base/ime/input_method_minimal.cc +++ b/ui/base/ime/input_method_minimal.cc
@@ -37,7 +37,7 @@ if (event->type() == ET_KEY_PRESSED && GetTextInputClient()) { const uint16 ch = event->GetCharacter(); if (ch) { - GetTextInputClient()->InsertChar(ch, event->flags()); + GetTextInputClient()->InsertChar(*event); event->StopPropagation(); } }
diff --git a/ui/base/ime/input_method_win.cc b/ui/base/ime/input_method_win.cc index 484be060..ef500a7 100644 --- a/ui/base/ime/input_method_win.cc +++ b/ui/base/ime/input_method_win.cc
@@ -82,8 +82,8 @@ break; case WM_CHAR: case WM_SYSCHAR: - original_result = OnChar( - event.hwnd, event.message, event.wParam, event.lParam, &handled); + original_result = OnChar(event.hwnd, event.message, event.wParam, + event.lParam, event, &handled); break; case WM_IME_NOTIFY: original_result = OnImeNotify( @@ -108,7 +108,8 @@ BOOL handled = FALSE; if (native_key_event.message == WM_CHAR) { OnChar(native_key_event.hwnd, native_key_event.message, - native_key_event.wParam, native_key_event.lParam, &handled); + native_key_event.wParam, native_key_event.lParam, native_key_event, + &handled); if (handled) event->StopPropagation(); return; @@ -175,7 +176,7 @@ for (size_t i = 0; i < char_msgs.size(); ++i) { MSG msg = char_msgs[i]; - OnChar(msg.hwnd, msg.message, msg.wParam, msg.lParam, &handled); + OnChar(msg.hwnd, msg.message, msg.wParam, msg.lParam, msg, &handled); } } @@ -255,6 +256,7 @@ UINT message, WPARAM wparam, LPARAM lparam, + const base::NativeEvent& event, BOOL* handled) { *handled = TRUE; @@ -271,8 +273,10 @@ accept_carriage_return_ = true; // Conditionally ignore '\r' events to work around crbug.com/319100. // TODO(yukawa, IME): Figure out long-term solution. - if (ch != kCarriageReturn || accept_carriage_return_) - GetTextInputClient()->InsertChar(ch, ui::GetModifiersFromKeyState()); + if (ch != kCarriageReturn || accept_carriage_return_) { + ui::KeyEvent char_event(event); + GetTextInputClient()->InsertChar(char_event); + } } // Explicitly show the system menu at a good location on [Alt]+[Space]. @@ -589,9 +593,9 @@ void InputMethodWin::DispatchFabricatedKeyEvent(ui::KeyEvent* event) { if (event->is_char()) { if (GetTextInputClient()) { - GetTextInputClient()->InsertChar( - static_cast<base::char16>(event->key_code()), - ui::GetModifiersFromKeyState()); + ui::KeyEvent ch_event(*event); + ch_event.set_character(static_cast<base::char16>(event->key_code())); + GetTextInputClient()->InsertChar(ch_event); return; } }
diff --git a/ui/base/ime/input_method_win.h b/ui/base/ime/input_method_win.h index 7cc347a..d813e4ea 100644 --- a/ui/base/ime/input_method_win.h +++ b/ui/base/ime/input_method_win.h
@@ -50,6 +50,7 @@ UINT message, WPARAM wparam, LPARAM lparam, + const base::NativeEvent& event, BOOL* handled); LRESULT OnImeSetContext(HWND window_handle,
diff --git a/ui/base/ime/remote_input_method_win.cc b/ui/base/ime/remote_input_method_win.cc index 1a589bb..e9f7fc2 100644 --- a/ui/base/ime/remote_input_method_win.cc +++ b/ui/base/ime/remote_input_method_win.cc
@@ -183,9 +183,7 @@ if (event->HasNativeEvent()) { const base::NativeEvent& native_key_event = event->native_event(); if (native_key_event.message == WM_CHAR && text_input_client_) { - text_input_client_->InsertChar( - static_cast<base::char16>(native_key_event.wParam), - ui::GetModifiersFromKeyState()); + text_input_client_->InsertChar(*event); event->StopPropagation(); } return; @@ -193,9 +191,7 @@ if (event->is_char()) { if (text_input_client_) { - text_input_client_->InsertChar( - event->GetCharacter(), - ui::GetModifiersFromKeyState()); + text_input_client_->InsertChar(*event); } event->StopPropagation(); return; @@ -319,8 +315,12 @@ // According to the comment in text_input_client.h, // TextInputClient::InsertText should never be called when the // text input type is TEXT_INPUT_TYPE_NONE. - for (size_t i = 0; i < text.size(); ++i) - text_input_client_->InsertChar(text[i], 0); + + for (size_t i = 0; i < text.size(); ++i) { + ui::KeyEvent char_event(text[i], static_cast<ui::KeyboardCode>(text[i]), + ui::EF_NONE); + text_input_client_->InsertChar(char_event); + } return; } text_input_client_->InsertText(text);
diff --git a/ui/base/ime/remote_input_method_win_unittest.cc b/ui/base/ime/remote_input_method_win_unittest.cc index 686937b..0970de0 100644 --- a/ui/base/ime/remote_input_method_win_unittest.cc +++ b/ui/base/ime/remote_input_method_win_unittest.cc
@@ -79,8 +79,8 @@ void SetCompositionText(const ui::CompositionText& composition) override { ++call_count_set_composition_text_; } - void InsertChar(base::char16 ch, int flags) override { - inserted_text_.append(1, ch); + void InsertChar(const ui::KeyEvent& event) override { + inserted_text_.append(1, event.GetCharacter()); ++call_count_insert_char_; } void InsertText(const base::string16& text) override {
diff --git a/ui/base/ime/text_input_client.h b/ui/base/ime/text_input_client.h index 390a96b..ae2ee6f7 100644 --- a/ui/base/ime/text_input_client.h +++ b/ui/base/ime/text_input_client.h
@@ -21,6 +21,8 @@ namespace ui { +class KeyEvent; + // An interface implemented by a View that needs text input support. class UI_BASE_IME_EXPORT TextInputClient { public: @@ -46,14 +48,14 @@ virtual void InsertText(const base::string16& text) = 0; // Inserts a single char at the insertion point. Unlike above InsertText() - // method, this method has an extra |flags| parameter indicating the modifier - // key states when the character is generated. This method should only be + // method, this method takes an |event| parameter indicating + // the event that was unprocessed. This method should only be // called when a key press is not handled by the input method but still // generates a character (eg. by the keyboard driver). In another word, the // preceding key press event should not be a VKEY_PROCESSKEY. // This method will be called whenever a char is generated by the keyboard, // even if the current text input type is TEXT_INPUT_TYPE_NONE. - virtual void InsertChar(base::char16 ch, int flags) = 0; + virtual void InsertChar(const ui::KeyEvent& event) = 0; // Input context information -------------------------------------------------
diff --git a/ui/events/win/events_win.cc b/ui/events/win/events_win.cc index 52d1f47..1f50e5d8 100644 --- a/ui/events/win/events_win.cc +++ b/ui/events/win/events_win.cc
@@ -91,6 +91,7 @@ return native_event.message == WM_KEYDOWN || native_event.message == WM_SYSKEYDOWN || native_event.message == WM_CHAR || + native_event.message == WM_SYSCHAR || native_event.message == WM_KEYUP || native_event.message == WM_SYSKEYUP; } @@ -150,6 +151,7 @@ case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_CHAR: + case WM_SYSCHAR: return ET_KEY_PRESSED; // The WM_DEADCHAR message is posted to the window with the keyboard focus // when a WM_KEYUP message is translated. This happens for special keyboard @@ -265,7 +267,7 @@ } bool IsCharFromNative(const base::NativeEvent& native_event) { - return native_event.message == WM_CHAR; + return native_event.message == WM_CHAR || native_event.message == WM_SYSCHAR; } int GetChangedMouseButtonFlagsFromNative(
diff --git a/ui/file_manager/video_player/css/header.css b/ui/file_manager/video_player/css/header.css deleted file mode 100644 index 06c27b6..0000000 --- a/ui/file_manager/video_player/css/header.css +++ /dev/null
@@ -1,88 +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. */ - -#video-player > .header { - -webkit-app-region: drag; - background: rgba(30, 30, 30, 0.8); - border-bottom: 1px solid rgba(50, 50, 50, 0.8); - display: flex; - flex-direction: row; - height: 45px; - opacity: 1; - pointer-events: auto; - position: absolute; - top: 0; - width: 100%; - z-index: 200; -} - -#video-player > .header > #title { - color: white; - flex-grow: 1; - font-size: 16px; - line-height: 45px; - overflow: hidden; - padding-left: 15px; - text-overflow: ellipsis; - white-space: nowrap; -} - -#video-player > .header > button, -#video-player > .header > button[disabled] { - align-content: center; - background-color: rgba(0, 0, 0, 0); - background-position: center; - background-repeat: no-repeat; - border: none; - box-shadow: none; - color: white; - cursor: pointer; - padding: 1px; /* Instead of a border. */ - position: relative; - z-index: 10; -} - -#video-player > .header > button { - height: 40px; - margin: 6px 0; - min-width: 40px; /* Reset. */ - width: 40px; -} - -#video-player > .header > button:hover { - background-color: rgba(240, 240, 240, 1); -} - -#video-player > .header > button:active, -#video-player > .header > button[pressed], -#video-player > .header > button[pressed]:hover { - color: black; -} - -#video-player > .header > button { - -webkit-app-region: no-drag; - -webkit-margin-end: 10px; - cursor: default; - height: 32px; - min-width: 32px; - width: 32px; -} - -#video-player > .header > button.minimize-button { - background-image: -webkit-image-set( - url(chrome://resources/images/apps/topbar_button_minimize.png) 1x, - url(chrome://resources/images/2x/apps/topbar_button_minimize.png) 2x); -} - -#video-player > .header > button.maximize-button { - background-image: -webkit-image-set( - url(chrome://resources/images/apps/topbar_button_maximize.png) 1x, - url(chrome://resources/images/2x/apps/topbar_button_maximize.png) 2x); -} - -#video-player > .header > button.close-button { - background-image: -webkit-image-set( - url(chrome://resources/images/apps/topbar_button_close.png) 1x, - url(chrome://resources/images/2x/apps/topbar_button_close.png) 2x); -}
diff --git a/ui/file_manager/video_player/images/icon/video-player-128.png b/ui/file_manager/video_player/images/icon/video-player-128.png index 4ba890e..bca680d 100644 --- a/ui/file_manager/video_player/images/icon/video-player-128.png +++ b/ui/file_manager/video_player/images/icon/video-player-128.png Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-256.png b/ui/file_manager/video_player/images/icon/video-player-256.png index b6b3d3f..669ca83 100644 --- a/ui/file_manager/video_player/images/icon/video-player-256.png +++ b/ui/file_manager/video_player/images/icon/video-player-256.png Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-32.png b/ui/file_manager/video_player/images/icon/video-player-32.png index e5ac0d6..31d8e21 100644 --- a/ui/file_manager/video_player/images/icon/video-player-32.png +++ b/ui/file_manager/video_player/images/icon/video-player-32.png Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-48.png b/ui/file_manager/video_player/images/icon/video-player-48.png index f3c342d7..ce573b3 100644 --- a/ui/file_manager/video_player/images/icon/video-player-48.png +++ b/ui/file_manager/video_player/images/icon/video-player-48.png Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-64.png b/ui/file_manager/video_player/images/icon/video-player-64.png index 0e722b69..286e725 100644 --- a/ui/file_manager/video_player/images/icon/video-player-64.png +++ b/ui/file_manager/video_player/images/icon/video-player-64.png Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-96.png b/ui/file_manager/video_player/images/icon/video-player-96.png index fc2b5bd..e757329 100644 --- a/ui/file_manager/video_player/images/icon/video-player-96.png +++ b/ui/file_manager/video_player/images/icon/video-player-96.png Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-favicon-16.png b/ui/file_manager/video_player/images/icon/video-player-favicon-16.png index b0ab880..4a5bee1 100644 --- a/ui/file_manager/video_player/images/icon/video-player-favicon-16.png +++ b/ui/file_manager/video_player/images/icon/video-player-favicon-16.png Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-favicon-32.png b/ui/file_manager/video_player/images/icon/video-player-favicon-32.png index 5168798..53927bd 100644 --- a/ui/file_manager/video_player/images/icon/video-player-favicon-32.png +++ b/ui/file_manager/video_player/images/icon/video-player-favicon-32.png Binary files differ
diff --git a/ui/file_manager/video_player/js/background.js b/ui/file_manager/video_player/js/background.js index 3b7adacb..3df0cdb1 100644 --- a/ui/file_manager/video_player/js/background.js +++ b/ui/file_manager/video_player/js/background.js
@@ -17,7 +17,9 @@ * @type {Object} */ var windowCreateOptions = { - frame: 'none', + frame: { + color: '#fafafa' + }, minWidth: 480, minHeight: 270 };
diff --git a/ui/file_manager/video_player/js/video_player.js b/ui/file_manager/video_player/js/video_player.js index dff5801..a9c39133 100644 --- a/ui/file_manager/video_player/js/video_player.js +++ b/ui/file_manager/video_player/js/video_player.js
@@ -218,37 +218,6 @@ document.ondragstart = preventDefault; - var maximizeButton = queryRequiredElement('.maximize-button'); - maximizeButton.addEventListener( - 'click', - function(event) { - var appWindow = chrome.app.window.current(); - if (appWindow.isMaximized()) - appWindow.restore(); - else - appWindow.maximize(); - event.stopPropagation(); - }.wrap(null)); - maximizeButton.addEventListener('mousedown', preventDefault); - - var minimizeButton = queryRequiredElement('.minimize-button'); - minimizeButton.addEventListener( - 'click', - function(event) { - chrome.app.window.current().minimize(); - event.stopPropagation(); - }.wrap(null)); - minimizeButton.addEventListener('mousedown', preventDefault); - - var closeButton = queryRequiredElement('.close-button'); - closeButton.addEventListener( - 'click', - function(event) { - window.close(); - event.stopPropagation(); - }.wrap(null)); - closeButton.addEventListener('mousedown', preventDefault); - cr.ui.decorate(getRequiredElement('cast-menu'), cr.ui.Menu); this.controls_ = new FullWindowVideoControls( @@ -308,8 +277,6 @@ this.loadQueue_.run(function(callback) { document.title = video.name; - getRequiredElement('title').innerText = video.name; - var videoPlayerElement = getRequiredElement('video-player'); if (this.currentPos_ === (this.videos_.length - 1)) videoPlayerElement.setAttribute('last-video', true);
diff --git a/ui/file_manager/video_player/video_player.html b/ui/file_manager/video_player/video_player.html index ede9ad0..ad3fcb3c 100644 --- a/ui/file_manager/video_player/video_player.html +++ b/ui/file_manager/video_player/video_player.html
@@ -13,7 +13,6 @@ <link rel="stylesheet" href="chrome://resources/css/menu.css"> <link rel="stylesheet" type="text/css" href="css/video_player.css"> - <link rel="stylesheet" type="text/css" href="css/header.css"> <link rel="stylesheet" type="text/css" href="css/arrow_box.css"> <link rel="stylesheet" type="text/css" href="css/cast_menu.css"> @@ -36,12 +35,6 @@ <div id="spinner-container"> <div class="spinner"></div> </div> - <div id="header-container" class="header tool"> - <div id="title"> </div> - <button class="minimize-button tool" tabindex="-1"></button> - <button class="maximize-button tool" tabindex="-1"></button> - <button class="close-button tool" tabindex="-1"></button> - </div> <div id="controls-wrapper"> <div id="controls" class="tool"></div> </div>
diff --git a/ui/keyboard/keyboard_util.cc b/ui/keyboard/keyboard_util.cc index 2a5ae6a..4d2cefa 100644 --- a/ui/keyboard/keyboard_util.cc +++ b/ui/keyboard/keyboard_util.cc
@@ -319,7 +319,9 @@ ui::TextInputClient* tic = input_method->GetTextInputClient(); SendProcessKeyEvent(ui::ET_KEY_PRESSED, host); - tic->InsertChar(static_cast<uint16>(key_value), ui::EF_NONE); + + ui::KeyEvent char_event(key_value, code, ui::EF_NONE); + tic->InsertChar(char_event); SendProcessKeyEvent(ui::ET_KEY_RELEASED, host); } } else {
diff --git a/ui/views/cocoa/bridged_content_view.mm b/ui/views/cocoa/bridged_content_view.mm index 43a95a8..c30e7c7 100644 --- a/ui/views/cocoa/bridged_content_view.mm +++ b/ui/views/cocoa/bridged_content_view.mm
@@ -639,10 +639,14 @@ // processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in // |text| but sending 'Alt' to InsertChar would filter it out since it thinks // it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:. - if (inKeyDown_ && [text length] == 1) - textInputClient_->InsertChar([text characterAtIndex:0], 0); - else + if (inKeyDown_ && [text length] == 1) { + ui::KeyEvent char_event( + [text characterAtIndex:0], + static_cast<ui::KeyboardCode>([text characterAtIndex:0]), ui::EF_NONE); + textInputClient_->InsertChar(char_event); + } else { textInputClient_->InsertText(base::SysNSStringToUTF16(text)); + } } - (NSRange)markedRange {
diff --git a/ui/views/controls/combobox/combobox_unittest.cc b/ui/views/controls/combobox/combobox_unittest.cc index 0161c12..0d1211c 100644 --- a/ui/views/controls/combobox/combobox_unittest.cc +++ b/ui/views/controls/combobox/combobox_unittest.cc
@@ -15,6 +15,7 @@ #include "ui/events/event.h" #include "ui/events/event_constants.h" #include "ui/events/event_utils.h" +#include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/test/combobox_test_api.h" @@ -695,13 +696,19 @@ widget_->GetInputMethod()->GetTextInputClient(); // Type the first character of the second menu item ("JELLY"). - input_client->InsertChar('J', ui::EF_NONE); + ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_J, ui::DomCode::KEY_J, 0, + ui::DomKey::FromCharacter('J'), ui::EventTimeForNow()); + + input_client->InsertChar(key_event); EXPECT_EQ(1, listener.actions_performed()); EXPECT_EQ(1, listener.perform_action_index()); // Type the second character of "JELLY", item shouldn't change and // OnPerformAction() shouldn't be re-called. - input_client->InsertChar('E', ui::EF_NONE); + key_event = + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_E, ui::DomCode::KEY_E, 0, + ui::DomKey::FromCharacter('E'), ui::EventTimeForNow()); + input_client->InsertChar(key_event); EXPECT_EQ(1, listener.actions_performed()); EXPECT_EQ(1, listener.perform_action_index()); @@ -711,7 +718,10 @@ // Type the first character of "PEANUT BUTTER", which should change the // selected index and perform an action. - input_client->InsertChar('P', ui::EF_NONE); + key_event = + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_E, ui::DomCode::KEY_P, 0, + ui::DomKey::FromCharacter('P'), ui::EventTimeForNow()); + input_client->InsertChar(key_event); EXPECT_EQ(2, listener.actions_performed()); EXPECT_EQ(2, listener.perform_action_index()); }
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc index cd7fd41..b3a6ada 100644 --- a/ui/views/controls/menu/menu_controller.cc +++ b/ui/views/controls/menu/menu_controller.cc
@@ -2209,7 +2209,7 @@ } if (is_combobox_) { - item->GetSubmenu()->GetPrefixSelector()->InsertChar(character, 0); + item->GetSubmenu()->GetPrefixSelector()->InsertText(char_array); } else { // If no mnemonics found, look at first character of titles. details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic);
diff --git a/ui/views/controls/prefix_selector.cc b/ui/views/controls/prefix_selector.cc index 9b84cfa..723e0e40 100644 --- a/ui/views/controls/prefix_selector.cc +++ b/ui/views/controls/prefix_selector.cc
@@ -52,8 +52,8 @@ OnTextInput(text); } -void PrefixSelector::InsertChar(base::char16 ch, int flags) { - OnTextInput(base::string16(1, ch)); +void PrefixSelector::InsertChar(const ui::KeyEvent& event) { + OnTextInput(base::string16(1, event.GetCharacter())); } ui::TextInputType PrefixSelector::GetTextInputType() const {
diff --git a/ui/views/controls/prefix_selector.h b/ui/views/controls/prefix_selector.h index c3dcc369..2b3988bb 100644 --- a/ui/views/controls/prefix_selector.h +++ b/ui/views/controls/prefix_selector.h
@@ -29,7 +29,7 @@ void ConfirmCompositionText() override; void ClearCompositionText() override; void InsertText(const base::string16& text) override; - void InsertChar(base::char16 ch, int flags) override; + void InsertChar(const ui::KeyEvent& event) override; ui::TextInputType GetTextInputType() const override; ui::TextInputMode GetTextInputMode() const override; int GetTextInputFlags() const override;
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc index e11410b..039102b7 100644 --- a/ui/views/controls/textfield/textfield.cc +++ b/ui/views/controls/textfield/textfield.cc
@@ -1450,14 +1450,15 @@ OnAfterUserAction(); } -void Textfield::InsertChar(base::char16 ch, int flags) { +void Textfield::InsertChar(const ui::KeyEvent& event) { // Filter out all control characters, including tab and new line characters, // and all characters with Alt modifier (and Search on ChromeOS). But allow // characters with the AltGr modifier. On Windows AltGr is represented by // Alt+Ctrl or Right Alt, and on Linux it's a different flag that we don't // care about. + const base::char16 ch = event.GetCharacter(); const bool should_insert_char = ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && - !ui::IsSystemKeyModifier(flags); + !ui::IsSystemKeyModifier(event.flags()); if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char) return;
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h index fc61858..2cac671 100644 --- a/ui/views/controls/textfield/textfield.h +++ b/ui/views/controls/textfield/textfield.h
@@ -284,7 +284,7 @@ void ConfirmCompositionText() override; void ClearCompositionText() override; void InsertText(const base::string16& text) override; - void InsertChar(base::char16 ch, int flags) override; + void InsertChar(const ui::KeyEvent& event) override; ui::TextInputType GetTextInputType() const override; ui::TextInputMode GetTextInputMode() const override; int GetTextInputFlags() const override;
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc index 31748de..5e6e121 100644 --- a/ui/views/controls/textfield/textfield_unittest.cc +++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -172,7 +172,7 @@ } else if (key->type() == ui::ET_KEY_PRESSED) { base::char16 ch = key->GetCharacter(); if (ch) - client->InsertChar(ch, key->flags()); + client->InsertChar(*key); } } @@ -272,8 +272,8 @@ } // ui::TextInputClient overrides: - void InsertChar(base::char16 ch, int flags) override { - views::Textfield::InsertChar(ch, flags); + void InsertChar(const ui::KeyEvent& e) override { + views::Textfield::InsertChar(e); #if defined(OS_MACOSX) // On Mac, characters are inserted directly rather than attempting to get a // unicode character from the ui::KeyEvent (which isn't always possible).
diff --git a/ui/views/mus/input_method_mus.cc b/ui/views/mus/input_method_mus.cc index 05c8c72ce..a7222dd 100644 --- a/ui/views/mus/input_method_mus.cc +++ b/ui/views/mus/input_method_mus.cc
@@ -56,7 +56,7 @@ // character event, we instead check to see if this is a character event and // send out the key if it is. (We fallback to normal dispatch if it isn't.) if (event->is_char()) { - GetTextInputClient()->InsertChar(event->GetCharacter(), event->flags()); + GetTextInputClient()->InsertChar(*event); event->StopPropagation(); return; }