diff --git a/AUTHORS b/AUTHORS index 7ad2718..a933b97 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -755,6 +755,7 @@ Cathie Chen <cathiechen@tencent.com> Kyle Plumadore <kyle.plumadore@amd.com> Sunchang Li <johnstonli@tencent.com> +Yeol Park <peary2@gmail.com> BlackBerry Limited <*@blackberry.com> Code Aurora Forum <*@codeaurora.org>
diff --git a/BUILD.gn b/BUILD.gn index 73c12e9..87a552d 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -907,7 +907,7 @@ "//breakpad:minidump_dump", "//breakpad:minidump_stackwalk", "//breakpad:symupload", - "//tools/android/forwarder", + "//tools/android/forwarder2", ] }
diff --git a/DEPS b/DEPS index 182078d..da8c07b 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': 'c4cbd74a38232a0e9f1cc1cc8fb826bb06c577a9', + 'skia_revision': '47bf4c0009f1f2c86e831447e69475e50cbfc958', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -55,7 +55,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling build tools # and whatever else without interference from each other. - 'buildtools_revision': '86f7e41d9424b9d8faf66c601b129855217f9a08', + 'buildtools_revision': '5fd66957f08bb752dca714a591c84587c9d70762', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -99,7 +99,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. - 'libfuzzer_revision': 'eb9b8b0366f34b53cd2ffde6837f037728aa5e9c', + 'libfuzzer_revision': 'ad607143eab09705a9e96324d040b5768f35081d', } # Only these hosts are allowed for dependencies in this DEPS file. @@ -1018,6 +1018,8 @@ ] recursedeps = [ + # buildtools provides clang_format, libc++, and libc++abi + 'src/buildtools', # android_tools manages the NDK. 'src/third_party/android_tools', # ANGLE manages DEPS that it also owns the build files for, such as dEQP.
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn index 3ef9827..e092d23a 100644 --- a/android_webview/BUILD.gn +++ b/android_webview/BUILD.gn
@@ -54,61 +54,10 @@ } locale_pak_resources("locale_paks") { - sources = [ - "$root_out_dir/android_webview/locales/am.pak", - "$root_out_dir/android_webview/locales/ar.pak", - "$root_out_dir/android_webview/locales/bg.pak", - "$root_out_dir/android_webview/locales/bn.pak", - "$root_out_dir/android_webview/locales/ca.pak", - "$root_out_dir/android_webview/locales/cs.pak", - "$root_out_dir/android_webview/locales/da.pak", - "$root_out_dir/android_webview/locales/de.pak", - "$root_out_dir/android_webview/locales/el.pak", - "$root_out_dir/android_webview/locales/en-GB.pak", - "$root_out_dir/android_webview/locales/en-US.pak", - "$root_out_dir/android_webview/locales/es-419.pak", - "$root_out_dir/android_webview/locales/es.pak", - "$root_out_dir/android_webview/locales/et.pak", - "$root_out_dir/android_webview/locales/fa.pak", - "$root_out_dir/android_webview/locales/fi.pak", - "$root_out_dir/android_webview/locales/fil.pak", - "$root_out_dir/android_webview/locales/fr.pak", - "$root_out_dir/android_webview/locales/gu.pak", - "$root_out_dir/android_webview/locales/he.pak", - "$root_out_dir/android_webview/locales/hi.pak", - "$root_out_dir/android_webview/locales/hr.pak", - "$root_out_dir/android_webview/locales/hu.pak", - "$root_out_dir/android_webview/locales/id.pak", - "$root_out_dir/android_webview/locales/it.pak", - "$root_out_dir/android_webview/locales/ja.pak", - "$root_out_dir/android_webview/locales/kn.pak", - "$root_out_dir/android_webview/locales/ko.pak", - "$root_out_dir/android_webview/locales/lt.pak", - "$root_out_dir/android_webview/locales/lv.pak", - "$root_out_dir/android_webview/locales/ml.pak", - "$root_out_dir/android_webview/locales/mr.pak", - "$root_out_dir/android_webview/locales/ms.pak", - "$root_out_dir/android_webview/locales/nb.pak", - "$root_out_dir/android_webview/locales/nl.pak", - "$root_out_dir/android_webview/locales/pl.pak", - "$root_out_dir/android_webview/locales/pt-BR.pak", - "$root_out_dir/android_webview/locales/pt-PT.pak", - "$root_out_dir/android_webview/locales/ro.pak", - "$root_out_dir/android_webview/locales/ru.pak", - "$root_out_dir/android_webview/locales/sk.pak", - "$root_out_dir/android_webview/locales/sl.pak", - "$root_out_dir/android_webview/locales/sr.pak", - "$root_out_dir/android_webview/locales/sv.pak", - "$root_out_dir/android_webview/locales/sw.pak", - "$root_out_dir/android_webview/locales/ta.pak", - "$root_out_dir/android_webview/locales/te.pak", - "$root_out_dir/android_webview/locales/th.pak", - "$root_out_dir/android_webview/locales/tr.pak", - "$root_out_dir/android_webview/locales/uk.pak", - "$root_out_dir/android_webview/locales/vi.pak", - "$root_out_dir/android_webview/locales/zh-CN.pak", - "$root_out_dir/android_webview/locales/zh-TW.pak", - ] + sources = [] + foreach(_locale, locales) { + sources += [ "$root_out_dir/android_webview/locales/$_locale.pak" ] + } deps = [ ":repack_locales", ] @@ -232,60 +181,10 @@ source = "ui/aw_strings.grd" outputs = [ "grit/aw_strings.h", - "aw_strings_am.pak", - "aw_strings_ar.pak", - "aw_strings_bg.pak", - "aw_strings_bn.pak", - "aw_strings_ca.pak", - "aw_strings_cs.pak", - "aw_strings_da.pak", - "aw_strings_de.pak", - "aw_strings_el.pak", - "aw_strings_en-US.pak", - "aw_strings_en-GB.pak", - "aw_strings_es.pak", - "aw_strings_es-419.pak", - "aw_strings_et.pak", - "aw_strings_fa.pak", - "aw_strings_fi.pak", - "aw_strings_fil.pak", - "aw_strings_fr.pak", - "aw_strings_gu.pak", - "aw_strings_he.pak", - "aw_strings_hi.pak", - "aw_strings_hr.pak", - "aw_strings_hu.pak", - "aw_strings_id.pak", - "aw_strings_it.pak", - "aw_strings_ja.pak", - "aw_strings_kn.pak", - "aw_strings_ko.pak", - "aw_strings_lt.pak", - "aw_strings_lv.pak", - "aw_strings_ml.pak", - "aw_strings_mr.pak", - "aw_strings_ms.pak", - "aw_strings_nl.pak", - "aw_strings_nb.pak", - "aw_strings_pl.pak", - "aw_strings_pt-BR.pak", - "aw_strings_pt-PT.pak", - "aw_strings_ro.pak", - "aw_strings_ru.pak", - "aw_strings_sk.pak", - "aw_strings_sl.pak", - "aw_strings_sr.pak", - "aw_strings_sv.pak", - "aw_strings_sw.pak", - "aw_strings_ta.pak", - "aw_strings_te.pak", - "aw_strings_th.pak", - "aw_strings_tr.pak", - "aw_strings_uk.pak", - "aw_strings_vi.pak", - "aw_strings_zh-CN.pak", - "aw_strings_zh-TW.pak", ] + foreach(_locale, locales) { + outputs += [ "aw_strings_${_locale}.pak" ] + } } grit("generate_components_strings") { @@ -305,60 +204,6 @@ ] outputs = [ "grit/components_strings.h", - "components_strings_am.pak", - "components_strings_ar.pak", - "components_strings_bg.pak", - "components_strings_bn.pak", - "components_strings_ca.pak", - "components_strings_cs.pak", - "components_strings_da.pak", - "components_strings_de.pak", - "components_strings_el.pak", - "components_strings_en-GB.pak", - "components_strings_en-US.pak", - "components_strings_es.pak", - "components_strings_es-419.pak", - "components_strings_et.pak", - "components_strings_fa.pak", - "components_strings_fake-bidi.pak", - "components_strings_fi.pak", - "components_strings_fil.pak", - "components_strings_fr.pak", - "components_strings_gu.pak", - "components_strings_he.pak", - "components_strings_hi.pak", - "components_strings_hr.pak", - "components_strings_hu.pak", - "components_strings_id.pak", - "components_strings_it.pak", - "components_strings_ja.pak", - "components_strings_kn.pak", - "components_strings_ko.pak", - "components_strings_lt.pak", - "components_strings_lv.pak", - "components_strings_ml.pak", - "components_strings_mr.pak", - "components_strings_ms.pak", - "components_strings_nl.pak", - "components_strings_nb.pak", - "components_strings_pl.pak", - "components_strings_pt-BR.pak", - "components_strings_pt-PT.pak", - "components_strings_ro.pak", - "components_strings_ru.pak", - "components_strings_sk.pak", - "components_strings_sl.pak", - "components_strings_sr.pak", - "components_strings_sv.pak", - "components_strings_sw.pak", - "components_strings_ta.pak", - "components_strings_te.pak", - "components_strings_th.pak", - "components_strings_tr.pak", - "components_strings_uk.pak", - "components_strings_vi.pak", - "components_strings_zh-CN.pak", - "components_strings_zh-TW.pak", "java/res/values-am/components_strings.xml", "java/res/values-ar/components_strings.xml", "java/res/values-bg/components_strings.xml", @@ -404,6 +249,9 @@ "java/res/values-zh-rTW/components_strings.xml", "java/res/values/components_strings.xml", ] + foreach(_locale, locales) { + outputs += [ "components_strings_${_locale}.pak" ] + } } source_set("webview_entry_point") {
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc index 55a309a..fa0dc88 100644 --- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc +++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
@@ -269,6 +269,7 @@ content::ResourceContext* resource_context, bool is_content_initiated, bool must_download, + bool is_new_request, ScopedVector<content::ResourceThrottle>* throttles) { GURL url(request->url()); std::string user_agent;
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h index 7bfc6ef..4731573 100644 --- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h +++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
@@ -39,6 +39,7 @@ content::ResourceContext* resource_context, bool is_content_initiated, bool must_download, + bool is_new_request, ScopedVector<content::ResourceThrottle>* throttles) override; content::ResourceDispatcherHostLoginDelegate* CreateLoginDelegate( net::AuthChallengeInfo* auth_info,
diff --git a/ash/common/wm/system_modal_container_layout_manager.cc b/ash/common/wm/system_modal_container_layout_manager.cc index 4185abf..1a895ab 100644 --- a/ash/common/wm/system_modal_container_layout_manager.cc +++ b/ash/common/wm/system_modal_container_layout_manager.cc
@@ -52,6 +52,21 @@ //////////////////////////////////////////////////////////////////////////////// // SystemModalContainerLayoutManager, WmLayoutManager implementation: +void SystemModalContainerLayoutManager::OnChildWindowVisibilityChanged( + WmWindow* window, + bool visible) { + if (GetModalType(window) != ui::MODAL_TYPE_SYSTEM) + return; + + if (window->IsVisible()) { + DCHECK(!base::ContainsValue(modal_windows_, window)); + AddModalWindow(window); + } else { + if (RemoveModalWindow(window)) + WmShell::Get()->OnModalWindowRemoved(window); + } +} + void SystemModalContainerLayoutManager::OnWindowResized() { PositionDialogsAfterWorkAreaResize(); } @@ -110,20 +125,6 @@ } } -void SystemModalContainerLayoutManager::OnWindowVisibilityChanged( - WmWindow* window, - bool visible) { - if (GetModalType(window) != ui::MODAL_TYPE_SYSTEM) - return; - - if (window->IsVisible()) { - AddModalWindow(window); - } else { - RemoveModalWindow(window); - WmShell::Get()->OnModalWindowRemoved(window); - } -} - //////////////////////////////////////////////////////////////////////////////// // SystemModalContainerLayoutManager, Keyboard::KeybaordControllerObserver // implementation:
diff --git a/ash/common/wm/system_modal_container_layout_manager.h b/ash/common/wm/system_modal_container_layout_manager.h index 18fe75d..7bf81c8 100644 --- a/ash/common/wm/system_modal_container_layout_manager.h +++ b/ash/common/wm/system_modal_container_layout_manager.h
@@ -36,6 +36,7 @@ bool has_window_dimmer() const { return window_dimmer_ != nullptr; } // Overridden from WmSnapToPixelLayoutManager: + void OnChildWindowVisibilityChanged(WmWindow* child, bool visible) override; void OnWindowResized() override; void OnWindowAddedToLayout(WmWindow* child) override; void OnWillRemoveWindowFromLayout(WmWindow* child) override; @@ -45,7 +46,6 @@ // Overridden from WmWindowObserver: void OnWindowPropertyChanged(WmWindow* window, WmWindowProperty property) override; - void OnWindowVisibilityChanged(WmWindow* window, bool visible) override; // Overridden from keyboard::KeyboardControllerObserver: void OnKeyboardBoundsChanging(const gfx::Rect& new_bounds) override;
diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc index 9ba6dfa..dc95fbd 100644 --- a/ash/wm/system_modal_container_layout_manager_unittest.cc +++ b/ash/wm/system_modal_container_layout_manager_unittest.cc
@@ -784,6 +784,16 @@ EXPECT_TRUE(WmShell::Get()->IsSystemModalWindowOpen()); EXPECT_TRUE(layout_manager->has_window_dimmer()); + // Make sure that a child visibility change should not cause + // inconsistent state. + std::unique_ptr<aura::Window> child = base::MakeUnique<aura::Window>(nullptr); + child->SetType(ui::wm::WINDOW_TYPE_CONTROL); + child->Init(ui::LAYER_TEXTURED); + modal_window->AddChild(child.get()); + child->Show(); + EXPECT_TRUE(WmShell::Get()->IsSystemModalWindowOpen()); + EXPECT_TRUE(layout_manager->has_window_dimmer()); + modal_window->Hide(); EXPECT_FALSE(WmShell::Get()->IsSystemModalWindowOpen()); EXPECT_FALSE(layout_manager->has_window_dimmer());
diff --git a/base/files/file_path_watcher.cc b/base/files/file_path_watcher.cc index a4624ab..e370fcb1 100644 --- a/base/files/file_path_watcher.cc +++ b/base/files/file_path_watcher.cc
@@ -18,12 +18,6 @@ } // static -void FilePathWatcher::CancelWatch( - const scoped_refptr<PlatformDelegate>& delegate) { - delegate->CancelOnMessageLoopThread(); -} - -// static bool FilePathWatcher::RecursiveWatchAvailable() { #if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) || \ defined(OS_LINUX) || defined(OS_ANDROID)
diff --git a/base/files/file_path_watcher.h b/base/files/file_path_watcher.h index d5c6db1ac..7b1483a 100644 --- a/base/files/file_path_watcher.h +++ b/base/files/file_path_watcher.h
@@ -53,11 +53,6 @@ virtual ~PlatformDelegate(); - // Stop watching. This is only called on the thread of the appropriate - // message loop. Since it can also be called more than once, it should - // check |is_cancelled()| to avoid duplicate work. - virtual void CancelOnMessageLoopThread() = 0; - scoped_refptr<base::SingleThreadTaskRunner> task_runner() const { return task_runner_; }
diff --git a/base/files/file_path_watcher_fsevents.h b/base/files/file_path_watcher_fsevents.h index cfbe020b..fbcca1f8 100644 --- a/base/files/file_path_watcher_fsevents.h +++ b/base/files/file_path_watcher_fsevents.h
@@ -54,7 +54,7 @@ const FilePath& resolved_target); // Cleans up and stops the event stream. - void CancelOnMessageLoopThread() override; + void CancelOnMessageLoopThread(); // (Re-)Initialize the event stream to start reporting events from // |start_event|.
diff --git a/base/files/file_path_watcher_kqueue.h b/base/files/file_path_watcher_kqueue.h index d9db8c2..365cac53 100644 --- a/base/files/file_path_watcher_kqueue.h +++ b/base/files/file_path_watcher_kqueue.h
@@ -61,7 +61,7 @@ typedef std::vector<struct kevent> EventVector; // Can only be called on |io_task_runner_|'s thread. - void CancelOnMessageLoopThread() override; + void CancelOnMessageLoopThread(); // Returns true if the kevent values are error free. bool AreKeventValuesValid(struct kevent* kevents, int count);
diff --git a/base/files/file_path_watcher_linux.cc b/base/files/file_path_watcher_linux.cc index 9d50169..958e60c 100644 --- a/base/files/file_path_watcher_linux.cc +++ b/base/files/file_path_watcher_linux.cc
@@ -120,7 +120,6 @@ // Cancel the watch. This unregisters the instance with InotifyReader. void Cancel() override; - void CancelOnMessageLoopThread() override; void CancelOnMessageLoopThreadOrInDestructor(); // Inotify watches are installed for all directory components of |target_|. @@ -329,8 +328,8 @@ return; } - // Check to see if CancelOnMessageLoopThread() has already been called. - // May happen when code flow reaches here from the PostTask() above. + // Check to see if CancelOnMessageLoopThreadOrInDestructor() has already been + // called. May happen when code flow reaches here from the PostTask() above. if (watches_.empty()) { DCHECK(target_.empty()); return; @@ -445,19 +444,17 @@ return; } - // Switch to the message_loop() if necessary so we can access |watches_|. + // Switch to the task_runner() if necessary so we can access |watches_|. if (!task_runner()->BelongsToCurrentThread()) { - task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch, - make_scoped_refptr(this))); + task_runner()->PostTask( + FROM_HERE, + Bind(&FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor, + this)); } else { - CancelOnMessageLoopThread(); + CancelOnMessageLoopThreadOrInDestructor(); } } -void FilePathWatcherImpl::CancelOnMessageLoopThread() { - CancelOnMessageLoopThreadOrInDestructor(); -} - void FilePathWatcherImpl::CancelOnMessageLoopThreadOrInDestructor() { DCHECK(in_destructor_ || task_runner()->BelongsToCurrentThread()); @@ -479,7 +476,7 @@ } void FilePathWatcherImpl::UpdateWatches() { - // Ensure this runs on the message_loop() exclusively in order to avoid + // Ensure this runs on the task_runner() exclusively in order to avoid // concurrency issues. DCHECK(task_runner()->BelongsToCurrentThread()); DCHECK(HasValidWatchVector());
diff --git a/base/files/file_path_watcher_mac.cc b/base/files/file_path_watcher_mac.cc index 7338eaf..b65591a2 100644 --- a/base/files/file_path_watcher_mac.cc +++ b/base/files/file_path_watcher_mac.cc
@@ -40,12 +40,6 @@ set_cancelled(); } - void CancelOnMessageLoopThread() override { - if (impl_.get()) - impl_->Cancel(); - set_cancelled(); - } - protected: ~FilePathWatcherImpl() override {}
diff --git a/base/files/file_path_watcher_stub.cc b/base/files/file_path_watcher_stub.cc index 8138692e..c637e3c 100644 --- a/base/files/file_path_watcher_stub.cc +++ b/base/files/file_path_watcher_stub.cc
@@ -21,8 +21,6 @@ void Cancel() override {} - void CancelOnMessageLoopThread() override {} - protected: ~FilePathWatcherImpl() override {} };
diff --git a/base/files/file_path_watcher_win.cc b/base/files/file_path_watcher_win.cc index 9121fa9..3bbf2fb 100644 --- a/base/files/file_path_watcher_win.cc +++ b/base/files/file_path_watcher_win.cc
@@ -60,7 +60,7 @@ void DestroyWatch(); // Cleans up and stops observing the |task_runner_| thread. - void CancelOnMessageLoopThread() override; + void CancelOnMessageLoopThread(); // Callback to notify upon changes. FilePathWatcher::Callback callback_; @@ -122,8 +122,8 @@ // Switch to the file thread if necessary so we can stop |watcher_|. if (!task_runner()->BelongsToCurrentThread()) { - task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch, - make_scoped_refptr(this))); + task_runner()->PostTask( + FROM_HERE, Bind(&FilePathWatcherImpl::CancelOnMessageLoopThread, this)); } else { CancelOnMessageLoopThread(); }
diff --git a/blimp/net/BUILD.gn b/blimp/net/BUILD.gn index 9c348825..2c6ab3a9 100644 --- a/blimp/net/BUILD.gn +++ b/blimp/net/BUILD.gn
@@ -77,6 +77,7 @@ defines = [ "BLIMP_NET_IMPLEMENTATION=1" ] deps = [ + ":helium", "//base", "//blimp/common", "//net", @@ -90,6 +91,21 @@ ] } +source_set("helium") { + sources = [ + "helium/vector_clock.cc", + "helium/vector_clock.h", + ] + deps = [ + "//base", + "//blimp/common", + ] + + public_deps = [ + "//blimp/common/proto", + ] +} + source_set("test_support") { testonly = true @@ -134,6 +150,7 @@ "compressed_packet_unittest.cc", "engine_authentication_handler_unittest.cc", "engine_connection_manager_unittest.cc", + "helium/vector_clock_unittest.cc", "input_message_unittest.cc", "ssl_client_transport_unittest.cc", "stream_packet_reader_unittest.cc", @@ -143,6 +160,7 @@ ] deps = [ + ":helium", ":net", ":test_support", "//base",
diff --git a/blimp/net/helium/vector_clock.cc b/blimp/net/helium/vector_clock.cc new file mode 100644 index 0000000..6b13d28 --- /dev/null +++ b/blimp/net/helium/vector_clock.cc
@@ -0,0 +1,57 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "blimp/net/helium/vector_clock.h" + +#include <algorithm> + +#include "base/logging.h" + +namespace blimp { + +VectorClock::VectorClock() {} + +VectorClock::VectorClock(Revision local_revision, Revision remote_revision) + : local_revision_(local_revision), remote_revision_(remote_revision) {} + +VectorClock::Comparison VectorClock::CompareTo(const VectorClock& other) const { + DCHECK(local_revision_ >= other.local_revision()); + + if (local_revision_ == other.local_revision()) { + if (remote_revision_ == other.remote_revision()) { + return VectorClock::Comparison::EqualTo; + } else if (remote_revision_ < other.remote_revision()) { + return VectorClock::Comparison::LessThan; + } else { + return VectorClock::Comparison::GreaterThan; + } + } else { + if (local_revision_ > other.local_revision()) { + if (remote_revision_ == other.remote_revision()) { + return VectorClock::Comparison::GreaterThan; + } else { + return VectorClock::Comparison::Conflict; + } + } else { // We know its not equal or greater, so its smaller + if (remote_revision_ == other.remote_revision()) { + return VectorClock::Comparison::LessThan; + } else { + LOG(FATAL) << "Local revision should always be greater or equal."; + return VectorClock::Comparison::Conflict; + } + } + } +} + +VectorClock VectorClock::MergeWith(const VectorClock& other) const { + VectorClock result(std::max(local_revision_, other.local_revision()), + std::max(remote_revision_, other.remote_revision())); + return result; +} + +void VectorClock::IncrementLocal() { + local_revision_++; +} + +} // namespace blimp
diff --git a/blimp/net/helium/vector_clock.h b/blimp/net/helium/vector_clock.h new file mode 100644 index 0000000..c375aac --- /dev/null +++ b/blimp/net/helium/vector_clock.h
@@ -0,0 +1,66 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BLIMP_NET_HELIUM_VECTOR_CLOCK_H_ +#define BLIMP_NET_HELIUM_VECTOR_CLOCK_H_ + +#include <stdint.h> + +namespace blimp { + +// From wikipedia: +// A vector clock is an algorithm for generating a partial ordering of events +// in a distributed system and detecting causality violations. This is used +// in Blimp to allow client and server modify a local copy of an object and +// later be able to detect ordering or conflicts if any. +// +// For more info see: +// https://en.wikipedia.org/wiki/Vector_clock + +typedef uint32_t Revision; + +class VectorClock { + public: + enum class Comparison { LessThan, EqualTo, GreaterThan, Conflict }; + + VectorClock(Revision local_revision, Revision remote_revision); + VectorClock(); + + // Compares two vector clocks. There are 4 possibilities for the result: + // * LessThan: One revision is equal and for the other is smaller. + // (1,0).CompareTo((2, 0)); + // * EqualTo: Both revisions are the same. + // * GreaterThan: One revision is equal and for the other is greater. + // (2,0).CompareTo((1, 0)); + // * Conflict: Both revisions are different. (1,0).CompareTo(0,1) + Comparison CompareTo(const VectorClock& other) const; + + // Merges two vector clocks. This function should be used at synchronization + // points. i.e. when client receives data from the server or vice versa. + VectorClock MergeWith(const VectorClock& other) const; + + // Increments local_revision_ by one. This is used when something changes + // in the local state like setting a property or applying a change set. + void IncrementLocal(); + + Revision local_revision() const { return local_revision_; } + + void set_local_revision(Revision local_revision) { + local_revision_ = local_revision; + } + + Revision remote_revision() const { return remote_revision_; } + + void set_remote_revision(Revision remote_revision) { + remote_revision_ = remote_revision; + } + + private: + Revision local_revision_ = 0; + Revision remote_revision_ = 0; +}; + +} // namespace blimp + +#endif // BLIMP_NET_HELIUM_VECTOR_CLOCK_H_
diff --git a/blimp/net/helium/vector_clock_unittest.cc b/blimp/net/helium/vector_clock_unittest.cc new file mode 100644 index 0000000..2bd258e --- /dev/null +++ b/blimp/net/helium/vector_clock_unittest.cc
@@ -0,0 +1,140 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "blimp/net/helium/vector_clock.h" + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace blimp { +namespace { + +class VectorClockComparisonTest + : public ::testing::TestWithParam< + std::tuple<VectorClock, VectorClock, VectorClock::Comparison>> { + public: + VectorClockComparisonTest() {} + ~VectorClockComparisonTest() override {} +}; + +TEST_P(VectorClockComparisonTest, CompareTo) { + auto param = GetParam(); + VectorClock v1 = std::get<0>(param); + VectorClock v2 = std::get<1>(param); + VectorClock::Comparison expected = std::get<2>(param); + EXPECT_EQ(expected, v1.CompareTo(v2)); +} + +INSTANTIATE_TEST_CASE_P( + LessThan, + VectorClockComparisonTest, + ::testing::Values(std::make_tuple(VectorClock(1, 2), + VectorClock(1, 3), + VectorClock::Comparison::LessThan))); + +INSTANTIATE_TEST_CASE_P( + GreaterThan, + VectorClockComparisonTest, + ::testing::Values(std::make_tuple(VectorClock(1, 3), + VectorClock(1, 2), + VectorClock::Comparison::GreaterThan), + std::make_tuple(VectorClock(2, 2), + VectorClock(1, 2), + VectorClock::Comparison::GreaterThan))); + +INSTANTIATE_TEST_CASE_P( + Conflict, + VectorClockComparisonTest, + ::testing::Values(std::make_tuple(VectorClock(1, 2), + VectorClock(0, 1), + VectorClock::Comparison::Conflict), + std::make_tuple(VectorClock(1, 2), + VectorClock(0, 3), + VectorClock::Comparison::Conflict))); + +INSTANTIATE_TEST_CASE_P( + EqualTo, + VectorClockComparisonTest, + ::testing::Values(std::make_tuple(VectorClock(1, 1), + VectorClock(1, 1), + VectorClock::Comparison::EqualTo), + std::make_tuple(VectorClock(2, 3), + VectorClock(2, 3), + VectorClock::Comparison::EqualTo), + std::make_tuple(VectorClock(3, 2), + VectorClock(3, 2), + VectorClock::Comparison::EqualTo))); + +class VectorClockTest : public testing::Test { + public: + VectorClockTest() {} + ~VectorClockTest() override {} + + protected: + void CheckCumulativeMerge(const VectorClock& v1, + const VectorClock& v2, + const VectorClock& expected) { + // Compute the merge of v1 and v2 + VectorClock r1 = v1.MergeWith(v2); + EXPECT_EQ(expected.local_revision(), r1.local_revision()); + EXPECT_EQ(expected.remote_revision(), r1.remote_revision()); + + // Compute the merge of v2 and v1 + VectorClock r2 = v2.MergeWith(v1); + EXPECT_EQ(expected.local_revision(), r2.local_revision()); + EXPECT_EQ(expected.remote_revision(), r2.remote_revision()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(VectorClockTest); +}; + +TEST_F(VectorClockTest, IncrementLocal1) { + VectorClock v(0, 0); + v.IncrementLocal(); + EXPECT_EQ(1U, v.local_revision()); + EXPECT_EQ(0U, v.remote_revision()); +} + +TEST_F(VectorClockTest, IncrementLocal2) { + VectorClock v(4, 5); + v.IncrementLocal(); + EXPECT_EQ(5U, v.local_revision()); + EXPECT_EQ(5U, v.remote_revision()); +} + +TEST_F(VectorClockTest, MergeLocalEqualRemoteSmaller) { + VectorClock v1(1, 2); + VectorClock v2(1, 4); + + VectorClock expected(1, 4); + CheckCumulativeMerge(v1, v2, expected); +} + +TEST_F(VectorClockTest, MergeLocalSmallerRemoteEqual) { + VectorClock v1(1, 4); + VectorClock v2(2, 4); + + VectorClock expected(2, 4); + CheckCumulativeMerge(v1, v2, expected); +} + +TEST_F(VectorClockTest, MergeLocalSmallerRemoteSmaller) { + VectorClock v1(1, 2); + VectorClock v2(3, 4); + + VectorClock expected(3, 4); + CheckCumulativeMerge(v1, v2, expected); +} + +TEST_F(VectorClockTest, MergeLocalSmallerRemoteGreater) { + VectorClock v1(1, 4); + VectorClock v2(3, 2); + + VectorClock expected(3, 4); + CheckCumulativeMerge(v1, v2, expected); +} + +} // namespace +} // namespace blimp
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 4bbc34f4..5fcc050 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -1084,9 +1084,7 @@ defines += [ "_FORTIFY_SOURCE=2" ] } - if (is_mac) { - # Warn if automatic synthesis is triggered. This triggers numerous - # warnings for internal iOS directories. + if (is_mac || is_ios) { cflags_objc = [ "-Wobjc-missing-property-synthesis" ] cflags_objcc = [ "-Wobjc-missing-property-synthesis" ] }
diff --git a/build/config/ios/codesign.py b/build/config/ios/codesign.py index 3d66b24..5b55318 100644 --- a/build/config/ios/codesign.py +++ b/build/config/ios/codesign.py
@@ -232,7 +232,6 @@ class Action(object): - """Class implementing one action supported by the script.""" @classmethod @@ -243,7 +242,6 @@ class CodeSignBundleAction(Action): - """Class implementing the code-sign-bundle action.""" name = 'code-sign-bundle' @@ -321,8 +319,49 @@ CodeSignBundle(bundle.path, args.identity, codesign_extra_args) -class GenerateEntitlementsAction(Action): +class CodeSignFileAction(Action): + """Class implementing code signature for a single file.""" + name = 'code-sign-file' + help = 'code-sign a single file' + + @staticmethod + def _Register(parser): + parser.add_argument( + 'path', help='path to the file to codesign') + parser.add_argument( + '--identity', '-i', required=True, + help='identity to use to codesign') + parser.add_argument( + '--output', '-o', + help='if specified copy the file to that location before signing it') + parser.set_defaults(sign=True) + + @staticmethod + def _Execute(args): + if not args.identity: + args.identity = '-' + + install_path = args.path + if args.output: + + if os.path.isfile(args.output): + os.unlink(args.output) + elif os.path.isdir(args.output): + shutil.rmtree(args.output) + + if os.path.isfile(args.path): + shutil.copy(args.path, args.output) + elif os.path.isdir(args.path): + shutil.copytree(args.path, args.output) + + install_path = args.output + + CodeSignBundle(install_path, args.identity, + ['--deep', '--preserve-metadata=identifier,entitlements']) + + +class GenerateEntitlementsAction(Action): """Class implementing the generate-entitlements action.""" name = 'generate-entitlements' @@ -353,7 +392,13 @@ parser = argparse.ArgumentParser('codesign iOS bundles') subparsers = parser.add_subparsers() - for action in [ CodeSignBundleAction, GenerateEntitlementsAction ]: + actions = [ + CodeSignBundleAction, + CodeSignFileAction, + GenerateEntitlementsAction, + ] + + for action in actions: action.Register(subparsers) args = parser.parse_args()
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni index 8d1c162..93565f0 100644 --- a/build/config/ios/rules.gni +++ b/build/config/ios/rules.gni
@@ -223,7 +223,7 @@ code_signing_outputs += [ "$_bundle_root_dir/_CodeSignature/CodeResources" ] } - if (ios_code_signing_identity != "" && ios_sdk_name != "iphonesimulator") { + if (ios_code_signing_identity != "" && !use_ios_simulator) { code_signing_outputs += [ "$_bundle_root_dir/embedded.mobileprovision" ] }
diff --git a/build/config/locales.gni b/build/config/locales.gni index 5886132..2b608b7 100644 --- a/build/config/locales.gni +++ b/build/config/locales.gni
@@ -2,6 +2,22 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# Android doesn't ship all locales in order to save space (but webview does). +# http://crbug.com/369218 +if (is_android) { + android_chrome_omitted_locales = [ + "bn", + "et", + "gu", + "kn", + "ml", + "mr", + "ms", + "ta", + "te", + ] +} + # Chrome on iOS only ships with a subset of the locales supported by other # version of Chrome as the corresponding locales are not supported by the # operating system (but for simplicity, the corresponding .pak files are
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn index 9dc878e..5040189e 100644 --- a/build/config/sanitizers/BUILD.gn +++ b/build/config/sanitizers/BUILD.gn
@@ -9,6 +9,10 @@ import("//build/config/sanitizers/sanitizers.gni") import("//build/toolchain/toolchain.gni") +if (is_ios) { + import("//build/config/ios/ios_sdk.gni") +} + # Contains the dependencies needed for sanitizers to link into executables and # shared_libraries. Unconditionally depend upon this target as it is empty if # |is_asan|, |is_lsan|, |is_tsan|, |is_msan| and |use_custom_libcxx| are false. @@ -67,35 +71,65 @@ public_deps += [ "//buildtools/third_party/libc++:libcxx_proxy" ] data += [ "$root_out_dir/libc++.so" ] } - if (is_mac || is_win) { + + # ASAN is supported on iOS but the runtime library depends on the compiler + # used (Chromium version of clang versus Xcode version of clang). Only copy + # the ASAN runtime on iOS if building with Chromium clang. + if (is_win || is_mac || (is_ios && !use_xcode_clang)) { data_deps = [ ":copy_asan_runtime", ] } - if (is_mac) { + if (is_mac || (is_ios && !use_xcode_clang)) { public_deps += [ ":asan_runtime_bundle_data" ] } } } -if ((is_mac || is_win) && using_sanitizer) { - copy("copy_asan_runtime") { - if (is_mac) { - clang_rt_dso_path = "darwin/libclang_rt.asan_osx_dynamic.dylib" - } else if (is_win && target_cpu == "x86") { - clang_rt_dso_path = "windows/clang_rt.asan_dynamic-i386.dll" - } else if (is_win && target_cpu == "x64") { - clang_rt_dso_path = "windows/clang_rt.asan_dynamic-x86_64.dll" - } - sources = [ - "$clang_base_path/lib/clang/$clang_version/lib/$clang_rt_dso_path", - ] - outputs = [ - "$root_out_dir/{{source_file_part}}", - ] +if ((is_mac || is_win || (is_ios && !use_xcode_clang)) && using_sanitizer) { + if (is_mac) { + _clang_rt_dso_path = "darwin/libclang_rt.asan_osx_dynamic.dylib" + } else if (is_ios) { + _clang_rt_dso_path = "darwin/libclang_rt.asan_iossim_dynamic.dylib" + } else if (is_win && target_cpu == "x86") { + _clang_rt_dso_path = "windows/clang_rt.asan_dynamic-i386.dll" + } else if (is_win && target_cpu == "x64") { + _clang_rt_dso_path = "windows/clang_rt.asan_dynamic-x86_64.dll" } - if (is_mac) { + _clang_rt_dso_full_path = + "$clang_base_path/lib/clang/$clang_version/lib/$_clang_rt_dso_path" + + if (!is_ios) { + copy("copy_asan_runtime") { + sources = [ + _clang_rt_dso_full_path, + ] + outputs = [ + "$root_out_dir/{{source_file_part}}", + ] + } + } else { + # On iOS, the runtime library need to be code signed (adhoc signature) + # starting with Xcode 8, so use an action instead of a copy on iOS. + action("copy_asan_runtime") { + script = "//build/config/ios/codesign.py" + sources = [ + _clang_rt_dso_full_path, + ] + outputs = [ + "$root_out_dir/" + get_path_info(sources[0], "file"), + ] + args = [ + "code-sign-file", + "--identity=" + ios_code_signing_identity, + "--output=" + rebase_path(outputs[0], root_build_dir), + rebase_path(sources[0], root_build_dir), + ] + } + } + + if (is_mac || is_ios) { bundle_data("asan_runtime_bundle_data") { sources = get_target_outputs(":copy_asan_runtime") outputs = [
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 8a22ed7..4f242d1 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -67,52 +67,10 @@ } locale_pak_resources("chrome_locale_paks") { - sources = [ - "$root_out_dir/locales/am.pak", - "$root_out_dir/locales/ar.pak", - "$root_out_dir/locales/bg.pak", - "$root_out_dir/locales/ca.pak", - "$root_out_dir/locales/cs.pak", - "$root_out_dir/locales/da.pak", - "$root_out_dir/locales/de.pak", - "$root_out_dir/locales/el.pak", - "$root_out_dir/locales/en-GB.pak", - "$root_out_dir/locales/en-US.pak", - "$root_out_dir/locales/es-419.pak", - "$root_out_dir/locales/es.pak", - "$root_out_dir/locales/fa.pak", - "$root_out_dir/locales/fi.pak", - "$root_out_dir/locales/fil.pak", - "$root_out_dir/locales/fr.pak", - "$root_out_dir/locales/he.pak", - "$root_out_dir/locales/hi.pak", - "$root_out_dir/locales/hr.pak", - "$root_out_dir/locales/hu.pak", - "$root_out_dir/locales/id.pak", - "$root_out_dir/locales/it.pak", - "$root_out_dir/locales/ja.pak", - "$root_out_dir/locales/ko.pak", - "$root_out_dir/locales/lt.pak", - "$root_out_dir/locales/lv.pak", - "$root_out_dir/locales/nb.pak", - "$root_out_dir/locales/nl.pak", - "$root_out_dir/locales/pl.pak", - "$root_out_dir/locales/pt-BR.pak", - "$root_out_dir/locales/pt-PT.pak", - "$root_out_dir/locales/ro.pak", - "$root_out_dir/locales/ru.pak", - "$root_out_dir/locales/sk.pak", - "$root_out_dir/locales/sl.pak", - "$root_out_dir/locales/sr.pak", - "$root_out_dir/locales/sv.pak", - "$root_out_dir/locales/sw.pak", - "$root_out_dir/locales/th.pak", - "$root_out_dir/locales/tr.pak", - "$root_out_dir/locales/uk.pak", - "$root_out_dir/locales/vi.pak", - "$root_out_dir/locales/zh-CN.pak", - "$root_out_dir/locales/zh-TW.pak", - ] + sources = [] + foreach(_locale, locales - android_chrome_omitted_locales) { + sources += [ "$root_out_dir/locales/$_locale.pak" ] + } deps = [ "//chrome:packed_resources",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java index d93e107..ec0bfdf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java
@@ -202,6 +202,17 @@ public void remove(ItemChooserRow item) { ItemChooserRow oldItem = mKeyToItemMap.remove(item.mKey); if (oldItem == null) return; + int oldItemPosition = getPosition(oldItem); + // If the removed item is the item that is currently selected, deselect it + // and disable the confirm button. Otherwise if the removed item is before + // the currently selected item, the currently selected item's index needs + // to be adjusted by one. + if (oldItemPosition == mSelectedItem) { + mSelectedItem = ListView.INVALID_POSITION; + mConfirmButton.setEnabled(false); + } else if (oldItemPosition < mSelectedItem) { + --mSelectedItem; + } removeFromDescriptionsMap(oldItem.mDescription); super.remove(oldItem); } @@ -221,6 +232,7 @@ * selected. */ public String getSelectedItemKey() { + if (mSelectedItem == ListView.INVALID_POSITION) return ""; ItemChooserRow row = getItem(mSelectedItem); if (row == null) return ""; return row.mKey; @@ -241,7 +253,7 @@ } /** - * Sets whether the itam is enabled. Disabled items are grayed out. + * Sets whether the item is enabled. Disabled items are grayed out. * @param id The id of the item to affect. * @param enabled Whether the item should be enabled or not. */ @@ -251,6 +263,14 @@ } else { mDisabledEntries.add(id); } + + if (mSelectedItem != ListView.INVALID_POSITION) { + ItemChooserRow selectedRow = getItem(mSelectedItem); + if (id.equals(selectedRow.mKey)) { + mConfirmButton.setEnabled(enabled); + } + } + notifyDataSetChanged(); } @@ -399,6 +419,7 @@ }); mItemAdapter = new ItemAdapter(mActivity, R.layout.item_chooser_dialog_row); + mItemAdapter.setNotifyOnChange(true); mListView.setAdapter(mItemAdapter); mListView.setEmptyView(mEmptyMessage); mListView.setOnItemClickListener(mItemAdapter);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemViewHolder.java index 0352799..4be5689 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemViewHolder.java
@@ -66,8 +66,9 @@ // Asynchronously grab a thumbnail for the file if it might have one. int fileType = item.getFilterType(); + Bitmap thumbnail = null; if (fileType == DownloadFilter.FILTER_IMAGE) { - thumbnailProvider.getThumbnail(this); + thumbnail = thumbnailProvider.getThumbnail(this); } else { // TODO(dfalcantara): Get thumbnails for audio and video files when possible. } @@ -94,7 +95,7 @@ } // Initialize the DownloadItemView. - mItemView.initialize(item, iconResource); + mItemView.initialize(item, iconResource, thumbnail); } @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java index e2b1766..337a7e1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
@@ -14,6 +14,8 @@ import org.chromium.chrome.browser.widget.TintedImageView; import org.chromium.chrome.browser.widget.selection.SelectableItemView; +import javax.annotation.Nullable; + /** * The view for a downloaded item displayed in the Downloads list. */ @@ -50,13 +52,15 @@ * * @param item The item represented by this DownloadItemView. * @param iconResId The drawable resource ID to use for the icon ImageView. + * @param thumbnail The Bitmap to use for the thumbnail or null. */ - public void initialize(DownloadHistoryItemWrapper item, int iconResId) { + public void initialize(DownloadHistoryItemWrapper item, int iconResId, + @Nullable Bitmap thumbnail) { mItem = item; setItem(item); mIconResId = iconResId; - mThumbnailBitmap = null; + mThumbnailBitmap = thumbnail; updateIconView(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProvider.java index 3ef3b466..032215e5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProvider.java
@@ -21,10 +21,11 @@ void destroy(); /** - * Asynchronously returns a thumbnail via {@link ThumbnailRequest#onThumbnailRetrieved}. + * Synchronously returns a thumbnail if it is cached. Otherwise, asynchronously returns a + * thumbnail via {@link ThumbnailRequest#onThumbnailRetrieved}. * @param request Parameters that describe the thumbnail being retrieved. */ - void getThumbnail(ThumbnailRequest request); + Bitmap getThumbnail(ThumbnailRequest request); /** Removes a particular request from the pending queue. */ void cancelRetrieval(ThumbnailRequest request);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java index c32b4c02..73366c0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java
@@ -67,12 +67,16 @@ } @Override - public void getThumbnail(ThumbnailRequest request) { + public Bitmap getThumbnail(ThumbnailRequest request) { String filePath = request.getFilePath(); - if (TextUtils.isEmpty(filePath)) return; + if (TextUtils.isEmpty(filePath)) return null; + + Bitmap cachedBitmap = getBitmapCache().get(filePath); + if (cachedBitmap != null) return cachedBitmap; mRequestQueue.offer(request); processQueue(); + return null; } /** Removes a particular file from the pending queue. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java index 1a1ac8f7e..de27aef 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -48,7 +48,6 @@ import org.chromium.chrome.browser.ntp.MostVisitedItem.MostVisitedItemManager; import org.chromium.chrome.browser.ntp.NewTabPage.OnSearchBoxScrollListener; import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter; -import org.chromium.chrome.browser.ntp.cards.NewTabPageItem; import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView; import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig; @@ -513,21 +512,6 @@ } /** - * Get the number of listed items (visible or not) for the given type. - * @param newTabPageItemViewType the item type to count. - */ - public int getViewCountMatchingViewType(@NewTabPageItem.ViewType int newTabPageItemViewType) { - int viewCount = 0; - int adapterSize = mNewTabPageAdapter.getItemCount(); - for (int i = 0; i < adapterSize; i++) { - if (mNewTabPageAdapter.getItemViewType(i) == newTabPageItemViewType) { - viewCount++; - } - } - return viewCount; - } - - /** * Sets up scrolling when snippets are enabled. It adds scroll listeners and touch listeners to * the RecyclerView. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java index 3701ed4..99dd3ba 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -230,9 +230,9 @@ } @Override - public void onSuggestionInvalidated(@CategoryInt int category, String suggestionId) { + public void onSuggestionInvalidated(@CategoryInt int category, String idWithinCategory) { if (!mSections.containsKey(category)) return; - mSections.get(category).removeSuggestionById(suggestionId); + mSections.get(category).removeSuggestionById(idWithinCategory); updateGroups(); } @@ -318,14 +318,11 @@ return getGroupPositionOffset(mBottomSpacer); } - public int getSuggestionPosition(String suggestionId) { + public int getSuggestionPosition(SnippetArticle article) { List<NewTabPageItem> items = getItems(); for (int i = 0; i < items.size(); i++) { NewTabPageItem item = items.get(i); - if (item instanceof SnippetArticle - && ((SnippetArticle) item).mId.equals(suggestionId)) { - return i; - } + if (article.equals(item)) return i; } return RecyclerView.NO_POSITION; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java index 351e0d4..dcd072f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
@@ -429,7 +429,7 @@ */ public void dismissItemWithAnimation(SnippetArticle suggestion) { // We need to recompute the position, as it might have changed. - final int position = getNewTabPageAdapter().getSuggestionPosition(suggestion.mId); + final int position = getNewTabPageAdapter().getSuggestionPosition(suggestion); if (position == RecyclerView.NO_POSITION) { // The item does not exist anymore, so ignore. return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java index 6348250..2b29f57 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -67,9 +67,9 @@ if (mMoreButton != null) mMoreButton.setDismissable(!hasSuggestions()); } - public void removeSuggestionById(String suggestionId) { + public void removeSuggestionById(String idWithinCategory) { for (SnippetArticle suggestion : mSuggestions) { - if (suggestion.mId.equals(suggestionId)) { + if (suggestion.mIdWithinCategory.equals(idWithinCategory)) { removeSuggestion(suggestion); return; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/FakeSuggestionsSource.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/FakeSuggestionsSource.java index bec2652..325e618c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/FakeSuggestionsSource.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/FakeSuggestionsSource.java
@@ -70,14 +70,14 @@ * Removes the given suggestion from the source and notifies any observer that it has been * invalidated. */ - public void fireSuggestionInvalidated(@CategoryInt int category, String suggestionId) { + public void fireSuggestionInvalidated(@CategoryInt int category, String idWithinCategory) { for (SnippetArticle suggestion : mSuggestions.get(category)) { - if (suggestion.mId.equals(suggestionId)) { + if (suggestion.mIdWithinCategory.equals(idWithinCategory)) { mSuggestions.get(category).remove(suggestion); break; } } - mObserver.onSuggestionInvalidated(category, suggestionId); + mObserver.onSuggestionInvalidated(category, idWithinCategory); } /** @@ -102,8 +102,8 @@ @Override public void fetchSuggestionImage(SnippetArticle suggestion, Callback<Bitmap> callback) { - if (mThumbnails.containsKey(suggestion.mId)) { - callback.onResult(mThumbnails.get(suggestion.mId)); + if (mThumbnails.containsKey(suggestion.mIdWithinCategory)) { + callback.onResult(mThumbnails.get(suggestion.mIdWithinCategory)); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java index 7fb0372..2387d61 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java
@@ -18,8 +18,8 @@ /** The category of this article. */ public final int mCategory; - /** The unique identifier for this article. */ - public final String mId; + /** The identifier for this article within the category - not necessarily unique globally. */ + public final String mIdWithinCategory; /** The title of this article. */ public final String mTitle; @@ -64,11 +64,11 @@ /** * Creates a SnippetArticleListItem object that will hold the data. */ - public SnippetArticle(int category, String id, String title, String publisher, + public SnippetArticle(int category, String idWithinCategory, String title, String publisher, String previewText, String url, String ampUrl, long timestamp, float score, int position, @ContentSuggestionsCardLayoutEnum int cardLayout) { mCategory = category; - mId = id; + mIdWithinCategory = idWithinCategory; mTitle = title; mPublisher = publisher; mPreviewText = previewText; @@ -83,12 +83,13 @@ @Override public boolean equals(Object other) { if (!(other instanceof SnippetArticle)) return false; - return mId.equals(((SnippetArticle) other).mId); + SnippetArticle rhs = (SnippetArticle) other; + return mCategory == rhs.mCategory && mIdWithinCategory.equals(rhs.mIdWithinCategory); } @Override public int hashCode() { - return mId.hashCode(); + return mCategory ^ mIdWithinCategory.hashCode(); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java index e3850d1..e3cdbd3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
@@ -107,13 +107,15 @@ @Override public void fetchSuggestionImage(SnippetArticle suggestion, Callback<Bitmap> callback) { assert mNativeSnippetsBridge != 0; - nativeFetchSuggestionImage(mNativeSnippetsBridge, suggestion.mId, callback); + nativeFetchSuggestionImage(mNativeSnippetsBridge, suggestion.mCategory, + suggestion.mIdWithinCategory, callback); } @Override public void dismissSuggestion(SnippetArticle suggestion) { assert mNativeSnippetsBridge != 0; - nativeDismissSuggestion(mNativeSnippetsBridge, suggestion.mId); + nativeDismissSuggestion( + mNativeSnippetsBridge, suggestion.mCategory, suggestion.mIdWithinCategory); } @Override @@ -219,8 +221,8 @@ } @CalledByNative - private void onSuggestionInvalidated(@CategoryInt int category, String suggestionId) { - if (mObserver != null) mObserver.onSuggestionInvalidated(category, suggestionId); + private void onSuggestionInvalidated(@CategoryInt int category, String idWithinCategory) { + if (mObserver != null) mObserver.onSuggestionInvalidated(category, idWithinCategory); } private native long nativeInit(Profile profile); @@ -233,9 +235,10 @@ long nativeNTPSnippetsBridge, int category); private native List<SnippetArticle> nativeGetSuggestionsForCategory( long nativeNTPSnippetsBridge, int category); - private native void nativeFetchSuggestionImage( - long nativeNTPSnippetsBridge, String suggestionId, Callback<Bitmap> callback); - private native void nativeDismissSuggestion(long nativeNTPSnippetsBridge, String suggestionId); + private native void nativeFetchSuggestionImage(long nativeNTPSnippetsBridge, int category, + String idWithinCategory, Callback<Bitmap> callback); + private native void nativeDismissSuggestion( + long nativeNTPSnippetsBridge, int category, String idWithinCategory); private native void nativeDismissCategory(long nativeNTPSnippetsBridge, int category); private native void nativeGetURLVisited( long nativeNTPSnippetsBridge, Callback<Boolean> callback, String url);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SuggestionsSource.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SuggestionsSource.java index 9bd37a8d..1f631587a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SuggestionsSource.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SuggestionsSource.java
@@ -31,7 +31,7 @@ * immediately. This event may be fired for a category or suggestion that does not * currently exist or has never existed and should be ignored in that case. */ - void onSuggestionInvalidated(@CategoryInt int category, String suggestionId); + void onSuggestionInvalidated(@CategoryInt int category, String idWithinCategory); } /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java index cdbc4f35..3f1abe26 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java
@@ -203,6 +203,117 @@ } @LargeTest + public void testPairButtonDisabledOrEnabledAfterSelectedItemDisabledOrEnabled() + throws InterruptedException { + Dialog dialog = mChooserDialog.getDialogForTesting(); + assertTrue(dialog.isShowing()); + + final Button button = (Button) dialog.findViewById(R.id.positive); + + mChooserDialog.addOrUpdateItem(new ItemChooserDialog.ItemChooserRow("key1", "desc1")); + mChooserDialog.addOrUpdateItem(new ItemChooserDialog.ItemChooserRow("key2", "desc2")); + + selectItem(dialog, 1, "key1", true); + assertTrue(button.isEnabled()); + + mChooserDialog.setEnabled("key1", false); + assertFalse(button.isEnabled()); + + mChooserDialog.setEnabled("key1", true); + assertTrue(button.isEnabled()); + + mChooserDialog.dismiss(); + } + + @LargeTest + public void testPairButtonDisabledAfterSelectedItemRemoved() throws InterruptedException { + Dialog dialog = mChooserDialog.getDialogForTesting(); + assertTrue(dialog.isShowing()); + + final Button button = (Button) dialog.findViewById(R.id.positive); + + ItemChooserDialog.ItemChooserRow item1 = + new ItemChooserDialog.ItemChooserRow("key1", "desc1"); + ItemChooserDialog.ItemChooserRow item2 = + new ItemChooserDialog.ItemChooserRow("key2", "desc2"); + mChooserDialog.addOrUpdateItem(item1); + mChooserDialog.addOrUpdateItem(item2); + + selectItem(dialog, 1, "key1", true); + assertTrue(button.isEnabled()); + + mChooserDialog.removeItemFromList(item1); + assertFalse(button.isEnabled()); + + mChooserDialog.dismiss(); + } + + @LargeTest + public void testSelectAnItemAndRemoveAnotherItem() throws InterruptedException { + Dialog dialog = mChooserDialog.getDialogForTesting(); + assertTrue(dialog.isShowing()); + + final Button button = (Button) dialog.findViewById(R.id.positive); + ItemChooserDialog.ItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting(); + + ItemChooserDialog.ItemChooserRow item1 = + new ItemChooserDialog.ItemChooserRow("key1", "desc1"); + ItemChooserDialog.ItemChooserRow item2 = + new ItemChooserDialog.ItemChooserRow("key2", "desc2"); + ItemChooserDialog.ItemChooserRow item3 = + new ItemChooserDialog.ItemChooserRow("key3", "desc3"); + + mChooserDialog.addOrUpdateItem(item1); + mChooserDialog.addOrUpdateItem(item2); + mChooserDialog.addOrUpdateItem(item3); + + selectItem(dialog, 2, "key2", true); + assertTrue(button.isEnabled()); + + // Remove the item before the currently selected item. + mChooserDialog.removeItemFromList(item1); + assertTrue(button.isEnabled()); + assertEquals("key2", itemAdapter.getSelectedItemKey()); + + // Remove the item after the currently selected item. + mChooserDialog.removeItemFromList(item3); + assertTrue(button.isEnabled()); + assertEquals("key2", itemAdapter.getSelectedItemKey()); + + mChooserDialog.dismiss(); + } + + @LargeTest + public void testSelectAnItemAndRemoveTheSelectedItem() throws InterruptedException { + Dialog dialog = mChooserDialog.getDialogForTesting(); + assertTrue(dialog.isShowing()); + + final Button button = (Button) dialog.findViewById(R.id.positive); + ItemChooserDialog.ItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting(); + + ItemChooserDialog.ItemChooserRow item1 = + new ItemChooserDialog.ItemChooserRow("key1", "desc1"); + ItemChooserDialog.ItemChooserRow item2 = + new ItemChooserDialog.ItemChooserRow("key2", "desc2"); + ItemChooserDialog.ItemChooserRow item3 = + new ItemChooserDialog.ItemChooserRow("key3", "desc3"); + + mChooserDialog.addOrUpdateItem(item1); + mChooserDialog.addOrUpdateItem(item2); + mChooserDialog.addOrUpdateItem(item3); + + selectItem(dialog, 2, "key2", true); + assertTrue(button.isEnabled()); + + // Remove the selected item. + mChooserDialog.removeItemFromList(item2); + assertFalse(button.isEnabled()); + assertEquals("", itemAdapter.getSelectedItemKey()); + + mChooserDialog.dismiss(); + } + + @LargeTest public void testAddOrUpdateItemAndRemoveItemFromList() throws InterruptedException { Dialog dialog = mChooserDialog.getDialogForTesting(); assertTrue(dialog.isShowing());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java index 81c0f67..66b52405 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java
@@ -8,6 +8,7 @@ import static junit.framework.Assert.assertNull; import android.content.ComponentName; +import android.graphics.Bitmap; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; @@ -145,8 +146,12 @@ public static class StubbedThumbnailProvider implements ThumbnailProvider { @Override public void destroy() {} + @Override - public void getThumbnail(ThumbnailRequest request) {} + public Bitmap getThumbnail(ThumbnailRequest request) { + return null; + } + @Override public void cancelRetrieval(ThumbnailRequest request) {} }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java index 956e6f0..67911e4 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -580,7 +580,7 @@ assertArticlesEqual(articles, 2, 5); SnippetArticle removed = articles.remove(1); - mSource.fireSuggestionInvalidated(KnownCategories.ARTICLES, removed.mId); + mSource.fireSuggestionInvalidated(KnownCategories.ARTICLES, removed.mIdWithinCategory); assertArticlesEqual(articles, 2, 4); }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index 299b886..e8e2180 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -4660,6 +4660,9 @@ <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_CHROMEVOX_SPOKEN_FEEDBACK" desc="The text in the keyboard overlay to explain the shortcut (enable or disable spoken feedback)."> ChromeVox (spoken feedback) </message> + <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_HIGH_CONTRAST_MODE" desc="The text in the keyboard overlay to explain the shortcut (Toggle high contrast mode)."> + Toggle High Contrast Mode + </message> <message name="IDS_KEYBOARD_OVERLAY_TOGGLE_PROJECTION_TOUCH_HUD" desc="The text in the keyboard overlay to explain the shortcut."> Projection touch HUD </message>
diff --git a/chrome/browser/android/download/download_controller.cc b/chrome/browser/android/download/download_controller.cc index 337b8ab62..024bef0 100644 --- a/chrome/browser/android/download/download_controller.cc +++ b/chrome/browser/android/download/download_controller.cc
@@ -217,11 +217,6 @@ RequestFileAccess(callback_id); } -void DownloadController::SetDefaultDownloadFileName( - const std::string& file_name) { - default_file_name_ = file_name; -} - bool DownloadController::HasFileAccessPermission( ui::WindowAndroid* window_android) { ScopedJavaLocalRef<jobject> jwindow_android = window_android->GetJavaObject();
diff --git a/chrome/browser/android/download/download_controller.h b/chrome/browser/android/download/download_controller.h index 163e815..737e380 100644 --- a/chrome/browser/android/download/download_controller.h +++ b/chrome/browser/android/download/download_controller.h
@@ -48,7 +48,6 @@ void AcquireFileAccessPermission( content::WebContents* web_contents, const AcquireFileAccessPermissionCallback& callback) override; - void SetDefaultDownloadFileName(const std::string& file_name) override; // UMA histogram enum for download cancellation reasons. Keep this // in sync with MobileDownloadCancelReason in histograms.xml. This should be
diff --git a/chrome/browser/android/download/download_controller_base.h b/chrome/browser/android/download/download_controller_base.h index c317ed4..ec85098 100644 --- a/chrome/browser/android/download/download_controller_base.h +++ b/chrome/browser/android/download/download_controller_base.h
@@ -88,10 +88,6 @@ // Called by unit test to approve or disapprove file access request. virtual void SetApproveFileAccessRequestForTesting(bool approve) {} - // Called to set the default download file name if it cannot be resolved - // from url and content disposition - virtual void SetDefaultDownloadFileName(const std::string& file_name) {} - protected: ~DownloadControllerBase() override {} static DownloadControllerBase* download_controller_;
diff --git a/chrome/browser/android/download/download_manager_service.cc b/chrome/browser/android/download/download_manager_service.cc index 5630e66..9b4fa66b 100644 --- a/chrome/browser/android/download/download_manager_service.cc +++ b/chrome/browser/android/download/download_manager_service.cc
@@ -111,8 +111,6 @@ JNIEnv* env, jobject obj) { java_ref_.Reset(env, obj); - DownloadControllerBase::Get()->SetDefaultDownloadFileName( - l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME)); } void DownloadManagerService::ResumeDownload(
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.cc b/chrome/browser/android/ntp/ntp_snippets_bridge.cc index 6b0c9fe..1e4f9805 100644 --- a/chrome/browser/android/ntp/ntp_snippets_bridge.cc +++ b/chrome/browser/android/ntp/ntp_snippets_bridge.cc
@@ -201,7 +201,8 @@ Java_SnippetsBridge_createSuggestionList(env); for (const ContentSuggestion& suggestion : suggestions) { Java_SnippetsBridge_addSuggestion( - env, result, category, ConvertUTF8ToJavaString(env, suggestion.id()), + env, result, category, + ConvertUTF8ToJavaString(env, suggestion.id().id_within_category()), ConvertUTF16ToJavaString(env, suggestion.title()), ConvertUTF16ToJavaString(env, suggestion.publisher_name()), ConvertUTF16ToJavaString(env, suggestion.snippet_text()), @@ -216,11 +217,13 @@ void NTPSnippetsBridge::FetchSuggestionImage( JNIEnv* env, const JavaParamRef<jobject>& obj, - const JavaParamRef<jstring>& suggestion_id, + jint category, + const JavaParamRef<jstring>& id_within_category, const JavaParamRef<jobject>& j_callback) { base::android::ScopedJavaGlobalRef<jobject> callback(j_callback); content_suggestions_service_->FetchSuggestionImage( - ConvertJavaStringToUTF8(env, suggestion_id), + ContentSuggestion::ID(CategoryFromIDValue(category), + ConvertJavaStringToUTF8(env, id_within_category)), base::Bind(&NTPSnippetsBridge::OnImageFetched, weak_ptr_factory_.GetWeakPtr(), callback)); } @@ -228,9 +231,11 @@ void NTPSnippetsBridge::DismissSuggestion( JNIEnv* env, const JavaParamRef<jobject>& obj, - const JavaParamRef<jstring>& suggestion_id) { + jint category, + const JavaParamRef<jstring>& id_within_category) { content_suggestions_service_->DismissSuggestion( - ConvertJavaStringToUTF8(env, suggestion_id)); + ContentSuggestion::ID(CategoryFromIDValue(category), + ConvertJavaStringToUTF8(env, id_within_category))); } void NTPSnippetsBridge::DismissCategory(JNIEnv* env, @@ -357,15 +362,14 @@ } void NTPSnippetsBridge::OnSuggestionInvalidated( - Category category, - const std::string& suggestion_id) { + const ContentSuggestion::ID& suggestion_id) { if (observer_.is_null()) return; JNIEnv* env = base::android::AttachCurrentThread(); Java_SnippetsBridge_onSuggestionInvalidated( - env, observer_.obj(), static_cast<int>(category.id()), - ConvertUTF8ToJavaString(env, suggestion_id).obj()); + env, observer_.obj(), static_cast<int>(suggestion_id.category().id()), + ConvertUTF8ToJavaString(env, suggestion_id.id_within_category()).obj()); } void NTPSnippetsBridge::ContentSuggestionsServiceShutdown() {
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.h b/chrome/browser/android/ntp/ntp_snippets_bridge.h index e5332b8..3f94cb4 100644 --- a/chrome/browser/android/ntp/ntp_snippets_bridge.h +++ b/chrome/browser/android/ntp/ntp_snippets_bridge.h
@@ -54,13 +54,15 @@ void FetchSuggestionImage( JNIEnv* env, const base::android::JavaParamRef<jobject>& obj, - const base::android::JavaParamRef<jstring>& suggestion_id, + jint category, + const base::android::JavaParamRef<jstring>& id_within_category, const base::android::JavaParamRef<jobject>& j_callback); void DismissSuggestion( JNIEnv* env, const base::android::JavaParamRef<jobject>& obj, - const base::android::JavaParamRef<jstring>& suggestion_id); + jint category, + const base::android::JavaParamRef<jstring>& id_within_category); void DismissCategory(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj, @@ -124,8 +126,8 @@ void OnCategoryStatusChanged( ntp_snippets::Category category, ntp_snippets::CategoryStatus new_status) override; - void OnSuggestionInvalidated(ntp_snippets::Category category, - const std::string& suggestion_id) override; + void OnSuggestionInvalidated( + const ntp_snippets::ContentSuggestion::ID& suggestion_id) override; void ContentSuggestionsServiceShutdown() override; void OnImageFetched(base::android::ScopedJavaGlobalRef<jobject> callback,
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc index 5f447423..3685359 100644 --- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc +++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
@@ -114,8 +114,6 @@ WebApplicationInfo web_app_info = received_web_app_info; web_app_info.title = web_app_info.title.substr(0, chrome::kMaxMetaTagAttributeLength); - web_app_info.description = - web_app_info.description.substr(0, chrome::kMaxMetaTagAttributeLength); // Simply set the user-editable title to be the page's title shortcut_info_.user_title = web_app_info.title.empty() @@ -211,6 +209,11 @@ (data.error_code == NO_ERROR_DETECTED && AreWebManifestUrlsWebApkCompatible(data.manifest)); weak_observer_->OnDidDetermineWebApkCompatibility(webapk_compatible); + + // WebAPKs are wholly defined by the Web Manifest. Ignore the <meta> tag + // data received in OnDidGetWebApplicationInfo(). + if (webapk_compatible) + shortcut_info_ = ShortcutInfo(GURL()); } if (!data.manifest.IsEmpty()) {
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc index 4bde273..b9180b2 100644 --- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc +++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc
@@ -14,6 +14,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/nullable_string16.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "chrome/common/web_application_info.h" @@ -96,7 +97,11 @@ // called. class ObserverWaiter : public AddToHomescreenDataFetcher::Observer { public: - ObserverWaiter() {} + ObserverWaiter() + : is_webapk_compatible_(false), + determined_webapk_compatibility_(false), + title_available_(false), + data_available_(false) {} ~ObserverWaiter() override {} // Waits till the OnDataAvailable() callback is called. @@ -275,6 +280,59 @@ DISALLOW_COPY_AND_ASSIGN(AddToHomescreenDataFetcherTestCommon); }; +// Test that when the manifest provides Manifest::short_name but not +// Manifest::name that Manifest::short_name is used as the name instead of +// WebApplicationInfo::title. +TEST_P(AddToHomescreenDataFetcherTestCommon, + ManifestShortNameClobbersWebApplicationName) { + WebApplicationInfo web_application_info; + web_application_info.title = base::UTF8ToUTF16("Meta Title"); + + content::Manifest manifest(BuildDefaultManifest()); + manifest.name = base::NullableString16(); + + RegisterServiceWorker(GURL(kDefaultStartUrl)); + SetManifest(GURL(kDefaultManifestUrl), manifest, 0); + + ObserverWaiter waiter; + scoped_refptr<AddToHomescreenDataFetcher> fetcher(BuildFetcher(&waiter)); + fetcher->OnDidGetWebApplicationInfo(web_application_info); + waiter.WaitForDataAvailable(); + + EXPECT_TRUE(base::EqualsASCII(fetcher->shortcut_info().name, + kDefaultManifestShortName)); + + fetcher->set_weak_observer(nullptr); +} + +// Test that when the manifest does not provide either Manifest::short_name nor +// Manifest::name that: +// - The page is not WebAPK compatible. +// - WebApplicationInfo::title is used as the "name". +TEST_P(AddToHomescreenDataFetcherTestCommon, ManifestNoNameNoShortName) { + const char* kWebApplicationInfoTitle = "Meta Title"; + WebApplicationInfo web_application_info; + web_application_info.title = base::UTF8ToUTF16(kWebApplicationInfoTitle); + + content::Manifest manifest(BuildDefaultManifest()); + manifest.name = base::NullableString16(); + manifest.short_name = base::NullableString16(); + + RegisterServiceWorker(GURL(kDefaultStartUrl)); + SetManifest(GURL(kDefaultManifestUrl), manifest, 0); + + ObserverWaiter waiter; + scoped_refptr<AddToHomescreenDataFetcher> fetcher(BuildFetcher(&waiter)); + fetcher->OnDidGetWebApplicationInfo(web_application_info); + waiter.WaitForDataAvailable(); + + EXPECT_FALSE(waiter.is_webapk_compatible()); + EXPECT_TRUE(base::EqualsASCII(fetcher->shortcut_info().name, + kWebApplicationInfoTitle)); + + fetcher->set_weak_observer(nullptr); +} + // Checks that the AddToHomescreenDataFetcher::Observer callbacks are called // when a service worker is registered and the manifest fetch times out. TEST_P(AddToHomescreenDataFetcherTestCommon, ManifestFetchTimesOut) {
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc index b43173d..f930fe5 100644 --- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc +++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -370,45 +370,31 @@ // [1] https://developer.chrome.com/apps/declare_permissions // [2] https://developer.chrome.com/apps/api_other const char* const kSafePermissionStrings[] = { - // Risky: Reading accessibility settings could allow to infer health - // information. - // "accessibilityFeatures.read", - // Modifying accessibility settings seems safe (at most a user could be // confused by it). "accessibilityFeatures.modify", + // Originally blocked due to concerns about leaking user health information, + // but it seems this does more harm than good as it would likely prevent the + // extension from enabling assistive features. If the concerns prevail, we + // should probably not block, but adjust the API to pretend accessibility is + // off, so we don't punish apps that try to be helpful. + "accessibilityFeatures.read", + + // Allows access to web contents in response to user gesture. Note that this + // doesn't trigger a permission warning on install though, so blocking is + // somewhat at odds with the spirit of the API - however I presume the API + // design assumes user-installed extensions, which we don't have here. + // "activeTab", + // Schedule code to run at future times. "alarms", - // Risk of listening attack. - // "audio", + // Allow, but keep PS UX on top regardless. + // "app.window.alwaysOnTop", - // Risk of listening attack. - // "audioCapture", - - // Just resource management, probably doesn't even apply to Chrome OS. - "background", - - // Open a new tab with a given URL. - "browser", - - // Risky: Reading from clipboard could expose private information. - // "clipboardRead", - - // Writing to clipboard is safe. - "clipboardWrite", - - // Potentially risky: Could be used to spoof system UI. - // "contextMenus", - - // Placing a document on the scanner implies user consent. - "documentScan", - - // Possibly risky due to its experimental nature: not vetted for security, - // potentially buggy, subject to change without notice. - // "experimental", - + // TODO(isandrk): The following permissions need to be adjusted + // (crbug/651071). // Fullscreen is a no-op for Public Session. Whitelisting nevertheless to // broaden the range of supported apps. (The recommended permission names // are "app.window.*" but their unprefixed counterparts are still @@ -418,88 +404,284 @@ "fullscreen", "overrideEscFullscreen", - // TBD - // "fileSystemProvider", + "app.window.shape", + + // The embedded app is subject to the restrictions as well obviously. + "appview", + + // Risk of listening attack. + // "audio", + + // Need to surface notification to the user. Check what existing UI we have + // and whether that's sufficient for PS. + // "audioCapture", + + // Just resource management, probably doesn't even apply to Chrome OS. + "background", + + // Access to URLs only, no content. + "bookmarks", + + // Open a new tab with a given URL. + "browser", + + // This allows to read the current browsing data removal dialog settings, + // but I don't see why this would be problematic. + "browsingData", + + "certificateProvider", + + // This is risky, but blocking extensions just because they declare + // clipboardRead is unfortunate. Options: (1) Make clipboardRead return + // empty string (2) confirmation dialog. + // "clipboardRead", + + // Writing to clipboard is safe. + "clipboardWrite", + + "contentSettings", + + // Provides access to URLs. + "contextMenus", + + // This would provie access to auth cookies, so needs to be blocked. + // "cookies", + + // Provides access to the DOM, so block. + // "debugger", + + // This is mostly fine, but has a RequestContentScript action that'd allow + // access to page content, which we can't allow. + // "declarativeContent", + + // Allow, but either (1) ask user for confirmation or (2) return blank + // capture. + // "desktopCapture", + + // Haven't checked in detail what this does, but messing with devtools + // usually comes with the ability to access page content. + // "devtools", + + // I think it's fine to allow this as it should be obvious to users that + // scanning a document on the scanner will make it available to the + // organization (placing a document in the scanner implies user consent). + "documentScan", + + // Doesn't allow access to file contents AFAICT, so should be fine. + "downloads", + + // Triggers a file open for the download. + "downloads.open", + + // Controls shelf visibility. + "downloads.shelf", + + "enterprise.deviceAttributes", + + "enterprise.platformKeys", + + // Possibly risky due to its experimental nature: not vetted for security, + // potentially buggy, subject to change without notice (shouldn't + // blanket-allow experimental stuff). + // "experimental", + + "fileBrowserHandler", + + // Allow: (1) session state is ephemeral anyways, so no leaks across users. + // (2) a user that stores data on an org-owned machine won't be surprised if + // the org can see it. + "fileSystem", + + "fileSystem.directory", + + "fileSystem.requestFileSystem", + + "fileSystem.retainEntries", + + "fileSystem.write", + + "fileSystemProvider", + + "fontSettings", // Just another type of connectivity. On the system side, no user data is // involved, implicitly or explicity. "gcm", - // Risky: Accessing location without explicit user consent. - // "geolocation", + // It's fair game for a kiosk device owner to locate their device. Could + // just as well do this via IP-geolocation mechanism, so little difference. + "geolocation", - // Risky: Potentially allows keylogging. - // "hid", + // Somewhat risky as this opens up the ability to intercept user input. + // However, keyboards and mice are apparently not surfaced via this API. + "hid", + + // Just URLs and meta data. + "history", + + // Not really useful as there's no signed-in user, so OK to allow. + "identity", + + "identity.email", // Detection of idle state. "idle", - // Dev channel only. Not evaluated. - // "location", + // IME extensions see keystrokes. This might be useful though, might rely on + // manual whitelisting (assuming the number of useful IME extensions is + // relatively limited). + // "input", + + // Fair game - admin can manipulate extensions via policy anyways. + "management", // Just another type of connectivity. "mdns", - // Risky: The "allAutoDectected" option could allow access to user data - // without their consent. - // "mediaGalleries", + // Storage is ephemeral, so user needs to get their content onto the Kiosk + // device (download or plug in media), both of which seem sufficient consent + // actions. + "mediaGalleries", - // Potentially risky: Could be used to spoof system UI. - // "notifications", + "mediaGalleries.allAutoDetected", - // TBD. Could allow UX spoofing. - // "pointerLock", + "mediaGalleries.copyTo", + + "mediaGalleries.delete", + + "mediaGalleries.read", + + // Probably doesn't work on Chrome OS anyways. + "nativeMessaging", + + // Admin controls network connectivity anyways. + "networking.config", + + // Status quo considers this risky due to the ability to fake system UI - + // low risk IMHO however since notifications are already badged with app + // icon and won't extract any data. + "notifications", + + // Captures page content, so block. Alternatively: Allow, but either (1) + // prompt user or (2) return blank content. + // "pageCapture", + + // Allows to use machine crypto keys - these would be provisioned by the + // admin anyways. + "platformKeys", + + // No plugins on Chrome OS anyways. + "plugin", + + // Status quo notes concern about UX spoofing - not an issue IMHO. + "pointerLock", // Potentiall risky: chrome.power.requestKeepAwake can inhibit idle time // detection and prevent idle time logout and that way reduce isolation // between subsequent Public Session users. + // OK to allow as long as it doesn't affect PS idle time detection. // "power", - // Risky: Could be used to siphon printed documents. - // "printerProvider", + // Printing initiated by user anyways, which provides consent gesture. + "printerProvider", + + // The settings exposed via the API are under admin policy control anyways. + "privacy", + + // Admin controls network anyways. + "proxy", + + "runtime", + + // Looking at the code, this feature is declared but used nowhere. + // "screensaver", // Access serial port. It's hard to conceive a case in which private data // is stored on a serial device and being read without the user's consent. + // Minor risk of intercepting input events from serial input devices - given + // that serial input devices are exceedingly rare, OK to allow. "serial", + // Access to URLs. + "sessions", + + "socket", + // Per-app sandbox. User cannot log into Public Session, thus storage // cannot be sync'ed to the cloud. "storage", - // Access system parameters. + // Not very useful since no signed-in user. + "syncFileSystem", + + // Returns CPU parameters. "system.cpu", - // Access system parameters. + // Display parameters query/manipulation. "system.display", - // Access system parameters. + // Memory parameters access. "system.memory", - // Access system parameters. + // Enumerates network interfaces. "system.network", - // Risky: Could leak the name of a user-supplied storage medium. - // "system.storage", + // Enumerates removable storage. + "system.storage", - // Just UX. + // Provides access to screen contents, so block. Alternatively, (1) prompt + // for user consent or (2) return blank capture. + // "tabCapture", + + // URLs and page titles. + "tabs", + + // URLs and page titles. + "topSites", + + // Allows to generate TTS, but no content access. Just UX. "tts", + // Might need this, but has content access. Manual whitelisting? + // "ttsEngine", + // Excessive resource usage is not a risk. "unlimitedStorage", - // Risky: Raw peripheral access could allow an app to read user data from - // USB storage devices that have been plugged in by the user. Not sure if - // that can happen though, because the system might claim storage devices - // for itself. Still, leaving disallowed for now to be on the safe side. - // "usb", + // Plugging the USB device is sufficient as consent gesture. + "usb", - // TBD: What if one user connects and the next one is unaware of that? - // "vpnProvider", + // Belongs to the USB API. + "usbDevices", + + // Need to surface notification to the user. Check what existing UI we have + // and whether that's sufficient for PS. + // "videoCapture", + + // Admin controls network config anyways. + "vpnProvider", // Just UX. "wallpaper", - // Web capabilities are safe. + // Access to URLs. + "webNavigation", + + // Provides access to cookies and form upload data. Options: (1) block, + // (2) strip all content in events. + // "webRequest", + + // Fine once webRequest is adjusted. + // "webRequestBlocking", + + // This allows content scripts and capturing. However, the webview runs + // within a separate storage partition, i.e. doesn't share cookies and other + // storage with the browsing session. Furthermore, the embedding app could + // just as well proxy 3rd-party origin content through its own web origin + // server-side or via chrome.socket. Finally, web security doesn't make a + // lot of sense when there's no URL bar or HTTPS padlock providing trusted + // UI. Bottom line: Risks are mitigated, further restrictions don't make + // sense, so OK to allow. "webview", }; @@ -717,6 +899,7 @@ return true; } + // TODO(isandrk): Remove when whitelisting work is done (crbug/651027). // Allow extension if its type is whitelisted for use in public sessions. if (extension->GetType() == extensions::Manifest::TYPE_HOSTED_APP) { return true;
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc index da28470..97cb50e 100644 --- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc +++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -501,6 +501,7 @@ content::ResourceContext* resource_context, bool is_content_initiated, bool must_download, + bool is_new_request, ScopedVector<content::ResourceThrottle>* throttles) { const content::ResourceRequestInfo* info = content::ResourceRequestInfo::ForRequest(request); @@ -516,9 +517,9 @@ request->url(), request->method())); } - // If this isn't a new request, we've seen this before and added the standard - // resource throttles already so no need to add it again. - if (!request->is_pending()) { + // If this isn't a new request, the standard resource throttles have already + // been added, so no need to add them again. + if (is_new_request) { AppendStandardResourceThrottles(request, resource_context, content::RESOURCE_TYPE_MAIN_FRAME,
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h index 001be0ea..b730ddbb 100644 --- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h +++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
@@ -58,6 +58,7 @@ content::ResourceContext* resource_context, bool is_content_initiated, bool must_download, + bool is_new_request, ScopedVector<content::ResourceThrottle>* throttles) override; content::ResourceDispatcherHostLoginDelegate* CreateLoginDelegate( net::AuthChallengeInfo* auth_info, @@ -103,6 +104,14 @@ static void SetExternalProtocolHandlerDelegateForTesting( ExternalProtocolHandler::Delegate* delegate); + protected: + // Virtual for testing. + virtual void AppendStandardResourceThrottles( + net::URLRequest* request, + content::ResourceContext* resource_context, + content::ResourceType resource_type, + ScopedVector<content::ResourceThrottle>* throttles); + private: #if defined(ENABLE_EXTENSIONS) struct StreamTargetInfo { @@ -111,12 +120,6 @@ }; #endif - void AppendStandardResourceThrottles( - net::URLRequest* request, - content::ResourceContext* resource_context, - content::ResourceType resource_type, - ScopedVector<content::ResourceThrottle>* throttles); - scoped_refptr<DownloadRequestLimiter> download_request_limiter_; scoped_refptr<safe_browsing::SafeBrowsingService> safe_browsing_; #if defined(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc index cda953f8..ebf2a250 100644 --- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc +++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc
@@ -11,20 +11,25 @@ #include <utility> #include <vector> +#include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" +#include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/path_service.h" +#include "base/run_loop.h" #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/test/scoped_command_line.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/download/download_browsertest.h" #include "chrome/browser/loader/chrome_navigation_data.h" #include "chrome/browser/policy/cloud/policy_header_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h" @@ -36,6 +41,8 @@ #include "components/signin/core/browser/signin_header_helper.h" #include "components/signin/core/common/signin_pref_names.h" #include "components/signin/core/common/signin_switches.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_data.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/resource_dispatcher_host.h" @@ -56,6 +63,7 @@ using testing::Not; namespace { + static const char kTestPolicyHeader[] = "test_header"; static const char kServerRedirectUrl[] = "/server-redirect"; @@ -87,6 +95,7 @@ TestDispatcherHostDelegate() : should_add_data_reduction_proxy_data_(false) {} ~TestDispatcherHostDelegate() override {} + // ResourceDispatcherHostDelegate implementation: void RequestBeginning( net::URLRequest* request, content::ResourceContext* resource_context, @@ -125,10 +134,40 @@ return ChromeResourceDispatcherHostDelegate::GetNavigationData(request); } + // ChromeResourceDispatcherHost implementation: + void AppendStandardResourceThrottles( + net::URLRequest* request, + content::ResourceContext* resource_context, + content::ResourceType resource_type, + ScopedVector<content::ResourceThrottle>* throttles) override { + ++times_stardard_throttles_added_for_url_[request->url()]; + ChromeResourceDispatcherHostDelegate::AppendStandardResourceThrottles( + request, resource_context, resource_type, throttles); + } + + void set_should_add_data_reduction_proxy_data( + bool should_add_data_reduction_proxy_data) { + should_add_data_reduction_proxy_data_ = + should_add_data_reduction_proxy_data; + } + + const net::HttpRequestHeaders& request_headers() const { + return request_headers_; + } + + // Writes the number of times the standard set of throttles have been added + // for requests for the speficied URL to |count|. + void GetTimesStandardThrottlesAddedForURL(const GURL& url, int* count) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + *count = times_stardard_throttles_added_for_url_[url]; + } + + private: bool should_add_data_reduction_proxy_data_; net::HttpRequestHeaders request_headers_; - private: + std::map<GURL, int> times_stardard_throttles_added_for_url_; + DISALLOW_COPY_AND_ASSIGN(TestDispatcherHostDelegate); }; @@ -194,6 +233,11 @@ (*it)->SetServerURLForTest(dm_url_.spec()); (*it)->UpdateHeader(kTestPolicyHeader); } + + // Set up temp directory for downloads. + ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir()); + browser()->profile()->GetPrefs()->SetFilePath( + prefs::kDownloadDefaultDirectory, downloads_directory_.GetPath()); } void TearDownOnMainThread() override { @@ -202,14 +246,32 @@ } void SetShouldAddDataReductionProxyData(bool add_data) { - dispatcher_host_delegate_->should_add_data_reduction_proxy_data_ = add_data; + dispatcher_host_delegate_->set_should_add_data_reduction_proxy_data( + add_data); + } + + int GetTimesStandardThrottlesAddedForURL(const GURL& url) { + int count; + base::RunLoop run_loop; + content::BrowserThread::PostTaskAndReply( + content::BrowserThread::IO, FROM_HERE, + base::Bind( + &TestDispatcherHostDelegate::GetTimesStandardThrottlesAddedForURL, + base::Unretained(dispatcher_host_delegate_.get()), url, &count), + run_loop.QuitClosure()); + run_loop.Run(); + return count; } protected: // The fake URL for DMServer we are using. GURL dm_url_; std::unique_ptr<TestDispatcherHostDelegate> dispatcher_host_delegate_; + private: + // Location of the downloads directory for tests that use one. + base::ScopedTempDir downloads_directory_; + DISALLOW_COPY_AND_ASSIGN(ChromeResourceDispatcherHostDelegateBrowserTest); }; @@ -220,7 +282,7 @@ // request. DCHECK(!embedded_test_server()->base_url().spec().empty()); ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url()); - ASSERT_FALSE(dispatcher_host_delegate_->request_headers_.HasHeader( + ASSERT_FALSE(dispatcher_host_delegate_->request_headers().HasHeader( policy::kChromePolicyHeader)); } @@ -230,7 +292,7 @@ // request. ui_test_utils::NavigateToURL(browser(), dm_url_); std::string value; - ASSERT_TRUE(dispatcher_host_delegate_->request_headers_.GetHeader( + ASSERT_TRUE(dispatcher_host_delegate_->request_headers().GetHeader( policy::kChromePolicyHeader, &value)); ASSERT_EQ(kTestPolicyHeader, value); } @@ -247,7 +309,7 @@ ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL( redirect_url)); std::string value; - ASSERT_TRUE(dispatcher_host_delegate_->request_headers_.GetHeader( + ASSERT_TRUE(dispatcher_host_delegate_->request_headers().GetHeader( policy::kChromePolicyHeader, &value)); ASSERT_EQ(kTestPolicyHeader, value); } @@ -531,3 +593,46 @@ } } } + +// Check that exactly one set of throttles is added to smaller downloads, which +// have their mime type determined only after the response is completely +// received. +// See https://crbug.com/640545 +IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest, + ThrottlesAddedExactlyOnceToTinySniffedDownloads) { + GURL url = embedded_test_server()->GetURL("/downloads/tiny_binary.bin"); + DownloadTestObserverNotInProgress download_observer( + content::BrowserContext::GetDownloadManager(browser()->profile()), 1); + download_observer.StartObserving(); + ui_test_utils::NavigateToURL(browser(), url); + download_observer.WaitForFinished(); + EXPECT_EQ(1, GetTimesStandardThrottlesAddedForURL(url)); +} + +// Check that exactly one set of throttles is added to larger downloads, which +// have their mime type determined before the end of the response is reported. +IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest, + ThrottlesAddedExactlyOnceToLargeSniffedDownloads) { + GURL url = embedded_test_server()->GetURL("/downloads/thisdayinhistory.xls"); + DownloadTestObserverNotInProgress download_observer( + content::BrowserContext::GetDownloadManager(browser()->profile()), 1); + download_observer.StartObserving(); + ui_test_utils::NavigateToURL(browser(), url); + download_observer.WaitForFinished(); + EXPECT_EQ(1, GetTimesStandardThrottlesAddedForURL(url)); +} + +// Check that exactly one set of throttles is added to downloads started by an +// <a download> click. +IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest, + ThrottlesAddedExactlyOnceToADownloads) { + DownloadTestObserverNotInProgress download_observer( + content::BrowserContext::GetDownloadManager(browser()->profile()), 1); + download_observer.StartObserving(); + ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL( + "/download-anchor-attrib.html")); + download_observer.WaitForFinished(); + EXPECT_EQ(1, + GetTimesStandardThrottlesAddedForURL( + embedded_test_server()->GetURL("/anchor_download_test.png"))); +}
diff --git a/chrome/browser/plugins/plugin_info_message_filter.cc b/chrome/browser/plugins/plugin_info_message_filter.cc index b1ea5b4..b8cc37a 100644 --- a/chrome/browser/plugins/plugin_info_message_filter.cc +++ b/chrome/browser/plugins/plugin_info_message_filter.cc
@@ -365,7 +365,9 @@ } #endif // defined(ENABLE_EXTENSIONS) - if (plugin_setting == CONTENT_SETTING_DETECT_IMPORTANT_CONTENT) { + if (plugin_setting == CONTENT_SETTING_DETECT_IMPORTANT_CONTENT || + (plugin_setting == CONTENT_SETTING_ALLOW && + base::FeatureList::IsEnabled(features::kPreferHtmlOverPlugins))) { *status = ChromeViewHostMsg_GetPluginInfo_Status::kPlayImportantContent; } else if (plugin_setting == CONTENT_SETTING_BLOCK) { // For managed users with the ASK policy, we allow manually running plugins
diff --git a/chrome/browser/plugins/plugin_power_saver_browsertest.cc b/chrome/browser/plugins/plugin_power_saver_browsertest.cc index 6cb3bd6..63136c54 100644 --- a/chrome/browser/plugins/plugin_power_saver_browsertest.cc +++ b/chrome/browser/plugins/plugin_power_saver_browsertest.cc
@@ -12,6 +12,7 @@ #include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" @@ -20,6 +21,7 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/test_switches.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/zoom/zoom_controller.h" #include "content/public/browser/readback_types.h" #include "content/public/browser/render_frame_host.h" @@ -312,6 +314,14 @@ GetActiveWebContents()->GetMainFrame())); } + // Loads a peripheral plugin (small cross origin) named 'plugin'. + void LoadPeripheralPlugin() { + LoadHTML( + "<object id='plugin' data='http://otherorigin.com/fake.swf' " + " type='application/x-shockwave-flash' width='400' height='100'>" + "</object>"); + } + // Returns the background WebContents. content::WebContents* LoadHTMLInBackgroundTab(const std::string& html) { embedded_test_server()->RegisterRequestHandler( @@ -457,6 +467,23 @@ SimulateClickAndAwaitMarkedEssential("plugin_poster", gfx::Point(50, 150)); } +IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest, ContentSettings) { + HostContentSettingsMap* content_settings_map = + HostContentSettingsMapFactory::GetForProfile(browser()->profile()); + + // Throttle on DETECT. + content_settings_map->SetDefaultContentSetting( + CONTENT_SETTINGS_TYPE_PLUGINS, CONTENT_SETTING_DETECT_IMPORTANT_CONTENT); + LoadPeripheralPlugin(); + VerifyPluginIsThrottled(GetActiveWebContents(), "plugin"); + + // Don't throttle on ALLOW. + content_settings_map->SetDefaultContentSetting(CONTENT_SETTINGS_TYPE_PLUGINS, + CONTENT_SETTING_ALLOW); + LoadPeripheralPlugin(); + VerifyPluginMarkedEssential(GetActiveWebContents(), "plugin"); +} + IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest, SmallerThanPlayIcon) { LoadHTML( "<object id='plugin_16' type='application/x-shockwave-flash' " @@ -622,10 +649,7 @@ } IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest, ExpandingSmallPlugin) { - LoadHTML( - "<object id='plugin' data='http://otherorigin.com/fake.swf' " - " type='application/x-shockwave-flash' width='400' height='80'>" - "</object>"); + LoadPeripheralPlugin(); VerifyPluginIsThrottled(GetActiveWebContents(), "plugin"); std::string script = "window.document.getElementById('plugin').height = 400;"; @@ -738,3 +762,27 @@ VerifyPluginMarkedEssential(GetActiveWebContents(), "tiny_cross_origin_1"); VerifyPluginMarkedEssential(GetActiveWebContents(), "tiny_cross_origin_2"); } + +// Separate test case with HTML By Default feature flag on. +class PluginPowerSaverPreferHtmlBrowserTest + : public PluginPowerSaverBrowserTest { + public: + void SetUpInProcessBrowserTestFixture() override { + PluginPowerSaverBrowserTest::SetUpInProcessBrowserTestFixture(); + feature_list.InitAndEnableFeature(features::kPreferHtmlOverPlugins); + } + + private: + base::test::ScopedFeatureList feature_list; +}; + +IN_PROC_BROWSER_TEST_F(PluginPowerSaverPreferHtmlBrowserTest, + ThrottlePluginsOnAllowContentSetting) { + HostContentSettingsMap* content_settings_map = + HostContentSettingsMapFactory::GetForProfile(browser()->profile()); + + content_settings_map->SetDefaultContentSetting(CONTENT_SETTINGS_TYPE_PLUGINS, + CONTENT_SETTING_ALLOW); + LoadPeripheralPlugin(); + VerifyPluginIsThrottled(GetActiveWebContents(), "plugin"); +}
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc index b34cd78..bc80138 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor.cc
@@ -10,6 +10,7 @@ #include "base/command_line.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/strings/string_number_conversions.h" @@ -23,7 +24,6 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/url_constants.h" #include "components/history/core/browser/history_database.h" -#include "components/history/core/browser/history_db_task.h" #include "components/history/core/browser/history_service.h" #include "components/mime_util/mime_util.h" #include "content/public/browser/browser_thread.h" @@ -66,52 +66,6 @@ namespace predictors { //////////////////////////////////////////////////////////////////////////////// -// History lookup task. - -// Used to fetch the visit count for a URL from the History database. -class GetUrlVisitCountTask : public history::HistoryDBTask { - public: - typedef ResourcePrefetchPredictor::URLRequestSummary URLRequestSummary; - typedef base::Callback<void( - size_t, // Visit count. - const NavigationID&, - const std::vector<URLRequestSummary>&)> VisitInfoCallback; - - GetUrlVisitCountTask( - const NavigationID& navigation_id, - std::vector<URLRequestSummary>* requests, - VisitInfoCallback callback) - : visit_count_(0), - navigation_id_(navigation_id), - requests_(requests), - callback_(callback) { - DCHECK(requests_.get()); - } - - bool RunOnDBThread(history::HistoryBackend* backend, - history::HistoryDatabase* db) override { - history::URLRow url_row; - if (db->GetRowForURL(navigation_id_.main_frame_url, &url_row)) - visit_count_ = url_row.visit_count(); - return true; - } - - void DoneRunOnMainThread() override { - callback_.Run(visit_count_, navigation_id_, *requests_); - } - - private: - ~GetUrlVisitCountTask() override {} - - int visit_count_; - NavigationID navigation_id_; - std::unique_ptr<std::vector<URLRequestSummary>> requests_; - VisitInfoCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(GetUrlVisitCountTask); -}; - -//////////////////////////////////////////////////////////////////////////////// // ResourcePrefetchPredictor static functions. // static @@ -257,7 +211,7 @@ } //////////////////////////////////////////////////////////////////////////////// -// ResourcePrefetchPredictor structs. +// ResourcePrefetchPredictor nested types. ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary() : resource_type(content::RESOURCE_TYPE_LAST_TYPE), @@ -318,6 +272,38 @@ return true; } +ResourcePrefetchPredictor::GetUrlVisitCountTask::GetUrlVisitCountTask( + const NavigationID& navigation_id, + std::unique_ptr<PageRequestSummary> summary, + VisitInfoCallback callback) + : visit_count_(0), + navigation_id_(navigation_id), + summary_(std::move(summary)), + callback_(callback) { + DCHECK(summary_.get()); +} + +bool ResourcePrefetchPredictor::GetUrlVisitCountTask::RunOnDBThread( + history::HistoryBackend* backend, + history::HistoryDatabase* db) { + history::URLRow url_row; + if (db->GetRowForURL(navigation_id_.main_frame_url, &url_row)) + visit_count_ = url_row.visit_count(); + return true; +} + +void ResourcePrefetchPredictor::GetUrlVisitCountTask::DoneRunOnMainThread() { + callback_.Run(visit_count_, navigation_id_, *summary_); +} + +ResourcePrefetchPredictor::GetUrlVisitCountTask::~GetUrlVisitCountTask() {} + +ResourcePrefetchPredictor::PageRequestSummary::PageRequestSummary( + const GURL& i_initial_url) + : initial_url(i_initial_url) {} + +ResourcePrefetchPredictor::PageRequestSummary::~PageRequestSummary() {} + //////////////////////////////////////////////////////////////////////////////// // ResourcePrefetchPredictor. @@ -414,9 +400,10 @@ CleanupAbandonedNavigations(request.navigation_id); // New empty navigation entry. - inflight_navigations_.insert(std::make_pair( - request.navigation_id, - make_linked_ptr(new std::vector<URLRequestSummary>()))); + const GURL& initial_url = request.navigation_id.main_frame_url; + inflight_navigations_.insert( + std::make_pair(request.navigation_id, + base::MakeUnique<PageRequestSummary>(initial_url))); } void ResourcePrefetchPredictor::OnMainFrameResponse( @@ -432,27 +419,33 @@ const URLRequestSummary& response) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - // TODO(shishir): There are significant gains to be had here if we can use the - // start URL in a redirect chain as the key to start prefetching. We can save - // of redirect times considerably assuming that the redirect chains do not - // change. - // Stop any inflight prefetching. Remove the older navigation. StopPrefetching(response.navigation_id); - inflight_navigations_.erase(response.navigation_id); - // A redirect will not lead to another OnMainFrameRequest call, so record the - // redirect url as a new navigation. + std::unique_ptr<PageRequestSummary> summary; + NavigationMap::iterator nav_it = + inflight_navigations_.find(response.navigation_id); + if (nav_it != inflight_navigations_.end()) { + summary.reset(nav_it->second.release()); + inflight_navigations_.erase(nav_it); + } // The redirect url may be empty if the URL was invalid. if (response.redirect_url.is_empty()) return; + // If we lost the information about the first hop for some reason. + if (!summary) { + const GURL& initial_url = response.navigation_id.main_frame_url; + summary = base::MakeUnique<PageRequestSummary>(initial_url); + } + + // A redirect will not lead to another OnMainFrameRequest call, so record the + // redirect url as a new navigation id and save the initial url. NavigationID navigation_id(response.navigation_id); navigation_id.main_frame_url = response.redirect_url; - inflight_navigations_.insert(std::make_pair( - navigation_id, - make_linked_ptr(new std::vector<URLRequestSummary>()))); + inflight_navigations_.insert( + std::make_pair(navigation_id, std::move(summary))); } void ResourcePrefetchPredictor::OnSubresourceResponse( @@ -465,7 +458,7 @@ return; } - nav_it->second->push_back(response); + nav_it->second->subresource_requests.push_back(response); } void ResourcePrefetchPredictor::OnNavigationComplete( @@ -480,7 +473,7 @@ const NavigationID navigation_id(nav_it->first); // Remove the navigation from the inflight navigations. - std::vector<URLRequestSummary>* requests = (nav_it->second).release(); + std::unique_ptr<PageRequestSummary> summary = std::move(nav_it->second); inflight_navigations_.erase(nav_it); // Kick off history lookup to determine if we should record the URL. @@ -490,7 +483,7 @@ DCHECK(history_service); history_service->ScheduleDBTask( std::unique_ptr<history::HistoryDBTask>(new GetUrlVisitCountTask( - navigation_id, requests, + navigation_id, std::move(summary), base::Bind(&ResourcePrefetchPredictor::OnVisitCountLookup, AsWeakPtr()))), &history_lookup_consumer_); @@ -586,31 +579,40 @@ initialization_state_ = INITIALIZING; // Create local caches using the database as loaded. - std::unique_ptr<PrefetchDataMap> url_data_map(new PrefetchDataMap()); - std::unique_ptr<PrefetchDataMap> host_data_map(new PrefetchDataMap()); - PrefetchDataMap* url_data_ptr = url_data_map.get(); - PrefetchDataMap* host_data_ptr = host_data_map.get(); + auto url_data_map = base::MakeUnique<PrefetchDataMap>(); + auto host_data_map = base::MakeUnique<PrefetchDataMap>(); + auto url_redirect_data_map = base::MakeUnique<RedirectDataMap>(); + auto host_redirect_data_map = base::MakeUnique<RedirectDataMap>(); BrowserThread::PostTaskAndReply( BrowserThread::DB, FROM_HERE, - base::Bind(&ResourcePrefetchPredictorTables::GetAllData, - tables_, url_data_ptr, host_data_ptr), + base::Bind(&ResourcePrefetchPredictorTables::GetAllData, tables_, + url_data_map.get(), host_data_map.get(), + url_redirect_data_map.get(), host_redirect_data_map.get()), base::Bind(&ResourcePrefetchPredictor::CreateCaches, AsWeakPtr(), - base::Passed(&url_data_map), base::Passed(&host_data_map))); + base::Passed(&url_data_map), base::Passed(&host_data_map), + base::Passed(&url_redirect_data_map), + base::Passed(&host_redirect_data_map))); } void ResourcePrefetchPredictor::CreateCaches( std::unique_ptr<PrefetchDataMap> url_data_map, - std::unique_ptr<PrefetchDataMap> host_data_map) { + std::unique_ptr<PrefetchDataMap> host_data_map, + std::unique_ptr<RedirectDataMap> url_redirect_data_map, + std::unique_ptr<RedirectDataMap> host_redirect_data_map) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(INITIALIZING, initialization_state_); DCHECK(!url_table_cache_); DCHECK(!host_table_cache_); + DCHECK(!url_redirect_table_cache_); + DCHECK(!host_redirect_table_cache_); DCHECK(inflight_navigations_.empty()); url_table_cache_ = std::move(url_data_map); host_table_cache_ = std::move(host_data_map); + url_redirect_table_cache_ = std::move(url_redirect_data_map); + host_redirect_table_cache_ = std::move(host_redirect_data_map); UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.UrlTableMainFrameUrlCount", url_table_cache_->size()); @@ -653,6 +655,8 @@ inflight_navigations_.clear(); url_table_cache_->clear(); host_table_cache_->clear(); + url_redirect_table_cache_->clear(); + host_redirect_table_cache_->clear(); BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, base::Bind(&ResourcePrefetchPredictorTables::DeleteAllData, tables_)); @@ -662,6 +666,7 @@ // Check all the urls in the database and pick out the ones that are present // in the cache. std::vector<std::string> urls_to_delete, hosts_to_delete; + std::vector<std::string> url_redirects_to_delete, host_redirects_to_delete; for (const auto& it : urls) { const std::string& url_spec = it.url().spec(); @@ -670,19 +675,37 @@ url_table_cache_->erase(url_spec); } + if (url_redirect_table_cache_->find(url_spec) != + url_redirect_table_cache_->end()) { + url_redirects_to_delete.push_back(url_spec); + url_redirect_table_cache_->erase(url_spec); + } + const std::string& host = it.url().host(); if (host_table_cache_->find(host) != host_table_cache_->end()) { hosts_to_delete.push_back(host); host_table_cache_->erase(host); } + + if (host_redirect_table_cache_->find(host) != + host_redirect_table_cache_->end()) { + host_redirects_to_delete.push_back(host); + host_redirect_table_cache_->erase(host); + } } if (!urls_to_delete.empty() || !hosts_to_delete.empty()) { - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - base::Bind(&ResourcePrefetchPredictorTables::DeleteData, - tables_, - urls_to_delete, - hosts_to_delete)); + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + base::Bind(&ResourcePrefetchPredictorTables::DeleteResourceData, + tables_, urls_to_delete, hosts_to_delete)); + } + + if (!url_redirects_to_delete.empty() || !host_redirects_to_delete.empty()) { + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + base::Bind(&ResourcePrefetchPredictorTables::DeleteRedirectData, + tables_, url_redirects_to_delete, host_redirects_to_delete)); } } @@ -703,23 +726,49 @@ } data_map->erase(key_to_delete); - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint, - tables_, - key_to_delete, - key_type)); + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + base::Bind( + &ResourcePrefetchPredictorTables::DeleteSingleResourceDataPoint, + tables_, key_to_delete, key_type)); +} + +void ResourcePrefetchPredictor::RemoveOldestEntryInRedirectDataMap( + PrefetchKeyType key_type, + RedirectDataMap* data_map) { + if (data_map->empty()) + return; + + uint64_t oldest_time = UINT64_MAX; + std::string key_to_delete; + for (const auto& kv : *data_map) { + const RedirectData& data = kv.second; + if (key_to_delete.empty() || data.last_visit_time() < oldest_time) { + key_to_delete = data.primary_key(); + oldest_time = data.last_visit_time(); + } + } + + data_map->erase(key_to_delete); + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + base::Bind( + &ResourcePrefetchPredictorTables::DeleteSingleRedirectDataPoint, + tables_, key_to_delete, key_type)); } void ResourcePrefetchPredictor::OnVisitCountLookup( size_t visit_count, const NavigationID& navigation_id, - const std::vector<URLRequestSummary>& requests) { + const PageRequestSummary& summary) { DCHECK_CURRENTLY_ON(BrowserThread::UI); UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HistoryVisitCountForUrl", visit_count); - // URL level data - merge only if we are already saving the data, or we it + // TODO(alexilin): make only one request to DB thread. + + // URL level data - merge only if we already saved the data, or it // meets the cutoff requirement. const std::string url_spec = navigation_id.main_frame_url.spec(); bool already_tracking = url_table_cache_->find(url_spec) != @@ -728,17 +777,19 @@ (visit_count >= config_.min_url_visit_count); if (should_track_url && config_.IsURLLearningEnabled()) { - LearnNavigation(url_spec, PREFETCH_KEY_TYPE_URL, requests, - config_.max_urls_to_track, url_table_cache_.get()); + LearnNavigation(url_spec, PREFETCH_KEY_TYPE_URL, + summary.subresource_requests, config_.max_urls_to_track, + url_table_cache_.get(), summary.initial_url.spec(), + url_redirect_table_cache_.get()); } // Host level data - no cutoff, always learn the navigation if enabled. if (config_.IsHostLearningEnabled()) { - LearnNavigation(navigation_id.main_frame_url.host(), - PREFETCH_KEY_TYPE_HOST, - requests, - config_.max_hosts_to_track, - host_table_cache_.get()); + const std::string host = navigation_id.main_frame_url.host(); + LearnNavigation(host, PREFETCH_KEY_TYPE_HOST, summary.subresource_requests, + config_.max_hosts_to_track, host_table_cache_.get(), + summary.initial_url.host(), + host_redirect_table_cache_.get()); } } @@ -747,7 +798,9 @@ PrefetchKeyType key_type, const std::vector<URLRequestSummary>& new_resources, size_t max_data_map_size, - PrefetchDataMap* data_map) { + PrefetchDataMap* data_map, + const std::string& key_before_redirects, + RedirectDataMap* redirect_map) { DCHECK_CURRENTLY_ON(BrowserThread::UI); // If the primary key is too long reject it. @@ -756,10 +809,9 @@ PrefetchDataMap::iterator cache_entry = data_map->find(key); if (cache_entry == data_map->end()) { - if (data_map->size() >= max_data_map_size) { - // The table is full, delete an entry. + // If the table is full, delete an entry. + if (data_map->size() >= max_data_map_size) RemoveOldestEntryInPrefetchDataMap(key_type, data_map); - } cache_entry = data_map->insert(std::make_pair( key, PrefetchData(key_type, key))).first; @@ -879,23 +931,111 @@ data_map->erase(key); BrowserThread::PostTask( BrowserThread::DB, FROM_HERE, - base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint, - tables_, - key, - key_type)); + base::Bind( + &ResourcePrefetchPredictorTables::DeleteSingleResourceDataPoint, + tables_, key, key_type)); } else { bool is_host = key_type == PREFETCH_KEY_TYPE_HOST; PrefetchData empty_data( !is_host ? PREFETCH_KEY_TYPE_HOST : PREFETCH_KEY_TYPE_URL, std::string()); + RedirectData empty_redirect_data; const PrefetchData& host_data = is_host ? cache_entry->second : empty_data; const PrefetchData& url_data = is_host ? empty_data : cache_entry->second; BrowserThread::PostTask( BrowserThread::DB, FROM_HERE, - base::Bind(&ResourcePrefetchPredictorTables::UpdateData, - tables_, - url_data, - host_data)); + base::Bind(&ResourcePrefetchPredictorTables::UpdateData, tables_, + url_data, host_data, empty_redirect_data, + empty_redirect_data)); + } + + if (key != key_before_redirects) { + LearnRedirect(key_before_redirects, key_type, key, max_data_map_size, + redirect_map); + } +} + +void ResourcePrefetchPredictor::LearnRedirect(const std::string& key, + PrefetchKeyType key_type, + const std::string& final_redirect, + size_t max_redirect_map_size, + RedirectDataMap* redirect_map) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + RedirectDataMap::iterator cache_entry = redirect_map->find(key); + if (cache_entry == redirect_map->end()) { + if (redirect_map->size() >= max_redirect_map_size) + RemoveOldestEntryInRedirectDataMap(key_type, redirect_map); + + RedirectData new_data; + new_data.set_primary_key(key); + cache_entry = redirect_map->insert(std::make_pair(key, new_data)).first; + cache_entry->second.set_last_visit_time( + base::Time::Now().ToInternalValue()); + RedirectStat* redirect_to_add = + cache_entry->second.add_redirect_endpoints(); + redirect_to_add->set_url(final_redirect); + redirect_to_add->set_number_of_hits(1); + } else { + bool need_to_add = true; + cache_entry->second.set_last_visit_time( + base::Time::Now().ToInternalValue()); + + for (RedirectStat& redirect : + *(cache_entry->second.mutable_redirect_endpoints())) { + if (redirect.url() == final_redirect) { + need_to_add = false; + redirect.set_number_of_hits(redirect.number_of_hits() + 1); + redirect.set_consecutive_misses(0); + } else { + redirect.set_number_of_misses(redirect.number_of_misses() + 1); + redirect.set_consecutive_misses(redirect.consecutive_misses() + 1); + } + } + + if (need_to_add) { + RedirectStat* redirect_to_add = + cache_entry->second.add_redirect_endpoints(); + redirect_to_add->set_url(final_redirect); + redirect_to_add->set_number_of_hits(1); + } + } + + // Trim and sort redirects after update. + std::vector<RedirectStat> redirects; + redirects.reserve(cache_entry->second.redirect_endpoints_size()); + for (const RedirectStat& redirect : + cache_entry->second.redirect_endpoints()) { + if (redirect.consecutive_misses() < config_.max_consecutive_misses) + redirects.push_back(redirect); + } + ResourcePrefetchPredictorTables::SortRedirects(&redirects); + + cache_entry->second.clear_redirect_endpoints(); + for (const RedirectStat& redirect : redirects) + cache_entry->second.add_redirect_endpoints()->CopyFrom(redirect); + + if (redirects.empty()) { + redirect_map->erase(cache_entry); + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + base::Bind( + &ResourcePrefetchPredictorTables::DeleteSingleRedirectDataPoint, + tables_, key, key_type)); + } else { + bool is_host = key_type == PREFETCH_KEY_TYPE_HOST; + RedirectData empty_redirect_data; + PrefetchData empty_url_data(PREFETCH_KEY_TYPE_URL, std::string()); + PrefetchData empty_host_data(PREFETCH_KEY_TYPE_HOST, std::string()); + const RedirectData& host_redirect_data = + is_host ? cache_entry->second : empty_redirect_data; + const RedirectData& url_redirect_data = + is_host ? empty_redirect_data : cache_entry->second; + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + base::Bind(&ResourcePrefetchPredictorTables::UpdateData, tables_, + empty_url_data, empty_host_data, url_redirect_data, + host_redirect_data)); } }
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.h b/chrome/browser/predictors/resource_prefetch_predictor.h index c3177cd..969cfb8 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor.h +++ b/chrome/browser/predictors/resource_prefetch_predictor.h
@@ -9,12 +9,12 @@ #include <map> #include <memory> +#include <set> #include <string> #include <vector> #include "base/gtest_prod_util.h" #include "base/macros.h" -#include "base/memory/linked_ptr.h" #include "base/memory/weak_ptr.h" #include "base/scoped_observer.h" #include "base/task/cancelable_task_tracker.h" @@ -22,6 +22,7 @@ #include "chrome/browser/predictors/resource_prefetch_common.h" #include "chrome/browser/predictors/resource_prefetch_predictor_tables.h" #include "chrome/browser/predictors/resource_prefetcher.h" +#include "components/history/core/browser/history_db_task.h" #include "components/history/core/browser/history_service_observer.h" #include "components/history/core/browser/history_types.h" #include "components/keyed_service/core/keyed_service.h" @@ -146,6 +147,8 @@ FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, NavigationUrlNotInDB); FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, NavigationUrlNotInDBAndDBFull); + FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, RedirectUrlNotInDB); + FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, RedirectUrlInDB); FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, OnMainFrameRequest); FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, OnMainFrameRedirect); FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, @@ -159,9 +162,53 @@ INITIALIZED = 2 }; + // Stores information about inflight navigations. + struct PageRequestSummary { + explicit PageRequestSummary(const GURL& initial_url); + ~PageRequestSummary(); + + GURL initial_url; + + // Stores all subresources requests within a single navigation, from initial + // main frame request to navigation completion. + std::vector<URLRequestSummary> subresource_requests; + }; + + // Used to fetch the visit count for a URL from the History database. + class GetUrlVisitCountTask : public history::HistoryDBTask { + public: + typedef ResourcePrefetchPredictor::URLRequestSummary URLRequestSummary; + typedef ResourcePrefetchPredictor::PageRequestSummary PageRequestSummary; + typedef base::Callback<void(size_t, // Visit count. + const NavigationID&, + const PageRequestSummary&)> + VisitInfoCallback; + + GetUrlVisitCountTask(const NavigationID& navigation_id, + std::unique_ptr<PageRequestSummary> summary, + VisitInfoCallback callback); + + bool RunOnDBThread(history::HistoryBackend* backend, + history::HistoryDatabase* db) override; + + void DoneRunOnMainThread() override; + + private: + ~GetUrlVisitCountTask() override; + + int visit_count_; + NavigationID navigation_id_; + std::unique_ptr<PageRequestSummary> summary_; + VisitInfoCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(GetUrlVisitCountTask); + }; + typedef ResourcePrefetchPredictorTables::PrefetchData PrefetchData; typedef ResourcePrefetchPredictorTables::PrefetchDataMap PrefetchDataMap; - typedef std::map<NavigationID, linked_ptr<std::vector<URLRequestSummary> > > + typedef ResourcePrefetchPredictorTables::RedirectDataMap RedirectDataMap; + + typedef std::map<NavigationID, std::unique_ptr<PageRequestSummary>> NavigationMap; // Returns true if the main page request is supported for prediction. @@ -216,9 +263,11 @@ void StartInitialization(); // Callback for task to read predictor database. Takes ownership of - // |url_data_map| and |host_data_map|. + // all arguments. void CreateCaches(std::unique_ptr<PrefetchDataMap> url_data_map, - std::unique_ptr<PrefetchDataMap> host_data_map); + std::unique_ptr<PrefetchDataMap> host_data_map, + std::unique_ptr<RedirectDataMap> url_redirect_data_map, + std::unique_ptr<RedirectDataMap> host_redirect_data_map); // Called during initialization when history is read and the predictor // database has been read. @@ -239,25 +288,38 @@ // Callback for GetUrlVisitCountTask. void OnVisitCountLookup(size_t visit_count, const NavigationID& navigation_id, - const std::vector<URLRequestSummary>& requests); + const PageRequestSummary& summary); // Removes the oldest entry in the input |data_map|, also deleting it from the // predictor database. void RemoveOldestEntryInPrefetchDataMap(PrefetchKeyType key_type, PrefetchDataMap* data_map); + void RemoveOldestEntryInRedirectDataMap(PrefetchKeyType key_type, + RedirectDataMap* data_map); + // Merges resources in |new_resources| into the |data_map| and correspondingly - // updates the predictor database. + // updates the predictor database. Also calls LearnRedirect if relevant. void LearnNavigation(const std::string& key, PrefetchKeyType key_type, const std::vector<URLRequestSummary>& new_resources, size_t max_data_map_size, - PrefetchDataMap* data_map); + PrefetchDataMap* data_map, + const std::string& key_before_redirects, + RedirectDataMap* redirect_map); + + // Updates information about final redirect destination for |key| in + // |redirect_map| and correspondingly updates the predictor database. + void LearnRedirect(const std::string& key, + PrefetchKeyType key_type, + const std::string& final_redirect, + size_t max_redirect_map_size, + RedirectDataMap* redirect_map); // Reports overall page load time. void ReportPageLoadTimeStats(base::TimeDelta plt) const; - // Reports page load time for prefetched and not prefetched pages + // Reports page load time for prefetched and not prefetched pages. void ReportPageLoadTimePrefetchStats( base::TimeDelta plt, bool prefetched, @@ -289,12 +351,13 @@ scoped_refptr<ResourcePrefetcherManager> prefetch_manager_; base::CancelableTaskTracker history_lookup_consumer_; - // Map of all the navigations in flight to their resource requests. - NavigationMap inflight_navigations_; - // Copy of the data in the predictor tables. std::unique_ptr<PrefetchDataMap> url_table_cache_; std::unique_ptr<PrefetchDataMap> host_table_cache_; + std::unique_ptr<RedirectDataMap> url_redirect_table_cache_; + std::unique_ptr<RedirectDataMap> host_redirect_table_cache_; + + NavigationMap inflight_navigations_; ScopedObserver<history::HistoryService, history::HistoryServiceObserver> history_service_observer_;
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.proto b/chrome/browser/predictors/resource_prefetch_predictor.proto index 8616d5c..6d2a59b 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor.proto +++ b/chrome/browser/predictors/resource_prefetch_predictor.proto
@@ -4,11 +4,11 @@ // Protocol buffers used for storing in SQLite. // CAUTION: When any change is done here, bump kDatabaseVersion in -// resource_prefetch_predictor.h. +// resource_prefetch_predictor_tables.h. syntax = "proto2"; -package chrome_browser_predictors; +package predictors; // Required in Chrome. option optimize_for = LITE_RUNTIME; @@ -61,3 +61,20 @@ optional bool has_validators = 8; optional bool always_revalidate = 9; } + +// Represents a mapping from URL or host to a list of redirect endpoints. +message RedirectData { + // Represents a single redirect chain endpoint. + message RedirectStat { + // Represents the host for RedirectData in a host-based table. + optional string url = 1; + optional uint32 number_of_hits = 2; + optional uint32 number_of_misses = 3; + optional uint32 consecutive_misses = 4; + } + + // Represents main frame URL or host. + optional string primary_key = 1; + optional uint64 last_visit_time = 2; + repeated RedirectStat redirect_endpoints = 3; +}
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables.cc b/chrome/browser/predictors/resource_prefetch_predictor_tables.cc index 982e197..a300d5b0 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor_tables.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor_tables.cc
@@ -24,13 +24,17 @@ namespace { using ResourceData = predictors::ResourceData; +using RedirectData = predictors::RedirectData; const char kMetadataTableName[] = "resource_prefetch_predictor_metadata"; const char kUrlResourceTableName[] = "resource_prefetch_predictor_url"; const char kUrlMetadataTableName[] = "resource_prefetch_predictor_url_metadata"; +const char kUrlRedirectTableName[] = "resource_prefetch_predictor_url_redirect"; const char kHostResourceTableName[] = "resource_prefetch_predictor_host"; const char kHostMetadataTableName[] = "resource_prefetch_predictor_host_metadata"; +const char kHostRedirectTableName[] = + "resource_prefetch_predictor_host_redirect"; const char kCreateGlobalMetadataStatementTemplate[] = "CREATE TABLE %s ( " @@ -47,9 +51,16 @@ "main_page_url TEXT, " "last_visit_time INTEGER, " "PRIMARY KEY(main_page_url))"; +const char kCreateRedirectTableStatementTemplate[] = + "CREATE TABLE %s ( " + "main_page_url TEXT, " + "proto BLOB, " + "PRIMARY KEY(main_page_url))"; const char kInsertResourceTableStatementTemplate[] = "INSERT INTO %s (main_page_url, resource_url, proto) VALUES (?,?,?)"; +const char kInsertRedirectTableStatementTemplate[] = + "INSERT INTO %s (main_page_url, proto) VALUES (?,?)"; const char kInsertMetadataTableStatementTemplate[] = "INSERT INTO %s (main_page_url, last_visit_time) VALUES (?,?)"; const char kDeleteStatementTemplate[] = "DELETE FROM %s WHERE main_page_url=?"; @@ -58,7 +69,7 @@ const std::string& primary_key, Statement* statement) { int size = data.ByteSize(); - DCHECK(size > 0); + DCHECK_GT(size, 0); std::vector<char> proto_buffer(size); data.SerializeToArray(&proto_buffer[0], size); @@ -67,6 +78,17 @@ statement->BindBlob(2, &proto_buffer[0], size); } +void BindRedirectDataToStatement(const RedirectData& data, + Statement* statement) { + int size = data.ByteSize(); + DCHECK_GT(size, 0); + std::vector<char> proto_buffer(size); + data.SerializeToArray(&proto_buffer[0], size); + + statement->BindString(0, data.primary_key()); + statement->BindBlob(1, &proto_buffer[0], size); +} + bool StepAndInitializeResourceData(Statement* statement, ResourceData* data, std::string* primary_key) { @@ -74,7 +96,6 @@ return false; *primary_key = statement->ColumnString(0); - int size = statement->ColumnByteLength(2); const void* blob = statement->ColumnBlob(2); DCHECK(blob); @@ -86,6 +107,24 @@ return true; } +bool StepAndInitializeRedirectData(Statement* statement, + RedirectData* data, + std::string* primary_key) { + if (!statement->Step()) + return false; + + *primary_key = statement->ColumnString(0); + + int size = statement->ColumnByteLength(1); + const void* blob = statement->ColumnBlob(1); + DCHECK(blob); + data->ParseFromArray(blob, size); + + DCHECK(data->primary_key() == *primary_key); + + return true; +} + } // namespace namespace predictors { @@ -93,11 +132,18 @@ // static void ResourcePrefetchPredictorTables::SortResources( std::vector<ResourceData>* resources) { - // Sort indices instead of ResourceData objects and then apply resulting - // permutation to the resources. std::sort(resources->begin(), resources->end(), [](const ResourceData& x, const ResourceData& y) { - return ComputeScore(x) > ComputeScore(y); + return ComputeResourceScore(x) > ComputeResourceScore(y); + }); +} + +// static +void ResourcePrefetchPredictorTables::SortRedirects( + std::vector<RedirectStat>* redirects) { + std::sort(redirects->begin(), redirects->end(), + [](const RedirectStat& x, const RedirectStat& y) { + return ComputeRedirectScore(x) > ComputeRedirectScore(y); }); } @@ -121,45 +167,66 @@ void ResourcePrefetchPredictorTables::GetAllData( PrefetchDataMap* url_data_map, - PrefetchDataMap* host_data_map) { + PrefetchDataMap* host_data_map, + RedirectDataMap* url_redirect_data_map, + RedirectDataMap* host_redirect_data_map) { DCHECK_CURRENTLY_ON(BrowserThread::DB); if (CantAccessDatabase()) return; DCHECK(url_data_map); DCHECK(host_data_map); + DCHECK(url_redirect_data_map); + DCHECK(host_redirect_data_map); url_data_map->clear(); host_data_map->clear(); + url_redirect_data_map->clear(); + host_redirect_data_map->clear(); std::vector<std::string> urls_to_delete, hosts_to_delete; - GetAllDataHelper(PREFETCH_KEY_TYPE_URL, url_data_map, &urls_to_delete); - GetAllDataHelper(PREFETCH_KEY_TYPE_HOST, host_data_map, &hosts_to_delete); + GetAllResourceDataHelper(PREFETCH_KEY_TYPE_URL, url_data_map, + &urls_to_delete); + GetAllResourceDataHelper(PREFETCH_KEY_TYPE_HOST, host_data_map, + &hosts_to_delete); + GetAllRedirectDataHelper(PREFETCH_KEY_TYPE_URL, url_redirect_data_map); + GetAllRedirectDataHelper(PREFETCH_KEY_TYPE_HOST, host_redirect_data_map); if (!urls_to_delete.empty() || !hosts_to_delete.empty()) - DeleteData(urls_to_delete, hosts_to_delete); + DeleteResourceData(urls_to_delete, hosts_to_delete); } void ResourcePrefetchPredictorTables::UpdateData( const PrefetchData& url_data, - const PrefetchData& host_data) { + const PrefetchData& host_data, + const RedirectData& url_redirect_data, + const RedirectData& host_redirect_data) { DCHECK_CURRENTLY_ON(BrowserThread::DB); if (CantAccessDatabase()) return; DCHECK(!url_data.is_host() && host_data.is_host()); - DCHECK(!url_data.primary_key.empty() || !host_data.primary_key.empty()); + DCHECK(!url_data.primary_key.empty() || !host_data.primary_key.empty() || + url_redirect_data.has_primary_key() || + host_redirect_data.has_primary_key()); DB()->BeginTransaction(); - bool success = (url_data.primary_key.empty() || UpdateDataHelper(url_data)) && - (host_data.primary_key.empty() || UpdateDataHelper(host_data)); + bool success = + (url_data.primary_key.empty() || + UpdateResourceDataHelper(PREFETCH_KEY_TYPE_URL, url_data)) && + (host_data.primary_key.empty() || + UpdateResourceDataHelper(PREFETCH_KEY_TYPE_HOST, host_data)) && + (!url_redirect_data.has_primary_key() || + UpdateRedirectDataHelper(PREFETCH_KEY_TYPE_URL, url_redirect_data)) && + (!host_redirect_data.has_primary_key() || + UpdateRedirectDataHelper(PREFETCH_KEY_TYPE_HOST, host_redirect_data)); if (!success) DB()->RollbackTransaction(); - - DB()->CommitTransaction(); + else + DB()->CommitTransaction(); } -void ResourcePrefetchPredictorTables::DeleteData( +void ResourcePrefetchPredictorTables::DeleteResourceData( const std::vector<std::string>& urls, const std::vector<std::string>& hosts) { DCHECK_CURRENTLY_ON(BrowserThread::DB); @@ -169,19 +236,44 @@ DCHECK(!urls.empty() || !hosts.empty()); if (!urls.empty()) - DeleteDataHelper(PREFETCH_KEY_TYPE_URL, urls); + DeleteDataHelper(PREFETCH_KEY_TYPE_URL, PrefetchDataType::RESOURCE, urls); if (!hosts.empty()) - DeleteDataHelper(PREFETCH_KEY_TYPE_HOST, hosts); + DeleteDataHelper(PREFETCH_KEY_TYPE_HOST, PrefetchDataType::RESOURCE, hosts); } -void ResourcePrefetchPredictorTables::DeleteSingleDataPoint( +void ResourcePrefetchPredictorTables::DeleteSingleResourceDataPoint( const std::string& key, PrefetchKeyType key_type) { DCHECK_CURRENTLY_ON(BrowserThread::DB); if (CantAccessDatabase()) return; - DeleteDataHelper(key_type, std::vector<std::string>(1, key)); + DeleteDataHelper(key_type, PrefetchDataType::RESOURCE, {key}); +} + +void ResourcePrefetchPredictorTables::DeleteRedirectData( + const std::vector<std::string>& urls, + const std::vector<std::string>& hosts) { + DCHECK_CURRENTLY_ON(BrowserThread::DB); + if (CantAccessDatabase()) + return; + + DCHECK(!urls.empty() || !hosts.empty()); + + if (!urls.empty()) + DeleteDataHelper(PREFETCH_KEY_TYPE_URL, PrefetchDataType::REDIRECT, urls); + if (!hosts.empty()) + DeleteDataHelper(PREFETCH_KEY_TYPE_HOST, PrefetchDataType::REDIRECT, hosts); +} + +void ResourcePrefetchPredictorTables::DeleteSingleRedirectDataPoint( + const std::string& key, + PrefetchKeyType key_type) { + DCHECK_CURRENTLY_ON(BrowserThread::DB); + if (CantAccessDatabase()) + return; + + DeleteDataHelper(key_type, PrefetchDataType::REDIRECT, {key}); } void ResourcePrefetchPredictorTables::DeleteAllData() { @@ -190,8 +282,9 @@ Statement deleter; for (const char* table_name : - {kUrlResourceTableName, kUrlMetadataTableName, kHostResourceTableName, - kHostMetadataTableName}) { + {kUrlResourceTableName, kUrlMetadataTableName, kUrlRedirectTableName, + kHostResourceTableName, kHostMetadataTableName, + kHostRedirectTableName}) { deleter.Assign(DB()->GetUniqueStatement( base::StringPrintf("DELETE FROM %s", table_name).c_str())); deleter.Run(); @@ -205,7 +298,7 @@ ResourcePrefetchPredictorTables::~ResourcePrefetchPredictorTables() { } -void ResourcePrefetchPredictorTables::GetAllDataHelper( +void ResourcePrefetchPredictorTables::GetAllResourceDataHelper( PrefetchKeyType key_type, PrefetchDataMap* data_map, std::vector<std::string>* to_delete) { @@ -235,8 +328,8 @@ // Read the metadata and keep track of entries that have metadata, but no // resource entries, so they can be deleted. - const char* metadata_table_name = is_host ? kHostMetadataTableName : - kUrlMetadataTableName; + const char* metadata_table_name = + is_host ? kHostMetadataTableName : kUrlMetadataTableName; Statement metadata_reader(DB()->GetUniqueStatement( base::StringPrintf("SELECT * FROM %s", metadata_table_name).c_str())); @@ -253,7 +346,26 @@ } } -bool ResourcePrefetchPredictorTables::UpdateDataHelper( +void ResourcePrefetchPredictorTables::GetAllRedirectDataHelper( + PrefetchKeyType key_type, + RedirectDataMap* redirect_map) { + bool is_host = key_type == PREFETCH_KEY_TYPE_HOST; + + const char* redirect_table_name = + is_host ? kHostRedirectTableName : kUrlRedirectTableName; + Statement redirect_reader(DB()->GetUniqueStatement( + base::StringPrintf("SELECT * FROM %s", redirect_table_name).c_str())); + + RedirectData data; + std::string primary_key; + while (StepAndInitializeRedirectData(&redirect_reader, &data, &primary_key)) { + auto result = redirect_map->insert(std::make_pair(primary_key, data)); + DCHECK(result.second); + } +} + +bool ResourcePrefetchPredictorTables::UpdateResourceDataHelper( + PrefetchKeyType key_type, const PrefetchData& data) { DCHECK(!data.primary_key.empty()); @@ -263,57 +375,78 @@ } // Delete the older data from both the tables. - std::unique_ptr<Statement> deleter(data.is_host() - ? GetHostResourceDeleteStatement() - : GetUrlResourceDeleteStatement()); + std::unique_ptr<Statement> deleter(GetTableUpdateStatement( + key_type, PrefetchDataType::RESOURCE, TableOperationType::REMOVE)); deleter->BindString(0, data.primary_key); if (!deleter->Run()) return false; - deleter.reset(data.is_host() ? GetHostMetadataDeleteStatement() : - GetUrlMetadataDeleteStatement()); + deleter = GetTableUpdateStatement(key_type, PrefetchDataType::METADATA, + TableOperationType::REMOVE); deleter->BindString(0, data.primary_key); if (!deleter->Run()) return false; // Add the new data to the tables. for (const ResourceData& resource : data.resources) { - std::unique_ptr<Statement> resource_inserter( - data.is_host() ? GetHostResourceUpdateStatement() - : GetUrlResourceUpdateStatement()); + std::unique_ptr<Statement> resource_inserter(GetTableUpdateStatement( + key_type, PrefetchDataType::RESOURCE, TableOperationType::INSERT)); BindResourceDataToStatement(resource, data.primary_key, resource_inserter.get()); if (!resource_inserter->Run()) return false; } - std::unique_ptr<Statement> metadata_inserter( - data.is_host() ? GetHostMetadataUpdateStatement() - : GetUrlMetadataUpdateStatement()); + std::unique_ptr<Statement> metadata_inserter(GetTableUpdateStatement( + key_type, PrefetchDataType::METADATA, TableOperationType::INSERT)); metadata_inserter->BindString(0, data.primary_key); metadata_inserter->BindInt64(1, data.last_visit.ToInternalValue()); - if (!metadata_inserter->Run()) + return metadata_inserter->Run(); +} + +bool ResourcePrefetchPredictorTables::UpdateRedirectDataHelper( + PrefetchKeyType key_type, + const RedirectData& data) { + DCHECK(data.has_primary_key()); + + if (!StringsAreSmallerThanDBLimit(data)) { + UMA_HISTOGRAM_BOOLEAN("ResourcePrefetchPredictor.DbStringTooLong", true); + return false; + } + + // Delete the older data from the table. + std::unique_ptr<Statement> deleter(GetTableUpdateStatement( + key_type, PrefetchDataType::REDIRECT, TableOperationType::REMOVE)); + deleter->BindString(0, data.primary_key()); + if (!deleter->Run()) return false; - return true; + // Add the new data to the table. + std::unique_ptr<Statement> inserter(GetTableUpdateStatement( + key_type, PrefetchDataType::REDIRECT, TableOperationType::INSERT)); + BindRedirectDataToStatement(data, inserter.get()); + return inserter->Run(); } void ResourcePrefetchPredictorTables::DeleteDataHelper( PrefetchKeyType key_type, + PrefetchDataType data_type, const std::vector<std::string>& keys) { - bool is_host = key_type == PREFETCH_KEY_TYPE_HOST; + bool is_resource = data_type == PrefetchDataType::RESOURCE; for (const std::string& key : keys) { - std::unique_ptr<Statement> deleter(is_host - ? GetHostResourceDeleteStatement() - : GetUrlResourceDeleteStatement()); + std::unique_ptr<Statement> deleter(GetTableUpdateStatement( + key_type, data_type, TableOperationType::REMOVE)); deleter->BindString(0, key); deleter->Run(); - deleter.reset(is_host ? GetHostMetadataDeleteStatement() : - GetUrlMetadataDeleteStatement()); - deleter->BindString(0, key); - deleter->Run(); + if (is_resource) { + // Delete corresponding resource metadata as well. + deleter = GetTableUpdateStatement(key_type, PrefetchDataType::METADATA, + TableOperationType::REMOVE); + deleter->BindString(0, key); + deleter->Run(); + } } } @@ -330,8 +463,21 @@ return true; } +bool ResourcePrefetchPredictorTables::StringsAreSmallerThanDBLimit( + const RedirectData& data) { + if (data.primary_key().length() > kMaxStringLength) + return false; + + for (const RedirectStat& redirect : data.redirect_endpoints()) { + if (redirect.url().length() > kMaxStringLength) + return false; + } + return true; +} + // static -float ResourcePrefetchPredictorTables::ComputeScore(const ResourceData& data) { +float ResourcePrefetchPredictorTables::ComputeResourceScore( + const ResourceData& data) { // The score is calculated so that when the rows are sorted, stylesheets, // scripts and fonts appear first, sorted by position(ascending) and then the // rest of the resources sorted by position (ascending). @@ -350,6 +496,13 @@ } // static +float ResourcePrefetchPredictorTables::ComputeRedirectScore( + const RedirectStat& data) { + // TODO(alexilin): Invent some scoring. + return 0.0; +} + +// static bool ResourcePrefetchPredictorTables::DropTablesIfOutdated( sql::Connection* db) { int version = GetDatabaseVersion(db); @@ -360,7 +513,8 @@ if (incompatible_version) { for (const char* table_name : {kMetadataTableName, kUrlResourceTableName, kHostResourceTableName, - kUrlMetadataTableName, kHostMetadataTableName}) { + kUrlRedirectTableName, kHostRedirectTableName, kUrlMetadataTableName, + kHostMetadataTableName}) { success = success && db->Execute(base::StringPrintf("DROP TABLE IF EXISTS %s", table_name) @@ -427,6 +581,10 @@ db->Execute(base::StringPrintf(kCreateMetadataTableStatementTemplate, kUrlMetadataTableName) .c_str())) && + (db->DoesTableExist(kUrlRedirectTableName) || + db->Execute(base::StringPrintf(kCreateRedirectTableStatementTemplate, + kUrlRedirectTableName) + .c_str())) && (db->DoesTableExist(kHostResourceTableName) || db->Execute(base::StringPrintf(kCreateResourceTableStatementTemplate, kHostResourceTableName) @@ -434,6 +592,10 @@ (db->DoesTableExist(kHostMetadataTableName) || db->Execute(base::StringPrintf(kCreateMetadataTableStatementTemplate, kHostMetadataTableName) + .c_str())) && + (db->DoesTableExist(kHostRedirectTableName) || + db->Execute(base::StringPrintf(kCreateRedirectTableStatementTemplate, + kHostRedirectTableName) .c_str())); if (success) @@ -465,67 +627,61 @@ statement.ColumnInt(0)); } -Statement* - ResourcePrefetchPredictorTables::GetUrlResourceDeleteStatement() { - return new Statement(DB()->GetCachedStatement( - SQL_FROM_HERE, - base::StringPrintf(kDeleteStatementTemplate, kUrlResourceTableName) - .c_str())); +std::unique_ptr<Statement> +ResourcePrefetchPredictorTables::GetTableUpdateStatement( + PrefetchKeyType key_type, + PrefetchDataType data_type, + TableOperationType op_type) { + sql::StatementID id(__FILE__, key_type | (static_cast<int>(data_type) << 1) | + (static_cast<int>(op_type) << 3)); + const char* statement_template = + GetTableUpdateStatementTemplate(op_type, data_type); + const char* table_name = + GetTableUpdateStatementTableName(key_type, data_type); + return base::MakeUnique<Statement>(DB()->GetCachedStatement( + id, base::StringPrintf(statement_template, table_name).c_str())); } -Statement* - ResourcePrefetchPredictorTables::GetUrlResourceUpdateStatement() { - return new Statement(DB()->GetCachedStatement( - SQL_FROM_HERE, base::StringPrintf(kInsertResourceTableStatementTemplate, - kUrlResourceTableName) - .c_str())); +// static +const char* ResourcePrefetchPredictorTables::GetTableUpdateStatementTemplate( + TableOperationType op_type, + PrefetchDataType data_type) { + switch (op_type) { + case TableOperationType::REMOVE: + return kDeleteStatementTemplate; + case TableOperationType::INSERT: + switch (data_type) { + case PrefetchDataType::RESOURCE: + return kInsertResourceTableStatementTemplate; + case PrefetchDataType::REDIRECT: + return kInsertRedirectTableStatementTemplate; + case PrefetchDataType::METADATA: + return kInsertMetadataTableStatementTemplate; + } + } + + NOTREACHED(); + return nullptr; } -Statement* - ResourcePrefetchPredictorTables::GetUrlMetadataDeleteStatement() { - return new Statement(DB()->GetCachedStatement( - SQL_FROM_HERE, - base::StringPrintf(kDeleteStatementTemplate, kUrlMetadataTableName) - .c_str())); -} +// static +const char* ResourcePrefetchPredictorTables::GetTableUpdateStatementTableName( + PrefetchKeyType key_type, + PrefetchDataType data_type) { + DCHECK(key_type == PREFETCH_KEY_TYPE_URL || + key_type == PREFETCH_KEY_TYPE_HOST); + bool is_host = key_type == PREFETCH_KEY_TYPE_HOST; + switch (data_type) { + case PrefetchDataType::RESOURCE: + return is_host ? kHostResourceTableName : kUrlResourceTableName; + case PrefetchDataType::REDIRECT: + return is_host ? kHostRedirectTableName : kUrlRedirectTableName; + case PrefetchDataType::METADATA: + return is_host ? kHostMetadataTableName : kUrlMetadataTableName; + } -Statement* - ResourcePrefetchPredictorTables::GetUrlMetadataUpdateStatement() { - return new Statement(DB()->GetCachedStatement( - SQL_FROM_HERE, base::StringPrintf(kInsertMetadataTableStatementTemplate, - kUrlMetadataTableName) - .c_str())); -} - -Statement* - ResourcePrefetchPredictorTables::GetHostResourceDeleteStatement() { - return new Statement(DB()->GetCachedStatement( - SQL_FROM_HERE, - base::StringPrintf(kDeleteStatementTemplate, kHostResourceTableName) - .c_str())); -} - -Statement* - ResourcePrefetchPredictorTables::GetHostResourceUpdateStatement() { - return new Statement(DB()->GetCachedStatement( - SQL_FROM_HERE, base::StringPrintf(kInsertResourceTableStatementTemplate, - kHostResourceTableName) - .c_str())); -} - -Statement* - ResourcePrefetchPredictorTables::GetHostMetadataDeleteStatement() { - return new Statement(DB()->GetCachedStatement( - SQL_FROM_HERE, - base::StringPrintf(kDeleteStatementTemplate, kHostMetadataTableName) - .c_str())); -} - -Statement* ResourcePrefetchPredictorTables::GetHostMetadataUpdateStatement() { - return new Statement(DB()->GetCachedStatement( - SQL_FROM_HERE, base::StringPrintf(kInsertMetadataTableStatementTemplate, - kHostMetadataTableName) - .c_str())); + NOTREACHED(); + return nullptr; } } // namespace predictors
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables.h b/chrome/browser/predictors/resource_prefetch_predictor_tables.h index b56ed4c..6588b546 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor_tables.h +++ b/chrome/browser/predictors/resource_prefetch_predictor_tables.h
@@ -13,6 +13,7 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "chrome/browser/predictors/predictor_table_base.h" #include "chrome/browser/predictors/resource_prefetch_common.h" @@ -27,7 +28,8 @@ namespace predictors { -using chrome_browser_predictors::ResourceData; +// From resource_prefetch_predictor.proto. +using RedirectStat = RedirectData_RedirectStat; // Interface for database tables used by the ResourcePrefetchPredictor. // All methods except the constructor and destructor need to be called on the DB @@ -36,13 +38,12 @@ // Currently manages: // - UrlResourceTable - resources per Urls. // - UrlMetadataTable - misc data for Urls (like last visit time). +// - UrlRedirectTable - redirects per Urls. // - HostResourceTable - resources per host. // - HostMetadataTable - misc data for hosts. +// - HostRedirectTable - redirects per host. class ResourcePrefetchPredictorTables : public PredictorTableBase { public: - // Sorts the resources by score, decreasing. - static void SortResources(std::vector<ResourceData>* resources); - // Aggregated data for a Url or Host. Although the data differs slightly, we // store them in the same structure, because most of the fields are common and // it allows us to use the same functions. @@ -63,58 +64,95 @@ // Map from primary key to PrefetchData for the key. typedef std::map<std::string, PrefetchData> PrefetchDataMap; + // Map from primary key to RedirectData for the key. + typedef std::map<std::string, RedirectData> RedirectDataMap; + // Returns data for all Urls and Hosts. virtual void GetAllData(PrefetchDataMap* url_data_map, - PrefetchDataMap* host_data_map); + PrefetchDataMap* host_data_map, + RedirectDataMap* url_redirect_data_map, + RedirectDataMap* host_redirect_data_map); // Updates data for a Url and a host. If either of the |url_data| or - // |host_data| has an empty primary key, it will be ignored. - // Note that the Urls and primary key in |url_data| and |host_data| should be - // less than |kMaxStringLength| in length. + // |host_data| or |url_redirect_data| or |host_redirect_data| has an empty + // primary key, it will be ignored. + // Note that the Urls and primary key in |url_data|, |host_data|, + // |url_redirect_data| and |host_redirect_data| should be less than + // |kMaxStringLength| in length. virtual void UpdateData(const PrefetchData& url_data, - const PrefetchData& host_data); + const PrefetchData& host_data, + const RedirectData& url_redirect_data, + const RedirectData& host_redirect_data); // Delete data for the input |urls| and |hosts|. - virtual void DeleteData(const std::vector<std::string>& urls, - const std::vector<std::string>& hosts); + virtual void DeleteResourceData(const std::vector<std::string>& urls, + const std::vector<std::string>& hosts); - // Wrapper over DeleteData for convenience. - virtual void DeleteSingleDataPoint(const std::string& key, - PrefetchKeyType key_type); + // Wrapper over DeleteResourceData for convenience. + virtual void DeleteSingleResourceDataPoint(const std::string& key, + PrefetchKeyType key_type); + + // Delete data for the input |urls| and |hosts|. + virtual void DeleteRedirectData(const std::vector<std::string>& urls, + const std::vector<std::string>& hosts); + + // Wrapper over DeleteRedirectData for convenience. + virtual void DeleteSingleRedirectDataPoint(const std::string& key, + PrefetchKeyType key_type); // Deletes all data in all the tables. virtual void DeleteAllData(); + // Sorts the resources by score, decreasing. + static void SortResources(std::vector<ResourceData>* resources); + + // Sorts the redirects by score, decreasing. + static void SortRedirects(std::vector<RedirectStat>* redirects); + // The maximum length of the string that can be stored in the DB. static constexpr size_t kMaxStringLength = 1024; private: + // Represents the type of information that is stored in prefetch database. + enum class PrefetchDataType { RESOURCE, REDIRECT, METADATA }; + + enum class TableOperationType { INSERT, REMOVE }; + friend class PredictorDatabaseInternal; friend class MockResourcePrefetchPredictorTables; FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTablesTest, DatabaseVersionIsSet); FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTablesTest, DatabaseIsResetWhenIncompatible); - FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTablesTest, ComputeScore); + FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTablesTest, + ComputeResourceScore); ResourcePrefetchPredictorTables(); ~ResourcePrefetchPredictorTables() override; // Helper functions below help perform functions on the Url and host table // using the same code. - void GetAllDataHelper(PrefetchKeyType key_type, - PrefetchDataMap* data_map, - std::vector<std::string>* to_delete); - bool UpdateDataHelper(const PrefetchData& data); + void GetAllResourceDataHelper(PrefetchKeyType key_type, + PrefetchDataMap* data_map, + std::vector<std::string>* to_delete); + void GetAllRedirectDataHelper(PrefetchKeyType key_type, + RedirectDataMap* redirect_map); + bool UpdateResourceDataHelper(PrefetchKeyType key_type, + const PrefetchData& data); + bool UpdateRedirectDataHelper(PrefetchKeyType key_type, + const RedirectData& data); void DeleteDataHelper(PrefetchKeyType key_type, + PrefetchDataType data_type, const std::vector<std::string>& keys); // Returns true if the strings in the |data| are less than |kMaxStringLength| // in length. static bool StringsAreSmallerThanDBLimit(const PrefetchData& data); + static bool StringsAreSmallerThanDBLimit(const RedirectData& data); // Computes score of |data|. - static float ComputeScore(const ResourceData& data); + static float ComputeResourceScore(const ResourceData& data); + static float ComputeRedirectScore(const RedirectStat& data); // PredictorTableBase methods. void CreateTableIfNonExistent() override; @@ -122,23 +160,24 @@ // Database version. Always increment it when any change is made to the data // schema (including the .proto). - static constexpr int kDatabaseVersion = 2; + static constexpr int kDatabaseVersion = 3; static bool DropTablesIfOutdated(sql::Connection* db); static int GetDatabaseVersion(sql::Connection* db); static bool SetDatabaseVersion(sql::Connection* db, int version); - // Helpers to return Statements for cached Statements. The caller must take - // ownership of the return Statements. - sql::Statement* GetUrlResourceDeleteStatement(); - sql::Statement* GetUrlResourceUpdateStatement(); - sql::Statement* GetUrlMetadataDeleteStatement(); - sql::Statement* GetUrlMetadataUpdateStatement(); + // Helper to return Statements for cached Statements. + std::unique_ptr<sql::Statement> GetTableUpdateStatement( + PrefetchKeyType key_type, + PrefetchDataType data_type, + TableOperationType op_type); - sql::Statement* GetHostResourceDeleteStatement(); - sql::Statement* GetHostResourceUpdateStatement(); - sql::Statement* GetHostMetadataDeleteStatement(); - sql::Statement* GetHostMetadataUpdateStatement(); + static const char* GetTableUpdateStatementTemplate( + TableOperationType op_type, + PrefetchDataType data_type); + static const char* GetTableUpdateStatementTableName( + PrefetchKeyType key_type, + PrefetchDataType data_type); DISALLOW_COPY_AND_ASSIGN(ResourcePrefetchPredictorTables); };
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc b/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc index e7835442..c969bdea 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor_tables_unittest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <memory> #include <set> #include <utility> #include <vector> @@ -41,6 +42,7 @@ scoped_refptr<ResourcePrefetchPredictorTables> tables_; using PrefetchDataMap = ResourcePrefetchPredictorTables::PrefetchDataMap; + using RedirectDataMap = ResourcePrefetchPredictorTables::RedirectDataMap; private: using PrefetchData = ResourcePrefetchPredictorTables::PrefetchData; @@ -55,10 +57,20 @@ void TestResourcesAreEqual(const std::vector<ResourceData>& lhs, const std::vector<ResourceData>& rhs) const; + // Checks that the input RedirectData are the same, although the redirects + // can be in different order. + void TestRedirectDataAreEqual(const RedirectDataMap& lhs, + const RedirectDataMap& rhs) const; + void TestRedirectsAreEqual(const std::vector<RedirectStat>& lhs, + const std::vector<RedirectStat>& rhs) const; + void AddKey(PrefetchDataMap* m, const std::string& key) const; + void AddKey(RedirectDataMap* m, const std::string& key) const; PrefetchDataMap test_url_data_; PrefetchDataMap test_host_data_; + RedirectDataMap test_url_redirect_data_; + RedirectDataMap test_host_redirect_data_; }; class ResourcePrefetchPredictorTablesReopenTest @@ -95,38 +107,57 @@ void ResourcePrefetchPredictorTablesTest::TestGetAllData() { PrefetchDataMap actual_url_data, actual_host_data; - tables_->GetAllData(&actual_url_data, &actual_host_data); + RedirectDataMap actual_url_redirect_data, actual_host_redirect_data; + tables_->GetAllData(&actual_url_data, &actual_host_data, + &actual_url_redirect_data, &actual_host_redirect_data); TestPrefetchDataAreEqual(test_url_data_, actual_url_data); TestPrefetchDataAreEqual(test_host_data_, actual_host_data); + TestRedirectDataAreEqual(test_url_redirect_data_, actual_url_redirect_data); + TestRedirectDataAreEqual(test_host_redirect_data_, actual_host_redirect_data); } void ResourcePrefetchPredictorTablesTest::TestDeleteData() { - std::vector<std::string> urls_to_delete, hosts_to_delete; - urls_to_delete.push_back("http://www.google.com"); - urls_to_delete.push_back("http://www.yahoo.com"); - hosts_to_delete.push_back("www.yahoo.com"); + std::vector<std::string> urls_to_delete = {"http://www.google.com", + "http://www.yahoo.com"}; + std::vector<std::string> hosts_to_delete = {"www.yahoo.com"}; - tables_->DeleteData(urls_to_delete, hosts_to_delete); + tables_->DeleteResourceData(urls_to_delete, hosts_to_delete); + + urls_to_delete = {"http://fb.com/google", "http://google.com"}; + hosts_to_delete = {"microsoft.com"}; + + tables_->DeleteRedirectData(urls_to_delete, hosts_to_delete); PrefetchDataMap actual_url_data, actual_host_data; - tables_->GetAllData(&actual_url_data, &actual_host_data); + RedirectDataMap actual_url_redirect_data, actual_host_redirect_data; + tables_->GetAllData(&actual_url_data, &actual_host_data, + &actual_url_redirect_data, &actual_host_redirect_data); PrefetchDataMap expected_url_data, expected_host_data; + RedirectDataMap expected_url_redirect_data, expected_host_redirect_data; AddKey(&expected_url_data, "http://www.reddit.com"); AddKey(&expected_host_data, "www.facebook.com"); + AddKey(&expected_url_redirect_data, "http://nyt.com"); + AddKey(&expected_host_redirect_data, "bbc.com"); TestPrefetchDataAreEqual(expected_url_data, actual_url_data); TestPrefetchDataAreEqual(expected_host_data, actual_host_data); + TestRedirectDataAreEqual(expected_url_redirect_data, + actual_url_redirect_data); + TestRedirectDataAreEqual(expected_host_redirect_data, + actual_host_redirect_data); } void ResourcePrefetchPredictorTablesTest::TestDeleteSingleDataPoint() { // Delete a URL. - tables_->DeleteSingleDataPoint("http://www.reddit.com", - PREFETCH_KEY_TYPE_URL); + tables_->DeleteSingleResourceDataPoint("http://www.reddit.com", + PREFETCH_KEY_TYPE_URL); PrefetchDataMap actual_url_data, actual_host_data; - tables_->GetAllData(&actual_url_data, &actual_host_data); + RedirectDataMap actual_url_redirect_data, actual_host_redirect_data; + tables_->GetAllData(&actual_url_data, &actual_host_data, + &actual_url_redirect_data, &actual_host_redirect_data); PrefetchDataMap expected_url_data; AddKey(&expected_url_data, "http://www.google.com"); @@ -134,18 +165,65 @@ TestPrefetchDataAreEqual(expected_url_data, actual_url_data); TestPrefetchDataAreEqual(test_host_data_, actual_host_data); + TestRedirectDataAreEqual(test_url_redirect_data_, actual_url_redirect_data); + TestRedirectDataAreEqual(test_host_redirect_data_, actual_host_redirect_data); // Delete a host. - tables_->DeleteSingleDataPoint("www.facebook.com", PREFETCH_KEY_TYPE_HOST); + tables_->DeleteSingleResourceDataPoint("www.facebook.com", + PREFETCH_KEY_TYPE_HOST); actual_url_data.clear(); actual_host_data.clear(); - tables_->GetAllData(&actual_url_data, &actual_host_data); + actual_url_redirect_data.clear(); + actual_host_redirect_data.clear(); + tables_->GetAllData(&actual_url_data, &actual_host_data, + &actual_url_redirect_data, &actual_host_redirect_data); PrefetchDataMap expected_host_data; AddKey(&expected_host_data, "www.yahoo.com"); TestPrefetchDataAreEqual(expected_url_data, actual_url_data); TestPrefetchDataAreEqual(expected_host_data, actual_host_data); + TestRedirectDataAreEqual(test_url_redirect_data_, actual_url_redirect_data); + TestRedirectDataAreEqual(test_host_redirect_data_, actual_host_redirect_data); + + // Delete a URL redirect. + tables_->DeleteSingleRedirectDataPoint("http://nyt.com", + PREFETCH_KEY_TYPE_URL); + actual_url_data.clear(); + actual_host_data.clear(); + actual_url_redirect_data.clear(); + actual_host_redirect_data.clear(); + tables_->GetAllData(&actual_url_data, &actual_host_data, + &actual_url_redirect_data, &actual_host_redirect_data); + + RedirectDataMap expected_url_redirect_data; + AddKey(&expected_url_redirect_data, "http://fb.com/google"); + AddKey(&expected_url_redirect_data, "http://google.com"); + + TestPrefetchDataAreEqual(expected_url_data, actual_url_data); + TestPrefetchDataAreEqual(expected_host_data, actual_host_data); + TestRedirectDataAreEqual(expected_url_redirect_data, + actual_url_redirect_data); + TestRedirectDataAreEqual(test_host_redirect_data_, actual_host_redirect_data); + + // Delete a host redirect. + tables_->DeleteSingleRedirectDataPoint("bbc.com", PREFETCH_KEY_TYPE_HOST); + actual_url_data.clear(); + actual_host_data.clear(); + actual_url_redirect_data.clear(); + actual_host_redirect_data.clear(); + tables_->GetAllData(&actual_url_data, &actual_host_data, + &actual_url_redirect_data, &actual_host_redirect_data); + + RedirectDataMap expected_host_redirect_data; + AddKey(&expected_host_redirect_data, "microsoft.com"); + + TestPrefetchDataAreEqual(expected_url_data, actual_url_data); + TestPrefetchDataAreEqual(expected_host_data, actual_host_data); + TestRedirectDataAreEqual(expected_url_redirect_data, + actual_url_redirect_data); + TestRedirectDataAreEqual(expected_host_redirect_data, + actual_host_redirect_data); } void ResourcePrefetchPredictorTablesTest::TestUpdateData() { @@ -170,12 +248,29 @@ "http://www.yahoo.com/image.png", content::RESOURCE_TYPE_IMAGE, 120, 1, 1, 10.0, net::MEDIUM, true, false)); - tables_->UpdateData(google, yahoo); + RedirectData facebook; + facebook.set_primary_key("http://fb.com/google"); + facebook.set_last_visit_time(20); + InitializeRedirectStat(facebook.add_redirect_endpoints(), + "https://facebook.fr/google", 4, 2, 1); + + RedirectData microsoft; + microsoft.set_primary_key("microsoft.com"); + microsoft.set_last_visit_time(21); + InitializeRedirectStat(microsoft.add_redirect_endpoints(), "m.microsoft.com", + 5, 7, 1); + InitializeRedirectStat(microsoft.add_redirect_endpoints(), "microsoft.org", 7, + 2, 0); + + tables_->UpdateData(google, yahoo, facebook, microsoft); PrefetchDataMap actual_url_data, actual_host_data; - tables_->GetAllData(&actual_url_data, &actual_host_data); + RedirectDataMap actual_url_redirect_data, actual_host_redirect_data; + tables_->GetAllData(&actual_url_data, &actual_host_data, + &actual_url_redirect_data, &actual_host_redirect_data); PrefetchDataMap expected_url_data, expected_host_data; + RedirectDataMap expected_url_redirect_data, expected_host_redirect_data; AddKey(&expected_url_data, "http://www.reddit.com"); AddKey(&expected_url_data, "http://www.yahoo.com"); expected_url_data.insert(std::make_pair("http://www.google.com", google)); @@ -183,17 +278,34 @@ AddKey(&expected_host_data, "www.facebook.com"); expected_host_data.insert(std::make_pair("www.yahoo.com", yahoo)); + AddKey(&expected_url_redirect_data, "http://nyt.com"); + AddKey(&expected_url_redirect_data, "http://google.com"); + expected_url_redirect_data.insert( + std::make_pair("http://fb.com/google", facebook)); + + AddKey(&expected_host_redirect_data, "bbc.com"); + expected_host_redirect_data.insert( + std::make_pair("microsoft.com", microsoft)); + TestPrefetchDataAreEqual(expected_url_data, actual_url_data); TestPrefetchDataAreEqual(expected_host_data, actual_host_data); + TestRedirectDataAreEqual(expected_url_redirect_data, + actual_url_redirect_data); + TestRedirectDataAreEqual(expected_host_redirect_data, + actual_host_redirect_data); } void ResourcePrefetchPredictorTablesTest::TestDeleteAllData() { tables_->DeleteAllData(); PrefetchDataMap actual_url_data, actual_host_data; - tables_->GetAllData(&actual_url_data, &actual_host_data); + RedirectDataMap actual_url_redirect_data, actual_host_redirect_data; + tables_->GetAllData(&actual_url_data, &actual_host_data, + &actual_url_redirect_data, &actual_host_redirect_data); EXPECT_TRUE(actual_url_data.empty()); EXPECT_TRUE(actual_host_data.empty()); + EXPECT_TRUE(actual_url_redirect_data.empty()); + EXPECT_TRUE(actual_host_redirect_data.empty()); } void ResourcePrefetchPredictorTablesTest::TestPrefetchDataAreEqual( @@ -202,8 +314,10 @@ EXPECT_EQ(lhs.size(), rhs.size()); for (const std::pair<std::string, PrefetchData>& p : rhs) { - PrefetchDataMap::const_iterator lhs_it = lhs.find(p.first); + const auto lhs_it = lhs.find(p.first); ASSERT_TRUE(lhs_it != lhs.end()) << p.first; + EXPECT_TRUE(lhs_it->second.key_type == p.second.key_type); + EXPECT_TRUE(lhs_it->second.last_visit == p.second.last_visit); TestResourcesAreEqual(lhs_it->second.resources, p.second.resources); } @@ -230,19 +344,81 @@ EXPECT_EQ(lhs.size(), resources_seen.size()); } +void ResourcePrefetchPredictorTablesTest::TestRedirectDataAreEqual( + const RedirectDataMap& lhs, + const RedirectDataMap& rhs) const { + EXPECT_EQ(lhs.size(), rhs.size()); + + for (const auto& p : rhs) { + const auto lhs_it = lhs.find(p.first); + ASSERT_TRUE(lhs_it != lhs.end()) << p.first; + EXPECT_EQ(lhs_it->second.primary_key(), p.second.primary_key()); + EXPECT_EQ(lhs_it->second.last_visit_time(), p.second.last_visit_time()); + + std::vector<RedirectStat> lhs_redirects( + lhs_it->second.redirect_endpoints().begin(), + lhs_it->second.redirect_endpoints().end()); + std::vector<RedirectStat> rhs_redirects( + p.second.redirect_endpoints().begin(), + p.second.redirect_endpoints().end()); + + TestRedirectsAreEqual(lhs_redirects, rhs_redirects); + } +} + +void ResourcePrefetchPredictorTablesTest::TestRedirectsAreEqual( + const std::vector<RedirectStat>& lhs, + const std::vector<RedirectStat>& rhs) const { + EXPECT_EQ(lhs.size(), rhs.size()); + + std::map<std::string, RedirectStat> lhs_index; + // Repeated redirects are not allowed. + for (const auto& r : lhs) + EXPECT_TRUE(lhs_index.insert(std::make_pair(r.url(), r)).second); + + for (const auto& r : rhs) { + auto lhs_it = lhs_index.find(r.url()); + if (lhs_it != lhs_index.end()) { + EXPECT_EQ(r, lhs_it->second); + lhs_index.erase(lhs_it); + } else { + ADD_FAILURE() << r.url(); + } + } + + EXPECT_TRUE(lhs_index.empty()); +} + void ResourcePrefetchPredictorTablesTest::AddKey(PrefetchDataMap* m, const std::string& key) const { PrefetchDataMap::const_iterator it = test_url_data_.find(key); if (it != test_url_data_.end()) { - m->insert(std::make_pair(it->first, it->second)); + m->insert(*it); return; } it = test_host_data_.find(key); ASSERT_TRUE(it != test_host_data_.end()); - m->insert(std::make_pair(it->first, it->second)); + m->insert(*it); +} + +void ResourcePrefetchPredictorTablesTest::AddKey(RedirectDataMap* m, + const std::string& key) const { + auto it = test_url_redirect_data_.find(key); + if (it != test_url_redirect_data_.end()) { + m->insert(*it); + return; + } + it = test_host_redirect_data_.find(key); + ASSERT_TRUE(it != test_host_redirect_data_.end()); + m->insert(*it); } void ResourcePrefetchPredictorTablesTest::InitializeSampleData() { + PrefetchData empty_url_data(PREFETCH_KEY_TYPE_URL, std::string()); + PrefetchData empty_host_data(PREFETCH_KEY_TYPE_HOST, std::string()); + RedirectData empty_url_redirect_data; + RedirectData empty_host_redirect_data; + { // Url data. PrefetchData google(PREFETCH_KEY_TYPE_URL, "http://www.google.com"); google.last_visit = base::Time::FromInternalValue(1); @@ -283,10 +459,12 @@ test_url_data_.insert(std::make_pair("http://www.reddit.com", reddit)); test_url_data_.insert(std::make_pair("http://www.yahoo.com", yahoo)); - PrefetchData empty_host_data(PREFETCH_KEY_TYPE_HOST, std::string()); - tables_->UpdateData(google, empty_host_data); - tables_->UpdateData(reddit, empty_host_data); - tables_->UpdateData(yahoo, empty_host_data); + tables_->UpdateData(google, empty_host_data, empty_url_redirect_data, + empty_host_redirect_data); + tables_->UpdateData(reddit, empty_host_data, empty_url_redirect_data, + empty_host_redirect_data); + tables_->UpdateData(yahoo, empty_host_data, empty_url_redirect_data, + empty_host_redirect_data); } { // Host data. @@ -319,9 +497,72 @@ test_host_data_.insert(std::make_pair("www.facebook.com", facebook)); test_host_data_.insert(std::make_pair("www.yahoo.com", yahoo)); - PrefetchData empty_url_data(PREFETCH_KEY_TYPE_URL, std::string()); - tables_->UpdateData(empty_url_data, facebook); - tables_->UpdateData(empty_url_data, yahoo); + tables_->UpdateData(empty_url_data, facebook, empty_url_redirect_data, + empty_host_redirect_data); + tables_->UpdateData(empty_url_data, yahoo, empty_url_redirect_data, + empty_host_redirect_data); + } + + { // Url redirect data. + RedirectData facebook; + facebook.set_primary_key("http://fb.com/google"); + facebook.set_last_visit_time(6); + InitializeRedirectStat(facebook.add_redirect_endpoints(), + "https://facebook.com/google", 5, 1, 0); + InitializeRedirectStat(facebook.add_redirect_endpoints(), + "https://facebook.com/login", 3, 5, 1); + + RedirectData nytimes; + nytimes.set_primary_key("http://nyt.com"); + nytimes.set_last_visit_time(7); + InitializeRedirectStat(nytimes.add_redirect_endpoints(), + "https://nytimes.com", 2, 0, 0); + + RedirectData google; + google.set_primary_key("http://google.com"); + google.set_last_visit_time(8); + InitializeRedirectStat(google.add_redirect_endpoints(), + "https://google.com", 3, 0, 0); + + test_url_redirect_data_.clear(); + test_url_redirect_data_.insert( + std::make_pair(facebook.primary_key(), facebook)); + test_url_redirect_data_.insert( + std::make_pair(nytimes.primary_key(), nytimes)); + test_url_redirect_data_.insert( + std::make_pair(google.primary_key(), google)); + + tables_->UpdateData(empty_url_data, empty_host_data, facebook, + empty_host_redirect_data); + tables_->UpdateData(empty_url_data, empty_host_data, nytimes, + empty_host_redirect_data); + tables_->UpdateData(empty_url_data, empty_host_data, google, + empty_host_redirect_data); + } + + { // Host redirect data. + RedirectData bbc; + bbc.set_primary_key("bbc.com"); + bbc.set_last_visit_time(9); + InitializeRedirectStat(bbc.add_redirect_endpoints(), "www.bbc.com", 8, 4, + 1); + InitializeRedirectStat(bbc.add_redirect_endpoints(), "m.bbc.com", 5, 8, 0); + InitializeRedirectStat(bbc.add_redirect_endpoints(), "bbc.co.uk", 1, 3, 0); + + RedirectData microsoft; + microsoft.set_primary_key("microsoft.com"); + microsoft.set_last_visit_time(10); + InitializeRedirectStat(microsoft.add_redirect_endpoints(), + "www.microsoft.com", 10, 0, 0); + + test_host_redirect_data_.clear(); + test_host_redirect_data_.insert(std::make_pair(bbc.primary_key(), bbc)); + test_host_redirect_data_.insert( + std::make_pair(microsoft.primary_key(), microsoft)); + tables_->UpdateData(empty_url_data, empty_host_data, + empty_url_redirect_data, bbc); + tables_->UpdateData(empty_url_data, empty_host_data, + empty_url_redirect_data, microsoft); } } @@ -333,7 +574,7 @@ // Test cases. -TEST_F(ResourcePrefetchPredictorTablesTest, ComputeScore) { +TEST_F(ResourcePrefetchPredictorTablesTest, ComputeResourceScore) { ResourceData js_resource = CreateResourceData( "http://www.resources.google.com/script.js", content::RESOURCE_TYPE_SCRIPT, 11, 0, 0, 1., net::MEDIUM, false, false); @@ -349,13 +590,13 @@ content::RESOURCE_TYPE_FONT_RESOURCE, 11, 0, 0, 1., net::MEDIUM, false, false); float js_resource_score = - ResourcePrefetchPredictorTables::ComputeScore(js_resource); + ResourcePrefetchPredictorTables::ComputeResourceScore(js_resource); float css_resource_score = - ResourcePrefetchPredictorTables::ComputeScore(css_resource); + ResourcePrefetchPredictorTables::ComputeResourceScore(css_resource); float font_resource_score = - ResourcePrefetchPredictorTables::ComputeScore(font_resource); + ResourcePrefetchPredictorTables::ComputeResourceScore(font_resource); float image_resource_score = - ResourcePrefetchPredictorTables::ComputeScore(image_resource); + ResourcePrefetchPredictorTables::ComputeResourceScore(image_resource); EXPECT_TRUE(js_resource_score == css_resource_score); EXPECT_TRUE(js_resource_score == font_resource_score); @@ -403,7 +644,9 @@ ASSERT_EQ(version, ResourcePrefetchPredictorTables::GetDatabaseVersion(db)); PrefetchDataMap url_data, host_data; - tables_->GetAllData(&url_data, &host_data); + RedirectDataMap url_redirect_data, host_redirect_data; + tables_->GetAllData(&url_data, &host_data, &url_redirect_data, + &host_redirect_data); EXPECT_TRUE(url_data.empty()); EXPECT_TRUE(host_data.empty()); }
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_test_util.cc b/chrome/browser/predictors/resource_prefetch_predictor_test_util.cc index 013e1d46..5e18536 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor_test_util.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor_test_util.cc
@@ -33,13 +33,21 @@ return resource; } -void PrintTo(const ResourceData& resource, ::std::ostream* os) { - *os << "[" << resource.resource_url() << "," << resource.resource_type() - << "," << resource.number_of_hits() << "," << resource.number_of_misses() - << "," << resource.consecutive_misses() << "," - << resource.average_position() << "," << resource.priority() << "," - << resource.has_validators() << "," << resource.always_revalidate() - << "]"; +void InitializeRedirectStat(RedirectStat* redirect, + const std::string& url, + int number_of_hits, + int number_of_misses, + int consecutive_misses) { + redirect->set_url(url); + redirect->set_number_of_hits(number_of_hits); + redirect->set_number_of_misses(number_of_misses); + redirect->set_consecutive_misses(consecutive_misses); +} + +RedirectData CreateRedirectData(const std::string& primary_key) { + RedirectData data; + data.set_primary_key(primary_key); + return data; } void PrintTo(const PrefetchData& data, ::std::ostream* os) { @@ -52,6 +60,44 @@ } } +void PrintTo(const ResourceData& resource, ::std::ostream* os) { + *os << "[" << resource.resource_url() << "," << resource.resource_type() + << "," << resource.number_of_hits() << "," << resource.number_of_misses() + << "," << resource.consecutive_misses() << "," + << resource.average_position() << "," << resource.priority() << "," + << resource.has_validators() << "," << resource.always_revalidate() + << "]"; +} + +void PrintTo(const RedirectStat& redirect, ::std::ostream* os) { + *os << "[" << redirect.url() << "," << redirect.number_of_hits() << "," + << redirect.number_of_misses() << "," << redirect.consecutive_misses() + << "]"; +} + +void PrintTo(const RedirectData& data, ::std::ostream* os) { + *os << "[" << data.primary_key() << "," << data.last_visit_time() << "]\n"; + for (const RedirectStat& redirect : data.redirect_endpoints()) { + *os << "\t\t"; + PrintTo(redirect, os); + *os << "\n"; + } +} + +bool operator==(const PrefetchData& lhs, const PrefetchData& rhs) { + bool equal = lhs.key_type == rhs.key_type && + lhs.primary_key == rhs.primary_key && + lhs.resources.size() == rhs.resources.size(); + + if (!equal) + return false; + + for (size_t i = 0; i < lhs.resources.size(); ++i) + equal = equal && lhs.resources[i] == rhs.resources[i]; + + return equal; +} + bool operator==(const ResourceData& lhs, const ResourceData& rhs) { return lhs.resource_url() == rhs.resource_url() && lhs.resource_type() == rhs.resource_type() && @@ -64,16 +110,22 @@ lhs.always_revalidate() == rhs.always_revalidate(); } -bool operator==(const PrefetchData& lhs, const PrefetchData& rhs) { - bool equal = lhs.key_type == rhs.key_type && - lhs.primary_key == rhs.primary_key && - lhs.resources.size() == rhs.resources.size(); +bool operator==(const RedirectStat& lhs, const RedirectStat& rhs) { + return lhs.url() == rhs.url() && + lhs.number_of_hits() == rhs.number_of_hits() && + lhs.number_of_misses() == rhs.number_of_misses() && + lhs.consecutive_misses() == rhs.consecutive_misses(); +} + +bool operator==(const RedirectData& lhs, const RedirectData& rhs) { + bool equal = lhs.primary_key() == rhs.primary_key() && + lhs.redirect_endpoints_size() == rhs.redirect_endpoints_size(); if (!equal) return false; - for (size_t i = 0; i < lhs.resources.size(); ++i) - equal = equal && lhs.resources[i] == rhs.resources[i]; + for (int i = 0; i < lhs.redirect_endpoints_size(); ++i) + equal = equal && lhs.redirect_endpoints(i) == rhs.redirect_endpoints(i); return equal; }
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_test_util.h b/chrome/browser/predictors/resource_prefetch_predictor_test_util.h index c07fc13..ff707a3 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor_test_util.h +++ b/chrome/browser/predictors/resource_prefetch_predictor_test_util.h
@@ -4,6 +4,8 @@ #ifndef CHROME_BROWSER_PREDICTORS_RESOURCE_PREFETCH_PREDICTOR_TEST_UTIL_H_ #define CHROME_BROWSER_PREDICTORS_RESOURCE_PREFETCH_PREDICTOR_TEST_UTIL_H_ +#include <string> + #include "chrome/browser/predictors/resource_prefetch_predictor_tables.h" namespace predictors { @@ -18,14 +20,26 @@ bool has_validators, bool always_revalidate); +void InitializeRedirectStat(RedirectStat* redirect, + const std::string& url, + int number_of_hits, + int number_of_misses, + int consecutive_misses); + +RedirectData CreateRedirectData(const std::string& primary_key); + // For printing failures nicely. -void PrintTo(const ResourceData& resource, ::std::ostream* os); void PrintTo(const ResourcePrefetchPredictorTables::PrefetchData& data, ::std::ostream* os); +void PrintTo(const ResourceData& resource, ::std::ostream* os); +void PrintTo(const RedirectStat& redirect, ::std::ostream* os); +void PrintTo(const RedirectData& data, ::std::ostream* os); -bool operator==(const ResourceData& lhs, const ResourceData& rhs); bool operator==(const ResourcePrefetchPredictorTables::PrefetchData& lhs, const ResourcePrefetchPredictorTables::PrefetchData& rhs); +bool operator==(const ResourceData& lhs, const ResourceData& rhs); +bool operator==(const RedirectStat& lhs, const RedirectStat& rhs); +bool operator==(const RedirectData& lhs, const RedirectData& rhs); } // namespace predictors
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc index 81f6d48..8829036b 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
@@ -6,6 +6,7 @@ #include <iostream> #include <memory> +#include <utility> #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" @@ -24,7 +25,6 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_job.h" #include "net/url_request/url_request_test_util.h" -#include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -38,6 +38,7 @@ typedef ResourcePrefetchPredictor::URLRequestSummary URLRequestSummary; typedef ResourcePrefetchPredictorTables::PrefetchData PrefetchData; typedef ResourcePrefetchPredictorTables::PrefetchDataMap PrefetchDataMap; +typedef ResourcePrefetchPredictorTables::RedirectDataMap RedirectDataMap; scoped_refptr<net::HttpResponseHeaders> MakeResponseHeaders( const char* headers) { @@ -127,14 +128,26 @@ public: MockResourcePrefetchPredictorTables() { } - MOCK_METHOD2(GetAllData, void(PrefetchDataMap* url_data_map, - PrefetchDataMap* host_data_map)); - MOCK_METHOD2(UpdateData, void(const PrefetchData& url_data, - const PrefetchData& host_data)); - MOCK_METHOD2(DeleteData, void(const std::vector<std::string>& urls, - const std::vector<std::string>& hosts)); - MOCK_METHOD2(DeleteSingleDataPoint, void(const std::string& key, - PrefetchKeyType key_type)); + MOCK_METHOD4(GetAllData, + void(PrefetchDataMap* url_data_map, + PrefetchDataMap* host_data_map, + RedirectDataMap* url_redirect_data_map, + RedirectDataMap* host_redirect_data_map)); + MOCK_METHOD4(UpdateData, + void(const PrefetchData& url_data, + const PrefetchData& host_data, + const RedirectData& url_redirect_data, + const RedirectData& host_redirect_data)); + MOCK_METHOD2(DeleteResourceData, + void(const std::vector<std::string>& urls, + const std::vector<std::string>& hosts)); + MOCK_METHOD2(DeleteSingleResourceDataPoint, + void(const std::string& key, PrefetchKeyType key_type)); + MOCK_METHOD2(DeleteRedirectData, + void(const std::vector<std::string>& urls, + const std::vector<std::string>& hosts)); + MOCK_METHOD2(DeleteSingleRedirectDataPoint, + void(const std::string& key, PrefetchKeyType key_type)); MOCK_METHOD0(DeleteAllData, void()); protected: @@ -172,19 +185,20 @@ return navigation_id; } - ResourcePrefetchPredictor::URLRequestSummary CreateURLRequestSummary( + URLRequestSummary CreateURLRequestSummary( int process_id, int render_frame_id, const std::string& main_frame_url, - const std::string& resource_url, - content::ResourceType resource_type, - net::RequestPriority priority, - const std::string& mime_type, - bool was_cached) { - ResourcePrefetchPredictor::URLRequestSummary summary; + const std::string& resource_url = std::string(), + content::ResourceType resource_type = content::RESOURCE_TYPE_MAIN_FRAME, + net::RequestPriority priority = net::MEDIUM, + const std::string& mime_type = std::string(), + bool was_cached = false) { + URLRequestSummary summary; summary.navigation_id = CreateNavigationID(process_id, render_frame_id, main_frame_url); - summary.resource_url = GURL(resource_url); + summary.resource_url = + resource_url.empty() ? GURL(main_frame_url) : GURL(resource_url); summary.resource_type = resource_type; summary.priority = priority; summary.mime_type = mime_type; @@ -192,6 +206,17 @@ return summary; } + URLRequestSummary CreateRedirectRequestSummary( + int process_id, + int render_frame_id, + const std::string& main_frame_url, + const std::string& redirect_url) { + URLRequestSummary summary = + CreateURLRequestSummary(process_id, render_frame_id, main_frame_url); + summary.redirect_url = GURL(redirect_url); + return summary; + } + std::unique_ptr<net::URLRequest> CreateURLRequest( const GURL& url, net::RequestPriority priority, @@ -254,8 +279,12 @@ PrefetchDataMap test_url_data_; PrefetchDataMap test_host_data_; + RedirectDataMap test_url_redirect_data_; + RedirectDataMap test_host_redirect_data_; PrefetchData empty_url_data_; PrefetchData empty_host_data_; + RedirectData empty_url_redirect_data_; + RedirectData empty_host_redirect_data_; MockURLRequestJobFactory url_request_job_factory_; EmptyURLRequestDelegate url_request_delegate_; @@ -268,7 +297,9 @@ profile_(new TestingProfile()), mock_tables_(new StrictMock<MockResourcePrefetchPredictorTables>()), empty_url_data_(PREFETCH_KEY_TYPE_URL, std::string()), - empty_host_data_(PREFETCH_KEY_TYPE_HOST, std::string()) {} + empty_host_data_(PREFETCH_KEY_TYPE_HOST, std::string()), + empty_url_redirect_data_(), + empty_host_redirect_data_() {} ResourcePrefetchPredictorTest::~ResourcePrefetchPredictorTest() { profile_.reset(NULL); @@ -288,7 +319,9 @@ ResourcePrefetchPredictor::NOT_INITIALIZED); EXPECT_CALL(*mock_tables_.get(), GetAllData(Pointee(ContainerEq(PrefetchDataMap())), - Pointee(ContainerEq(PrefetchDataMap())))); + Pointee(ContainerEq(PrefetchDataMap())), + Pointee(ContainerEq(RedirectDataMap())), + Pointee(ContainerEq(RedirectDataMap())))); InitializePredictor(); EXPECT_TRUE(predictor_->inflight_navigations_.empty()); EXPECT_EQ(predictor_->initialization_state_, @@ -373,24 +406,81 @@ test_host_data_.insert(std::make_pair("www.facebook.com", facebook)); test_host_data_.insert(std::make_pair("www.yahoo.com", yahoo)); } + + { // Url redirect data. + RedirectData facebook; + facebook.set_primary_key("http://fb.com/google"); + facebook.set_last_visit_time(6); + InitializeRedirectStat(facebook.add_redirect_endpoints(), + "https://facebook.com/google", 5, 1, 0); + InitializeRedirectStat(facebook.add_redirect_endpoints(), + "https://facebook.com/login", 3, 5, 1); + + RedirectData nytimes; + nytimes.set_primary_key("http://nyt.com"); + nytimes.set_last_visit_time(7); + InitializeRedirectStat(nytimes.add_redirect_endpoints(), + "https://nytimes.com", 2, 0, 0); + + RedirectData google; + google.set_primary_key("http://google.com"); + google.set_last_visit_time(8); + InitializeRedirectStat(google.add_redirect_endpoints(), + "https://google.com", 3, 0, 0); + + test_url_redirect_data_.clear(); + test_url_redirect_data_.insert( + std::make_pair(facebook.primary_key(), facebook)); + test_url_redirect_data_.insert( + std::make_pair(nytimes.primary_key(), nytimes)); + test_url_redirect_data_.insert( + std::make_pair(google.primary_key(), google)); + } + + { // Host redirect data. + RedirectData bbc; + bbc.set_primary_key("bbc.com"); + bbc.set_last_visit_time(9); + InitializeRedirectStat(bbc.add_redirect_endpoints(), "www.bbc.com", 8, 4, + 1); + InitializeRedirectStat(bbc.add_redirect_endpoints(), "m.bbc.com", 5, 8, 0); + InitializeRedirectStat(bbc.add_redirect_endpoints(), "bbc.co.uk", 1, 3, 0); + + RedirectData microsoft; + microsoft.set_primary_key("microsoft.com"); + microsoft.set_last_visit_time(10); + InitializeRedirectStat(microsoft.add_redirect_endpoints(), + "www.microsoft.com", 10, 0, 0); + + test_host_redirect_data_.clear(); + test_host_redirect_data_.insert(std::make_pair(bbc.primary_key(), bbc)); + test_host_redirect_data_.insert( + std::make_pair(microsoft.primary_key(), microsoft)); + } } +// Tests that the predictor initializes correctly without any data. TEST_F(ResourcePrefetchPredictorTest, LazilyInitializeEmpty) { - // Tests that the predictor initializes correctly without any data. EXPECT_TRUE(predictor_->url_table_cache_->empty()); EXPECT_TRUE(predictor_->host_table_cache_->empty()); + EXPECT_TRUE(predictor_->url_redirect_table_cache_->empty()); + EXPECT_TRUE(predictor_->host_redirect_table_cache_->empty()); } +// Tests that the history and the db tables data are loaded correctly. TEST_F(ResourcePrefetchPredictorTest, LazilyInitializeWithData) { - // Tests that the history and the db tables data are loaded correctly. AddUrlToHistory("http://www.google.com/", 4); AddUrlToHistory("http://www.yahoo.com/", 2); EXPECT_CALL(*mock_tables_.get(), GetAllData(Pointee(ContainerEq(PrefetchDataMap())), - Pointee(ContainerEq(PrefetchDataMap())))) + Pointee(ContainerEq(PrefetchDataMap())), + Pointee(ContainerEq(RedirectDataMap())), + Pointee(ContainerEq(RedirectDataMap())))) .WillOnce(DoAll(SetArgPointee<0>(test_url_data_), - SetArgPointee<1>(test_host_data_))); + SetArgPointee<1>(test_host_data_), + SetArgPointee<2>(test_url_redirect_data_), + SetArgPointee<3>(test_host_redirect_data_))); ResetPredictor(); InitializePredictor(); @@ -402,56 +492,64 @@ EXPECT_EQ(test_url_data_, *predictor_->url_table_cache_); EXPECT_EQ(test_host_data_, *predictor_->host_table_cache_); + EXPECT_EQ(test_url_redirect_data_, *predictor_->url_redirect_table_cache_); + EXPECT_EQ(test_host_redirect_data_, *predictor_->host_redirect_table_cache_); } +// Single navigation but history count is low, so should not record. TEST_F(ResourcePrefetchPredictorTest, NavigationNotRecorded) { - // Single navigation but history count is low, so should not record. AddUrlToHistory("http://www.google.com", 1); - URLRequestSummary main_frame = CreateURLRequestSummary( - 1, 1, "http://www.google.com", "http://www.google.com", - content::RESOURCE_TYPE_MAIN_FRAME, net::MEDIUM, std::string(), false); + URLRequestSummary main_frame = + CreateURLRequestSummary(1, 1, "http://www.google.com"); predictor_->RecordURLRequest(main_frame); EXPECT_EQ(1U, predictor_->inflight_navigations_.size()); + URLRequestSummary main_frame_redirect = CreateRedirectRequestSummary( + 1, 1, "http://www.google.com", "https://www.google.com"); + predictor_->RecordURLRedirect(main_frame_redirect); + EXPECT_EQ(1U, predictor_->inflight_navigations_.size()); + main_frame = CreateURLRequestSummary(1, 1, "https://www.google.com"); + // Now add a few subresources. URLRequestSummary resource1 = CreateURLRequestSummary( - 1, 1, "http://www.google.com", "http://google.com/style1.css", + 1, 1, "https://www.google.com", "https://google.com/style1.css", content::RESOURCE_TYPE_STYLESHEET, net::MEDIUM, "text/css", false); predictor_->RecordURLResponse(resource1); URLRequestSummary resource2 = CreateURLRequestSummary( - 1, 1, "http://www.google.com", "http://google.com/script1.js", + 1, 1, "https://www.google.com", "https://google.com/script1.js", content::RESOURCE_TYPE_SCRIPT, net::MEDIUM, "text/javascript", false); predictor_->RecordURLResponse(resource2); URLRequestSummary resource3 = CreateURLRequestSummary( - 1, 1, "http://www.google.com", "http://google.com/script2.js", + 1, 1, "https://www.google.com", "https://google.com/script2.js", content::RESOURCE_TYPE_SCRIPT, net::MEDIUM, "text/javascript", false); predictor_->RecordURLResponse(resource3); PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.google.com"); host_data.resources.push_back(CreateResourceData( - "http://google.com/style1.css", content::RESOURCE_TYPE_STYLESHEET, 1, 0, + "https://google.com/style1.css", content::RESOURCE_TYPE_STYLESHEET, 1, 0, 0, 1.0, net::MEDIUM, false, false)); host_data.resources.push_back(CreateResourceData( - "http://google.com/script1.js", content::RESOURCE_TYPE_SCRIPT, 1, 0, 0, + "https://google.com/script1.js", content::RESOURCE_TYPE_SCRIPT, 1, 0, 0, 2.0, net::MEDIUM, false, false)); host_data.resources.push_back(CreateResourceData( - "http://google.com/script2.js", content::RESOURCE_TYPE_SCRIPT, 1, 0, 0, + "https://google.com/script2.js", content::RESOURCE_TYPE_SCRIPT, 1, 0, 0, 3.0, net::MEDIUM, false, false)); - EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data)); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(empty_url_data_, host_data, empty_url_redirect_data_, + empty_host_redirect_data_)); - predictor_->OnNavigationComplete(main_frame.navigation_id); + predictor_->RecordMainFrameLoadComplete(main_frame.navigation_id); profile_->BlockUntilHistoryProcessesPendingRequests(); } +// Single navigation that will be recorded. Will check for duplicate +// resources and also for number of resources saved. TEST_F(ResourcePrefetchPredictorTest, NavigationUrlNotInDB) { - // Single navigation that will be recorded. Will check for duplicate - // resources and also for number of resources saved. AddUrlToHistory("http://www.google.com", 4); - URLRequestSummary main_frame = CreateURLRequestSummary( - 1, 1, "http://www.google.com", "http://www.google.com", - content::RESOURCE_TYPE_MAIN_FRAME, net::MEDIUM, std::string(), false); + URLRequestSummary main_frame = + CreateURLRequestSummary(1, 1, "http://www.google.com"); predictor_->RecordURLRequest(main_frame); EXPECT_EQ(1U, predictor_->inflight_navigations_.size()); @@ -497,24 +595,30 @@ url_data.resources.push_back(CreateResourceData( "http://google.com/style2.css", content::RESOURCE_TYPE_STYLESHEET, 1, 0, 0, 7.0, net::MEDIUM, false, false)); - EXPECT_CALL(*mock_tables_.get(), UpdateData(url_data, empty_host_data_)); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(url_data, empty_host_data_, empty_url_redirect_data_, + empty_host_redirect_data_)); PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.google.com"); host_data.resources = url_data.resources; - EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data)); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(empty_url_data_, host_data, empty_url_redirect_data_, + empty_host_redirect_data_)); predictor_->OnNavigationComplete(main_frame.navigation_id); profile_->BlockUntilHistoryProcessesPendingRequests(); } +// Tests that navigation is recorded correctly for URL already present in +// the database cache. TEST_F(ResourcePrefetchPredictorTest, NavigationUrlInDB) { - // Tests that navigation is recorded correctly for URL already present in - // the database cache. AddUrlToHistory("http://www.google.com", 4); EXPECT_CALL(*mock_tables_.get(), GetAllData(Pointee(ContainerEq(PrefetchDataMap())), - Pointee(ContainerEq(PrefetchDataMap())))) + Pointee(ContainerEq(PrefetchDataMap())), + Pointee(ContainerEq(RedirectDataMap())), + Pointee(ContainerEq(RedirectDataMap())))) .WillOnce(DoAll(SetArgPointee<0>(test_url_data_), SetArgPointee<1>(test_host_data_))); ResetPredictor(); @@ -570,11 +674,12 @@ url_data.resources.push_back(CreateResourceData( "http://google.com/script2.js", content::RESOURCE_TYPE_SCRIPT, 1, 0, 0, 3.0, net::MEDIUM, false, false)); - EXPECT_CALL(*mock_tables_.get(), UpdateData(url_data, empty_host_data_)); - - EXPECT_CALL( - *mock_tables_.get(), - DeleteSingleDataPoint("www.facebook.com", PREFETCH_KEY_TYPE_HOST)); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(url_data, empty_host_data_, empty_url_redirect_data_, + empty_host_redirect_data_)); + EXPECT_CALL(*mock_tables_.get(), + DeleteSingleResourceDataPoint("www.facebook.com", + PREFETCH_KEY_TYPE_HOST)); PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.google.com"); host_data.resources.push_back(CreateResourceData( @@ -589,19 +694,23 @@ host_data.resources.push_back(CreateResourceData( "http://google.com/style2.css", content::RESOURCE_TYPE_STYLESHEET, 1, 0, 0, 7.0, net::MEDIUM, false, false)); - EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data)); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(empty_url_data_, host_data, empty_url_redirect_data_, + empty_host_redirect_data_)); predictor_->OnNavigationComplete(main_frame.navigation_id); profile_->BlockUntilHistoryProcessesPendingRequests(); } +// Tests that a URL is deleted before another is added if the cache is full. TEST_F(ResourcePrefetchPredictorTest, NavigationUrlNotInDBAndDBFull) { - // Tests that a URL is deleted before another is added if the cache is full. AddUrlToHistory("http://www.nike.com/", 4); EXPECT_CALL(*mock_tables_.get(), GetAllData(Pointee(ContainerEq(PrefetchDataMap())), - Pointee(ContainerEq(PrefetchDataMap())))) + Pointee(ContainerEq(PrefetchDataMap())), + Pointee(ContainerEq(RedirectDataMap())), + Pointee(ContainerEq(RedirectDataMap())))) .WillOnce(DoAll(SetArgPointee<0>(test_url_data_), SetArgPointee<1>(test_host_data_))); ResetPredictor(); @@ -624,12 +733,12 @@ content::RESOURCE_TYPE_IMAGE, net::MEDIUM, "image/png", false); predictor_->RecordURLResponse(resource2); - EXPECT_CALL( - *mock_tables_.get(), - DeleteSingleDataPoint("http://www.google.com/", PREFETCH_KEY_TYPE_URL)); - EXPECT_CALL( - *mock_tables_.get(), - DeleteSingleDataPoint("www.facebook.com", PREFETCH_KEY_TYPE_HOST)); + EXPECT_CALL(*mock_tables_.get(), + DeleteSingleResourceDataPoint("http://www.google.com/", + PREFETCH_KEY_TYPE_URL)); + EXPECT_CALL(*mock_tables_.get(), + DeleteSingleResourceDataPoint("www.facebook.com", + PREFETCH_KEY_TYPE_HOST)); PrefetchData url_data(PREFETCH_KEY_TYPE_URL, "http://www.nike.com/"); url_data.resources.push_back(CreateResourceData( @@ -638,16 +747,126 @@ url_data.resources.push_back(CreateResourceData( "http://nike.com/image2.png", content::RESOURCE_TYPE_IMAGE, 1, 0, 0, 2.0, net::MEDIUM, false, false)); - EXPECT_CALL(*mock_tables_.get(), UpdateData(url_data, empty_host_data_)); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(url_data, empty_host_data_, empty_url_redirect_data_, + empty_host_redirect_data_)); PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.nike.com"); host_data.resources = url_data.resources; - EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data)); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(empty_url_data_, host_data, empty_url_redirect_data_, + empty_host_redirect_data_)); predictor_->OnNavigationComplete(main_frame.navigation_id); profile_->BlockUntilHistoryProcessesPendingRequests(); } +TEST_F(ResourcePrefetchPredictorTest, RedirectUrlNotInDB) { + AddUrlToHistory("https://facebook.com/google", 4); + + URLRequestSummary fb1 = CreateURLRequestSummary(1, 1, "http://fb.com/google"); + predictor_->RecordURLRequest(fb1); + EXPECT_EQ(1U, predictor_->inflight_navigations_.size()); + + URLRequestSummary fb2 = CreateRedirectRequestSummary( + 1, 1, "http://fb.com/google", "http://facebook.com/google"); + predictor_->RecordURLRedirect(fb2); + URLRequestSummary fb3 = CreateRedirectRequestSummary( + 1, 1, "http://facebook.com/google", "https://facebook.com/google"); + predictor_->RecordURLRedirect(fb3); + NavigationID fb_end = CreateNavigationID(1, 1, "https://facebook.com/google"); + + // Since the navigation hasn't resources, corresponding entry + // in resource table will be deleted. + EXPECT_CALL(*mock_tables_.get(), + DeleteSingleResourceDataPoint("https://facebook.com/google", + PREFETCH_KEY_TYPE_URL)); + EXPECT_CALL(*mock_tables_.get(), DeleteSingleResourceDataPoint( + "facebook.com", PREFETCH_KEY_TYPE_HOST)); + + RedirectData url_redirect_data; + url_redirect_data.set_primary_key("http://fb.com/google"); + InitializeRedirectStat(url_redirect_data.add_redirect_endpoints(), + "https://facebook.com/google", 1, 0, 0); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(empty_url_data_, empty_host_data_, url_redirect_data, + empty_host_redirect_data_)); + + RedirectData host_redirect_data; + host_redirect_data.set_primary_key("fb.com"); + InitializeRedirectStat(host_redirect_data.add_redirect_endpoints(), + "facebook.com", 1, 0, 0); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(empty_url_data_, empty_host_data_, + empty_url_redirect_data_, host_redirect_data)); + + predictor_->RecordMainFrameLoadComplete(fb_end); + profile_->BlockUntilHistoryProcessesPendingRequests(); +} + +// Tests that redirect is recorded correctly for URL already present in +// the database cache. +TEST_F(ResourcePrefetchPredictorTest, RedirectUrlInDB) { + AddUrlToHistory("https://facebook.com/google", 4); + + EXPECT_CALL(*mock_tables_.get(), + GetAllData(Pointee(ContainerEq(PrefetchDataMap())), + Pointee(ContainerEq(PrefetchDataMap())), + Pointee(ContainerEq(RedirectDataMap())), + Pointee(ContainerEq(RedirectDataMap())))) + .WillOnce(DoAll(SetArgPointee<2>(test_url_redirect_data_), + SetArgPointee<3>(test_host_redirect_data_))); + ResetPredictor(); + InitializePredictor(); + EXPECT_EQ(3U, predictor_->url_redirect_table_cache_->size()); + EXPECT_EQ(2U, predictor_->host_redirect_table_cache_->size()); + + URLRequestSummary fb1 = CreateURLRequestSummary(1, 1, "http://fb.com/google"); + predictor_->RecordURLRequest(fb1); + EXPECT_EQ(1U, predictor_->inflight_navigations_.size()); + + URLRequestSummary fb2 = CreateRedirectRequestSummary( + 1, 1, "http://fb.com/google", "http://facebook.com/google"); + predictor_->RecordURLRedirect(fb2); + URLRequestSummary fb3 = CreateRedirectRequestSummary( + 1, 1, "http://facebook.com/google", "https://facebook.com/google"); + predictor_->RecordURLRedirect(fb3); + NavigationID fb_end = CreateNavigationID(1, 1, "https://facebook.com/google"); + + // Oldest entries in tables will be superseded and deleted. + EXPECT_CALL(*mock_tables_.get(), + DeleteSingleRedirectDataPoint("bbc.com", PREFETCH_KEY_TYPE_HOST)); + + // Since the navigation hasn't resources, corresponding entry + // in resource table will be deleted. + EXPECT_CALL(*mock_tables_.get(), + DeleteSingleResourceDataPoint("https://facebook.com/google", + PREFETCH_KEY_TYPE_URL)); + EXPECT_CALL(*mock_tables_.get(), DeleteSingleResourceDataPoint( + "facebook.com", PREFETCH_KEY_TYPE_HOST)); + + RedirectData url_redirect_data; + url_redirect_data.set_primary_key("http://fb.com/google"); + InitializeRedirectStat(url_redirect_data.add_redirect_endpoints(), + "https://facebook.com/google", 6, 1, 0); + // Existing redirect to https://facebook.com/login will be deleted because of + // too many consecutive misses. + EXPECT_CALL(*mock_tables_.get(), + UpdateData(empty_url_data_, empty_host_data_, url_redirect_data, + empty_host_redirect_data_)); + + RedirectData host_redirect_data; + host_redirect_data.set_primary_key("fb.com"); + InitializeRedirectStat(host_redirect_data.add_redirect_endpoints(), + "facebook.com", 1, 0, 0); + EXPECT_CALL(*mock_tables_.get(), + UpdateData(empty_url_data_, empty_host_data_, + empty_url_redirect_data_, host_redirect_data)); + + predictor_->RecordMainFrameLoadComplete(fb_end); + profile_->BlockUntilHistoryProcessesPendingRequests(); +} + TEST_F(ResourcePrefetchPredictorTest, DeleteUrls) { // Add some dummy entries to cache. predictor_->url_table_cache_->insert(std::make_pair( @@ -676,31 +895,61 @@ "www.apple.com", PrefetchData(PREFETCH_KEY_TYPE_HOST, "www.apple.com"))); + predictor_->url_redirect_table_cache_->insert( + std::make_pair("http://www.google.com/page1.html", + CreateRedirectData("http://www.google.com/page1.html"))); + predictor_->url_redirect_table_cache_->insert( + std::make_pair("http://www.google.com/page2.html", + CreateRedirectData("http://www.google.com/page2.html"))); + predictor_->url_redirect_table_cache_->insert(std::make_pair( + "http://www.apple.com/", CreateRedirectData("http://www.apple.com/"))); + predictor_->url_redirect_table_cache_->insert( + std::make_pair("http://nyt.com/", CreateRedirectData("http://nyt.com/"))); + + predictor_->host_redirect_table_cache_->insert( + std::make_pair("www.google.com", CreateRedirectData("www.google.com"))); + predictor_->host_redirect_table_cache_->insert( + std::make_pair("www.nike.com", CreateRedirectData("www.nike.com"))); + predictor_->host_redirect_table_cache_->insert(std::make_pair( + "www.wikipedia.org", CreateRedirectData("www.wikipedia.org"))); + history::URLRows rows; rows.push_back(history::URLRow(GURL("http://www.google.com/page2.html"))); rows.push_back(history::URLRow(GURL("http://www.apple.com"))); rows.push_back(history::URLRow(GURL("http://www.nike.com"))); - std::vector<std::string> urls_to_delete, hosts_to_delete; + std::vector<std::string> urls_to_delete, hosts_to_delete, + url_redirects_to_delete, host_redirects_to_delete; urls_to_delete.push_back("http://www.google.com/page2.html"); urls_to_delete.push_back("http://www.apple.com/"); urls_to_delete.push_back("http://www.nike.com/"); hosts_to_delete.push_back("www.google.com"); hosts_to_delete.push_back("www.apple.com"); + url_redirects_to_delete.push_back("http://www.google.com/page2.html"); + url_redirects_to_delete.push_back("http://www.apple.com/"); + host_redirects_to_delete.push_back("www.google.com"); + host_redirects_to_delete.push_back("www.nike.com"); - EXPECT_CALL( - *mock_tables_.get(), - DeleteData(ContainerEq(urls_to_delete), ContainerEq(hosts_to_delete))); + EXPECT_CALL(*mock_tables_.get(), + DeleteResourceData(ContainerEq(urls_to_delete), + ContainerEq(hosts_to_delete))); + EXPECT_CALL(*mock_tables_.get(), + DeleteRedirectData(ContainerEq(url_redirects_to_delete), + ContainerEq(host_redirects_to_delete))); predictor_->DeleteUrls(rows); EXPECT_EQ(2U, predictor_->url_table_cache_->size()); EXPECT_EQ(1U, predictor_->host_table_cache_->size()); + EXPECT_EQ(2U, predictor_->url_redirect_table_cache_->size()); + EXPECT_EQ(1U, predictor_->host_redirect_table_cache_->size()); EXPECT_CALL(*mock_tables_.get(), DeleteAllData()); predictor_->DeleteAllUrls(); EXPECT_TRUE(predictor_->url_table_cache_->empty()); EXPECT_TRUE(predictor_->host_table_cache_->empty()); + EXPECT_TRUE(predictor_->url_redirect_table_cache_->empty()); + EXPECT_TRUE(predictor_->host_redirect_table_cache_->empty()); } TEST_F(ResourcePrefetchPredictorTest, OnMainFrameRequest) { @@ -721,7 +970,7 @@ predictor_->OnMainFrameRequest(summary3); EXPECT_EQ(3U, predictor_->inflight_navigations_.size()); - // Insert anther with same navigation id. It should replace. + // Insert another with same navigation id. It should replace. URLRequestSummary summary4 = CreateURLRequestSummary( 1, 1, "http://www.nike.com", "http://www.nike.com", content::RESOURCE_TYPE_MAIN_FRAME, net::MEDIUM, std::string(), false); @@ -753,30 +1002,78 @@ } TEST_F(ResourcePrefetchPredictorTest, OnMainFrameRedirect) { - URLRequestSummary summary1 = CreateURLRequestSummary( - 1, 1, "http://www.google.com", "http://www.google.com", - content::RESOURCE_TYPE_MAIN_FRAME, net::MEDIUM, std::string(), false); - URLRequestSummary summary2 = CreateURLRequestSummary( - 1, 2, "http://www.google.com", "http://www.google.com", - content::RESOURCE_TYPE_MAIN_FRAME, net::MEDIUM, std::string(), false); - URLRequestSummary summary3 = CreateURLRequestSummary( - 2, 1, "http://www.yahoo.com", "http://www.yahoo.com", - content::RESOURCE_TYPE_MAIN_FRAME, net::MEDIUM, std::string(), false); + URLRequestSummary yahoo = CreateURLRequestSummary(1, 1, "http://yahoo.com"); - predictor_->OnMainFrameRedirect(summary1); + URLRequestSummary bbc1 = CreateURLRequestSummary(2, 2, "http://bbc.com"); + URLRequestSummary bbc2 = CreateRedirectRequestSummary(2, 2, "http://bbc.com", + "https://www.bbc.com"); + NavigationID bbc_end = CreateNavigationID(2, 2, "https://www.bbc.com"); + + URLRequestSummary youtube1 = + CreateURLRequestSummary(1, 2, "http://youtube.com"); + URLRequestSummary youtube2 = CreateRedirectRequestSummary( + 1, 2, "http://youtube.com", "https://youtube.com"); + NavigationID youtube_end = CreateNavigationID(1, 2, "https://youtube.com"); + + URLRequestSummary nyt1 = CreateURLRequestSummary(2, 1, "http://nyt.com"); + URLRequestSummary nyt2 = CreateRedirectRequestSummary(2, 1, "http://nyt.com", + "http://nytimes.com"); + URLRequestSummary nyt3 = CreateRedirectRequestSummary( + 2, 1, "http://nytimes.com", "http://m.nytimes.com"); + NavigationID nyt_end = CreateNavigationID(2, 1, "http://m.nytimes.com"); + + URLRequestSummary fb1 = CreateURLRequestSummary(1, 3, "http://fb.com"); + URLRequestSummary fb2 = CreateRedirectRequestSummary(1, 3, "http://fb.com", + "http://facebook.com"); + URLRequestSummary fb3 = CreateRedirectRequestSummary( + 1, 3, "http://facebook.com", "https://facebook.com"); + URLRequestSummary fb4 = CreateRedirectRequestSummary( + 1, 3, "https://facebook.com", + "https://m.facebook.com/?refsrc=https%3A%2F%2Fwww.facebook.com%2F&_rdr"); + NavigationID fb_end = CreateNavigationID( + 1, 3, + "https://m.facebook.com/?refsrc=https%3A%2F%2Fwww.facebook.com%2F&_rdr"); + + // Redirect with empty redirect_url will be deleted. + predictor_->OnMainFrameRequest(yahoo); + EXPECT_EQ(1U, predictor_->inflight_navigations_.size()); + predictor_->OnMainFrameRedirect(yahoo); EXPECT_TRUE(predictor_->inflight_navigations_.empty()); - predictor_->OnMainFrameRequest(summary1); + // Redirect without previous request works fine. + // predictor_->OnMainFrameRequest(bbc1) missing. + predictor_->OnMainFrameRedirect(bbc2); EXPECT_EQ(1U, predictor_->inflight_navigations_.size()); - predictor_->OnMainFrameRequest(summary2); - EXPECT_EQ(2U, predictor_->inflight_navigations_.size()); + EXPECT_EQ(bbc1.navigation_id.main_frame_url, + predictor_->inflight_navigations_[bbc_end]->initial_url); - predictor_->OnMainFrameRedirect(summary3); + // http://youtube.com -> https://youtube.com. + predictor_->OnMainFrameRequest(youtube1); EXPECT_EQ(2U, predictor_->inflight_navigations_.size()); - predictor_->OnMainFrameRedirect(summary1); - EXPECT_EQ(1U, predictor_->inflight_navigations_.size()); - predictor_->OnMainFrameRedirect(summary2); - EXPECT_TRUE(predictor_->inflight_navigations_.empty()); + predictor_->OnMainFrameRedirect(youtube2); + EXPECT_EQ(2U, predictor_->inflight_navigations_.size()); + EXPECT_EQ(youtube1.navigation_id.main_frame_url, + predictor_->inflight_navigations_[youtube_end]->initial_url); + + // http://nyt.com -> http://nytimes.com -> http://m.nytimes.com. + predictor_->OnMainFrameRequest(nyt1); + EXPECT_EQ(3U, predictor_->inflight_navigations_.size()); + predictor_->OnMainFrameRedirect(nyt2); + predictor_->OnMainFrameRedirect(nyt3); + EXPECT_EQ(3U, predictor_->inflight_navigations_.size()); + EXPECT_EQ(nyt1.navigation_id.main_frame_url, + predictor_->inflight_navigations_[nyt_end]->initial_url); + + // http://fb.com -> http://facebook.com -> https://facebook.com -> + // https://m.facebook.com/?refsrc=https%3A%2F%2Fwww.facebook.com%2F&_rdr. + predictor_->OnMainFrameRequest(fb1); + EXPECT_EQ(4U, predictor_->inflight_navigations_.size()); + predictor_->OnMainFrameRedirect(fb2); + predictor_->OnMainFrameRedirect(fb3); + predictor_->OnMainFrameRedirect(fb4); + EXPECT_EQ(4U, predictor_->inflight_navigations_.size()); + EXPECT_EQ(fb1.navigation_id.main_frame_url, + predictor_->inflight_navigations_[fb_end]->initial_url); } TEST_F(ResourcePrefetchPredictorTest, OnSubresourceResponse) { @@ -806,17 +1103,17 @@ predictor_->OnSubresourceResponse(resource3); EXPECT_EQ(1U, predictor_->inflight_navigations_.size()); - EXPECT_EQ(3U, - predictor_->inflight_navigations_[main_frame1.navigation_id]->size()); + EXPECT_EQ(3U, predictor_->inflight_navigations_[main_frame1.navigation_id] + ->subresource_requests.size()); EXPECT_TRUE(URLRequestSummaryAreEqual( - resource1, - predictor_->inflight_navigations_[main_frame1.navigation_id]->at(0))); + resource1, predictor_->inflight_navigations_[main_frame1.navigation_id] + ->subresource_requests[0])); EXPECT_TRUE(URLRequestSummaryAreEqual( - resource2, - predictor_->inflight_navigations_[main_frame1.navigation_id]->at(1))); + resource2, predictor_->inflight_navigations_[main_frame1.navigation_id] + ->subresource_requests[1])); EXPECT_TRUE(URLRequestSummaryAreEqual( - resource3, - predictor_->inflight_navigations_[main_frame1.navigation_id]->at(2))); + resource3, predictor_->inflight_navigations_[main_frame1.navigation_id] + ->subresource_requests[2])); } TEST_F(ResourcePrefetchPredictorTest, HandledResourceTypes) { @@ -911,7 +1208,7 @@ response_info.was_cached = true; url_request_job_factory_.set_response_info(response_info); - // Protocol + // Protocol. std::unique_ptr<net::URLRequest> http_image_request = CreateURLRequest(GURL("http://www.google.com/cat.png"), net::MEDIUM, content::RESOURCE_TYPE_IMAGE, 1, 1, true); @@ -930,7 +1227,7 @@ EXPECT_FALSE(ResourcePrefetchPredictor::ShouldRecordResponse( file_image_request.get())); - // ResourceType + // ResourceType. std::unique_ptr<net::URLRequest> sub_frame_request = CreateURLRequest(GURL("http://www.google.com/frame.html"), net::MEDIUM, content::RESOURCE_TYPE_SUB_FRAME, 1, 1, true); @@ -973,7 +1270,7 @@ EXPECT_FALSE(ResourcePrefetchPredictor::ShouldRecordResponse( prefetch_unknown_font_request.get())); - // Not main frame + // Not main frame. std::unique_ptr<net::URLRequest> font_request_sub_frame = CreateURLRequest( GURL("http://www.google.com/comic-sans-ms.woff"), net::MEDIUM, content::RESOURCE_TYPE_FONT_RESOURCE, 1, 1, false);
diff --git a/chrome/browser/resources/chromeos/keyboard_overlay_data.js b/chrome/browser/resources/chromeos/keyboard_overlay_data.js index 275cb4c..3501bd3 100644 --- a/chrome/browser/resources/chromeos/keyboard_overlay_data.js +++ b/chrome/browser/resources/chromeos/keyboard_overlay_data.js
@@ -15870,6 +15870,7 @@ 'full screen<>SEARCH': 'keyboardOverlayF4', 'g<>CTRL': 'keyboardOverlayFindTextAgain', 'g<>CTRL<>SHIFT': 'keyboardOverlayFindPreviousText', + 'h<>ALT<>CTRL': 'keyboardOverlayToggleHighContrastMode', 'h<>CTRL': 'keyboardOverlayHistory', 'i<>ALT<>SHIFT': 'keyboardOverlayReportIssue', 'i<>CTRL<>SHIFT': 'keyboardOverlayDeveloperTools',
diff --git a/chrome/browser/resources/md_downloads/crisper.js b/chrome/browser/resources/md_downloads/crisper.js index 34ccde3e..288a392 100644 --- a/chrome/browser/resources/md_downloads/crisper.js +++ b/chrome/browser/resources/md_downloads/crisper.js
@@ -1,7030 +1,72 @@ // Copyright (c) 2013 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. -function assert(condition, opt_message) { - if (!condition) { - var message = 'Assertion failed'; - if (opt_message) message = message + ': ' + opt_message; - var error = new Error(message); - var global = function() { - return this; - }(); - if (global.traceAssertionsForTesting) console.warn(error.stack); - throw error; - } - return condition; -} - -function assertNotReached(opt_message) { - assert(false, opt_message || 'Unreachable code hit'); -} - -function assertInstanceof(value, type, opt_message) { - if (!(value instanceof type)) { - assertNotReached(opt_message || 'Value ' + value + ' is not a[n] ' + (type.name || typeof type)); - } - return value; -} - +function assert(condition,opt_message){if(!condition){var message="Assertion failed";if(opt_message)message=message+": "+opt_message;var error=new Error(message);var global=function(){return this}();if(global.traceAssertionsForTesting)console.warn(error.stack);throw error}return condition}function assertNotReached(opt_message){assert(false,opt_message||"Unreachable code hit")}function assertInstanceof(value,type,opt_message){if(!(value instanceof type)){assertNotReached(opt_message||"Value "+value+" is not a[n] "+(type.name||typeof type))}return value} // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -function PromiseResolver() { - this.resolve_; - this.reject_; - this.promise_ = new Promise(function(resolve, reject) { - this.resolve_ = resolve; - this.reject_ = reject; - }.bind(this)); -} - -PromiseResolver.prototype = { - get promise() { - return this.promise_; - }, - set promise(p) { - assertNotReached(); - }, - get resolve() { - return this.resolve_; - }, - set resolve(r) { - assertNotReached(); - }, - get reject() { - return this.reject_; - }, - set reject(s) { - assertNotReached(); - } -}; - +function PromiseResolver(){this.resolve_;this.reject_;this.promise_=new Promise(function(resolve,reject){this.resolve_=resolve;this.reject_=reject}.bind(this))}PromiseResolver.prototype={get promise(){return this.promise_},set promise(p){assertNotReached()},get resolve(){return this.resolve_},set resolve(r){assertNotReached()},get reject(){return this.reject_},set reject(s){assertNotReached()}}; // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var global = this; - -var WebUIListener; - -var cr = cr || function() { - 'use strict'; - function exportPath(name, opt_object, opt_objectToExportTo) { - var parts = name.split('.'); - var cur = opt_objectToExportTo || global; - for (var part; parts.length && (part = parts.shift()); ) { - if (!parts.length && opt_object !== undefined) { - cur[part] = opt_object; - } else if (part in cur) { - cur = cur[part]; - } else { - cur = cur[part] = {}; - } - } - return cur; - } - function dispatchPropertyChange(target, propertyName, newValue, oldValue) { - var e = new Event(propertyName + 'Change'); - e.propertyName = propertyName; - e.newValue = newValue; - e.oldValue = oldValue; - target.dispatchEvent(e); - } - function getAttributeName(jsName) { - return jsName.replace(/([A-Z])/g, '-$1').toLowerCase(); - } - var PropertyKind = { - JS: 'js', - ATTR: 'attr', - BOOL_ATTR: 'boolAttr' - }; - function getGetter(name, kind) { - switch (kind) { - case PropertyKind.JS: - var privateName = name + '_'; - return function() { - return this[privateName]; - }; - - case PropertyKind.ATTR: - var attributeName = getAttributeName(name); - return function() { - return this.getAttribute(attributeName); - }; - - case PropertyKind.BOOL_ATTR: - var attributeName = getAttributeName(name); - return function() { - return this.hasAttribute(attributeName); - }; - } - throw 'not reached'; - } - function getSetter(name, kind, opt_setHook) { - switch (kind) { - case PropertyKind.JS: - var privateName = name + '_'; - return function(value) { - var oldValue = this[name]; - if (value !== oldValue) { - this[privateName] = value; - if (opt_setHook) opt_setHook.call(this, value, oldValue); - dispatchPropertyChange(this, name, value, oldValue); - } - }; - - case PropertyKind.ATTR: - var attributeName = getAttributeName(name); - return function(value) { - var oldValue = this[name]; - if (value !== oldValue) { - if (value == undefined) this.removeAttribute(attributeName); else this.setAttribute(attributeName, value); - if (opt_setHook) opt_setHook.call(this, value, oldValue); - dispatchPropertyChange(this, name, value, oldValue); - } - }; - - case PropertyKind.BOOL_ATTR: - var attributeName = getAttributeName(name); - return function(value) { - var oldValue = this[name]; - if (value !== oldValue) { - if (value) this.setAttribute(attributeName, name); else this.removeAttribute(attributeName); - if (opt_setHook) opt_setHook.call(this, value, oldValue); - dispatchPropertyChange(this, name, value, oldValue); - } - }; - } - throw 'not reached'; - } - function defineProperty(obj, name, opt_kind, opt_setHook) { - if (typeof obj == 'function') obj = obj.prototype; - var kind = opt_kind || PropertyKind.JS; - if (!obj.__lookupGetter__(name)) obj.__defineGetter__(name, getGetter(name, kind)); - if (!obj.__lookupSetter__(name)) obj.__defineSetter__(name, getSetter(name, kind, opt_setHook)); - } - var uidCounter = 1; - function createUid() { - return uidCounter++; - } - function getUid(item) { - if (item.hasOwnProperty('uid')) return item.uid; - return item.uid = createUid(); - } - function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) { - var e = new Event(type, { - bubbles: opt_bubbles, - cancelable: opt_cancelable === undefined || opt_cancelable - }); - return target.dispatchEvent(e); - } - function define(name, fun) { - var obj = exportPath(name); - var exports = fun(); - for (var propertyName in exports) { - var propertyDescriptor = Object.getOwnPropertyDescriptor(exports, propertyName); - if (propertyDescriptor) Object.defineProperty(obj, propertyName, propertyDescriptor); - } - } - function addSingletonGetter(ctor) { - ctor.getInstance = function() { - return ctor.instance_ || (ctor.instance_ = new ctor()); - }; - } - function makePublic(ctor, methods, opt_target) { - methods.forEach(function(method) { - ctor[method] = function() { - var target = opt_target ? document.getElementById(opt_target) : ctor.getInstance(); - return target[method + '_'].apply(target, arguments); - }; - }); - } - var chromeSendResolverMap = {}; - function webUIResponse(id, isSuccess, response) { - var resolver = chromeSendResolverMap[id]; - delete chromeSendResolverMap[id]; - if (isSuccess) resolver.resolve(response); else resolver.reject(response); - } - function sendWithPromise(methodName, var_args) { - var args = Array.prototype.slice.call(arguments, 1); - var promiseResolver = new PromiseResolver(); - var id = methodName + '_' + createUid(); - chromeSendResolverMap[id] = promiseResolver; - chrome.send(methodName, [ id ].concat(args)); - return promiseResolver.promise; - } - var webUIListenerMap = {}; - function webUIListenerCallback(event, var_args) { - var eventListenersMap = webUIListenerMap[event]; - if (!eventListenersMap) { - return; - } - var args = Array.prototype.slice.call(arguments, 1); - for (var listenerId in eventListenersMap) { - eventListenersMap[listenerId].apply(null, args); - } - } - function addWebUIListener(eventName, callback) { - webUIListenerMap[eventName] = webUIListenerMap[eventName] || {}; - var uid = createUid(); - webUIListenerMap[eventName][uid] = callback; - return { - eventName: eventName, - uid: uid - }; - } - function removeWebUIListener(listener) { - var listenerExists = webUIListenerMap[listener.eventName] && webUIListenerMap[listener.eventName][listener.uid]; - if (listenerExists) { - delete webUIListenerMap[listener.eventName][listener.uid]; - return true; - } - return false; - } - return { - addSingletonGetter: addSingletonGetter, - createUid: createUid, - define: define, - defineProperty: defineProperty, - dispatchPropertyChange: dispatchPropertyChange, - dispatchSimpleEvent: dispatchSimpleEvent, - exportPath: exportPath, - getUid: getUid, - makePublic: makePublic, - PropertyKind: PropertyKind, - addWebUIListener: addWebUIListener, - removeWebUIListener: removeWebUIListener, - sendWithPromise: sendWithPromise, - webUIListenerCallback: webUIListenerCallback, - webUIResponse: webUIResponse, - get doc() { - return document; - }, - get isMac() { - return /Mac/.test(navigator.platform); - }, - get isWindows() { - return /Win/.test(navigator.platform); - }, - get isChromeOS() { - return /CrOS/.test(navigator.userAgent); - }, - get isLinux() { - return /Linux/.test(navigator.userAgent); - }, - get isAndroid() { - return /Android/.test(navigator.userAgent); - }, - get isIOS() { - return /iPad|iPhone|iPod/.test(navigator.platform); - } - }; -}(); - +var global=this;var WebUIListener;var cr=cr||function(){"use strict";function exportPath(name,opt_object,opt_objectToExportTo){var parts=name.split(".");var cur=opt_objectToExportTo||global;for(var part;parts.length&&(part=parts.shift());){if(!parts.length&&opt_object!==undefined){cur[part]=opt_object}else if(part in cur){cur=cur[part]}else{cur=cur[part]={}}}return cur}function dispatchPropertyChange(target,propertyName,newValue,oldValue){var e=new Event(propertyName+"Change");e.propertyName=propertyName;e.newValue=newValue;e.oldValue=oldValue;target.dispatchEvent(e)}function getAttributeName(jsName){return jsName.replace(/([A-Z])/g,"-$1").toLowerCase()}var PropertyKind={JS:"js",ATTR:"attr",BOOL_ATTR:"boolAttr"};function getGetter(name,kind){switch(kind){case PropertyKind.JS:var privateName=name+"_";return function(){return this[privateName]};case PropertyKind.ATTR:var attributeName=getAttributeName(name);return function(){return this.getAttribute(attributeName)};case PropertyKind.BOOL_ATTR:var attributeName=getAttributeName(name);return function(){return this.hasAttribute(attributeName)}}throw"not reached"}function getSetter(name,kind,opt_setHook){switch(kind){case PropertyKind.JS:var privateName=name+"_";return function(value){var oldValue=this[name];if(value!==oldValue){this[privateName]=value;if(opt_setHook)opt_setHook.call(this,value,oldValue);dispatchPropertyChange(this,name,value,oldValue)}};case PropertyKind.ATTR:var attributeName=getAttributeName(name);return function(value){var oldValue=this[name];if(value!==oldValue){if(value==undefined)this.removeAttribute(attributeName);else this.setAttribute(attributeName,value);if(opt_setHook)opt_setHook.call(this,value,oldValue);dispatchPropertyChange(this,name,value,oldValue)}};case PropertyKind.BOOL_ATTR:var attributeName=getAttributeName(name);return function(value){var oldValue=this[name];if(value!==oldValue){if(value)this.setAttribute(attributeName,name);else this.removeAttribute(attributeName);if(opt_setHook)opt_setHook.call(this,value,oldValue);dispatchPropertyChange(this,name,value,oldValue)}}}throw"not reached"}function defineProperty(obj,name,opt_kind,opt_setHook){if(typeof obj=="function")obj=obj.prototype;var kind=opt_kind||PropertyKind.JS;if(!obj.__lookupGetter__(name))obj.__defineGetter__(name,getGetter(name,kind));if(!obj.__lookupSetter__(name))obj.__defineSetter__(name,getSetter(name,kind,opt_setHook))}var uidCounter=1;function createUid(){return uidCounter++}function getUid(item){if(item.hasOwnProperty("uid"))return item.uid;return item.uid=createUid()}function dispatchSimpleEvent(target,type,opt_bubbles,opt_cancelable){var e=new Event(type,{bubbles:opt_bubbles,cancelable:opt_cancelable===undefined||opt_cancelable});return target.dispatchEvent(e)}function define(name,fun){var obj=exportPath(name);var exports=fun();for(var propertyName in exports){var propertyDescriptor=Object.getOwnPropertyDescriptor(exports,propertyName);if(propertyDescriptor)Object.defineProperty(obj,propertyName,propertyDescriptor)}}function addSingletonGetter(ctor){ctor.getInstance=function(){return ctor.instance_||(ctor.instance_=new ctor)}}function makePublic(ctor,methods,opt_target){methods.forEach(function(method){ctor[method]=function(){var target=opt_target?document.getElementById(opt_target):ctor.getInstance();return target[method+"_"].apply(target,arguments)}})}var chromeSendResolverMap={};function webUIResponse(id,isSuccess,response){var resolver=chromeSendResolverMap[id];delete chromeSendResolverMap[id];if(isSuccess)resolver.resolve(response);else resolver.reject(response)}function sendWithPromise(methodName,var_args){var args=Array.prototype.slice.call(arguments,1);var promiseResolver=new PromiseResolver;var id=methodName+"_"+createUid();chromeSendResolverMap[id]=promiseResolver;chrome.send(methodName,[id].concat(args));return promiseResolver.promise}var webUIListenerMap={};function webUIListenerCallback(event,var_args){var eventListenersMap=webUIListenerMap[event];if(!eventListenersMap){return}var args=Array.prototype.slice.call(arguments,1);for(var listenerId in eventListenersMap){eventListenersMap[listenerId].apply(null,args)}}function addWebUIListener(eventName,callback){webUIListenerMap[eventName]=webUIListenerMap[eventName]||{};var uid=createUid();webUIListenerMap[eventName][uid]=callback;return{eventName:eventName,uid:uid}}function removeWebUIListener(listener){var listenerExists=webUIListenerMap[listener.eventName]&&webUIListenerMap[listener.eventName][listener.uid];if(listenerExists){delete webUIListenerMap[listener.eventName][listener.uid];return true}return false}return{addSingletonGetter:addSingletonGetter,createUid:createUid,define:define,defineProperty:defineProperty,dispatchPropertyChange:dispatchPropertyChange,dispatchSimpleEvent:dispatchSimpleEvent,exportPath:exportPath,getUid:getUid,makePublic:makePublic,PropertyKind:PropertyKind,addWebUIListener:addWebUIListener,removeWebUIListener:removeWebUIListener,sendWithPromise:sendWithPromise,webUIListenerCallback:webUIListenerCallback,webUIResponse:webUIResponse,get doc(){return document},get isMac(){return/Mac/.test(navigator.platform)},get isWindows(){return/Win/.test(navigator.platform)},get isChromeOS(){return/CrOS/.test(navigator.userAgent)},get isLinux(){return/Linux/.test(navigator.userAgent)},get isAndroid(){return/Android/.test(navigator.userAgent)},get isIOS(){return/iPad|iPhone|iPod/.test(navigator.platform)}}}(); // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -cr.define('cr.ui', function() { - function decorate(source, constr) { - var elements; - if (typeof source == 'string') elements = cr.doc.querySelectorAll(source); else elements = [ source ]; - for (var i = 0, el; el = elements[i]; i++) { - if (!(el instanceof constr)) constr.decorate(el); - } - } - function createElementHelper(tagName, opt_bag) { - var doc; - if (opt_bag && opt_bag.ownerDocument) doc = opt_bag.ownerDocument; else doc = cr.doc; - return doc.createElement(tagName); - } - function define(tagNameOrFunction) { - var createFunction, tagName; - if (typeof tagNameOrFunction == 'function') { - createFunction = tagNameOrFunction; - tagName = ''; - } else { - createFunction = createElementHelper; - tagName = tagNameOrFunction; - } - function f(opt_propertyBag) { - var el = createFunction(tagName, opt_propertyBag); - f.decorate(el); - for (var propertyName in opt_propertyBag) { - el[propertyName] = opt_propertyBag[propertyName]; - } - return el; - } - f.decorate = function(el) { - el.__proto__ = f.prototype; - el.decorate(); - }; - return f; - } - function limitInputWidth(el, parentEl, min, opt_scale) { - el.style.width = '10px'; - var doc = el.ownerDocument; - var win = doc.defaultView; - var computedStyle = win.getComputedStyle(el); - var parentComputedStyle = win.getComputedStyle(parentEl); - var rtl = computedStyle.direction == 'rtl'; - var inputRect = el.getBoundingClientRect(); - var parentRect = parentEl.getBoundingClientRect(); - var startPos = rtl ? parentRect.right - inputRect.right : inputRect.left - parentRect.left; - var inner = parseInt(computedStyle.borderLeftWidth, 10) + parseInt(computedStyle.paddingLeft, 10) + parseInt(computedStyle.paddingRight, 10) + parseInt(computedStyle.borderRightWidth, 10); - var parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) : parseInt(parentComputedStyle.paddingRight, 10); - var max = parentEl.clientWidth - startPos - inner - parentPadding; - if (opt_scale) max *= opt_scale; - function limit() { - if (el.scrollWidth > max) { - el.style.width = max + 'px'; - } else { - el.style.width = 0; - var sw = el.scrollWidth; - if (sw < min) { - el.style.width = min + 'px'; - } else { - el.style.width = sw + 'px'; - } - } - } - el.addEventListener('input', limit); - limit(); - } - function toCssPx(pixels) { - if (!window.isFinite(pixels)) console.error('Pixel value is not a number: ' + pixels); - return Math.round(pixels) + 'px'; - } - function swallowDoubleClick(e) { - var doc = e.target.ownerDocument; - var counter = Math.min(1, e.detail); - function swallow(e) { - e.stopPropagation(); - e.preventDefault(); - } - function onclick(e) { - if (e.detail > counter) { - counter = e.detail; - swallow(e); - } else { - doc.removeEventListener('dblclick', swallow, true); - doc.removeEventListener('click', onclick, true); - } - } - setTimeout(function() { - doc.addEventListener('click', onclick, true); - doc.addEventListener('dblclick', swallow, true); - }, 0); - } - return { - decorate: decorate, - define: define, - limitInputWidth: limitInputWidth, - toCssPx: toCssPx, - swallowDoubleClick: swallowDoubleClick - }; -}); - +cr.define("cr.ui",function(){function decorate(source,constr){var elements;if(typeof source=="string")elements=cr.doc.querySelectorAll(source);else elements=[source];for(var i=0,el;el=elements[i];i++){if(!(el instanceof constr))constr.decorate(el)}}function createElementHelper(tagName,opt_bag){var doc;if(opt_bag&&opt_bag.ownerDocument)doc=opt_bag.ownerDocument;else doc=cr.doc;return doc.createElement(tagName)}function define(tagNameOrFunction){var createFunction,tagName;if(typeof tagNameOrFunction=="function"){createFunction=tagNameOrFunction;tagName=""}else{createFunction=createElementHelper;tagName=tagNameOrFunction}function f(opt_propertyBag){var el=createFunction(tagName,opt_propertyBag);f.decorate(el);for(var propertyName in opt_propertyBag){el[propertyName]=opt_propertyBag[propertyName]}return el}f.decorate=function(el){el.__proto__=f.prototype;el.decorate()};return f}function limitInputWidth(el,parentEl,min,opt_scale){el.style.width="10px";var doc=el.ownerDocument;var win=doc.defaultView;var computedStyle=win.getComputedStyle(el);var parentComputedStyle=win.getComputedStyle(parentEl);var rtl=computedStyle.direction=="rtl";var inputRect=el.getBoundingClientRect();var parentRect=parentEl.getBoundingClientRect();var startPos=rtl?parentRect.right-inputRect.right:inputRect.left-parentRect.left;var inner=parseInt(computedStyle.borderLeftWidth,10)+parseInt(computedStyle.paddingLeft,10)+parseInt(computedStyle.paddingRight,10)+parseInt(computedStyle.borderRightWidth,10);var parentPadding=rtl?parseInt(parentComputedStyle.paddingLeft,10):parseInt(parentComputedStyle.paddingRight,10);var max=parentEl.clientWidth-startPos-inner-parentPadding;if(opt_scale)max*=opt_scale;function limit(){if(el.scrollWidth>max){el.style.width=max+"px"}else{el.style.width=0;var sw=el.scrollWidth;if(sw<min){el.style.width=min+"px"}else{el.style.width=sw+"px"}}}el.addEventListener("input",limit);limit()}function toCssPx(pixels){if(!window.isFinite(pixels))console.error("Pixel value is not a number: "+pixels);return Math.round(pixels)+"px"}function swallowDoubleClick(e){var doc=e.target.ownerDocument;var counter=Math.min(1,e.detail);function swallow(e){e.stopPropagation();e.preventDefault()}function onclick(e){if(e.detail>counter){counter=e.detail;swallow(e)}else{doc.removeEventListener("dblclick",swallow,true);doc.removeEventListener("click",onclick,true)}}setTimeout(function(){doc.addEventListener("click",onclick,true);doc.addEventListener("dblclick",swallow,true)},0)}return{decorate:decorate,define:define,limitInputWidth:limitInputWidth,toCssPx:toCssPx,swallowDoubleClick:swallowDoubleClick}}); // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -cr.define('cr.ui', function() { - function KeyboardShortcut(shortcut) { - var mods = {}; - var ident = ''; - shortcut.split('|').forEach(function(part) { - var partLc = part.toLowerCase(); - switch (partLc) { - case 'alt': - case 'ctrl': - case 'meta': - case 'shift': - mods[partLc + 'Key'] = true; - break; - - default: - if (ident) throw Error('Invalid shortcut'); - ident = part; - } - }); - this.ident_ = ident; - this.mods_ = mods; - } - KeyboardShortcut.prototype = { - matchesEvent: function(e) { - if (e.key == this.ident_) { - var mods = this.mods_; - return [ 'altKey', 'ctrlKey', 'metaKey', 'shiftKey' ].every(function(k) { - return e[k] == !!mods[k]; - }); - } - return false; - } - }; - var Command = cr.ui.define('command'); - Command.prototype = { - __proto__: HTMLElement.prototype, - decorate: function() { - CommandManager.init(assert(this.ownerDocument)); - if (this.hasAttribute('shortcut')) this.shortcut = this.getAttribute('shortcut'); - }, - execute: function(opt_element) { - if (this.disabled) return; - var doc = this.ownerDocument; - if (doc.activeElement) { - var e = new Event('command', { - bubbles: true - }); - e.command = this; - (opt_element || doc.activeElement).dispatchEvent(e); - } - }, - canExecuteChange: function(opt_node) { - dispatchCanExecuteEvent(this, opt_node || this.ownerDocument.activeElement); - }, - shortcut_: '', - get shortcut() { - return this.shortcut_; - }, - set shortcut(shortcut) { - var oldShortcut = this.shortcut_; - if (shortcut !== oldShortcut) { - this.keyboardShortcuts_ = shortcut.split(/\s+/).map(function(shortcut) { - return new KeyboardShortcut(shortcut); - }); - this.shortcut_ = shortcut; - cr.dispatchPropertyChange(this, 'shortcut', this.shortcut_, oldShortcut); - } - }, - matchesEvent: function(e) { - if (!this.keyboardShortcuts_) return false; - return this.keyboardShortcuts_.some(function(keyboardShortcut) { - return keyboardShortcut.matchesEvent(e); - }); - } - }; - cr.defineProperty(Command, 'label', cr.PropertyKind.ATTR); - cr.defineProperty(Command, 'disabled', cr.PropertyKind.BOOL_ATTR); - cr.defineProperty(Command, 'hidden', cr.PropertyKind.BOOL_ATTR); - cr.defineProperty(Command, 'checked', cr.PropertyKind.BOOL_ATTR); - cr.defineProperty(Command, 'hideShortcutText', cr.PropertyKind.BOOL_ATTR); - function dispatchCanExecuteEvent(command, target) { - var e = new CanExecuteEvent(command); - target.dispatchEvent(e); - command.disabled = !e.canExecute; - } - var commandManagers = {}; - function CommandManager(doc) { - doc.addEventListener('focus', this.handleFocus_.bind(this), true); - doc.addEventListener('keydown', this.handleKeyDown_.bind(this), false); - } - CommandManager.init = function(doc) { - var uid = cr.getUid(doc); - if (!(uid in commandManagers)) { - commandManagers[uid] = new CommandManager(doc); - } - }; - CommandManager.prototype = { - handleFocus_: function(e) { - var target = e.target; - if (target.menu || target.command) return; - var commands = Array.prototype.slice.call(target.ownerDocument.querySelectorAll('command')); - commands.forEach(function(command) { - dispatchCanExecuteEvent(command, target); - }); - }, - handleKeyDown_: function(e) { - var target = e.target; - var commands = Array.prototype.slice.call(target.ownerDocument.querySelectorAll('command')); - for (var i = 0, command; command = commands[i]; i++) { - if (command.matchesEvent(e)) { - command.canExecuteChange(); - if (!command.disabled) { - e.preventDefault(); - e.stopPropagation(); - command.execute(); - return; - } - } - } - } - }; - function CanExecuteEvent(command) { - var e = new Event('canExecute', { - bubbles: true, - cancelable: true - }); - e.__proto__ = CanExecuteEvent.prototype; - e.command = command; - return e; - } - CanExecuteEvent.prototype = { - __proto__: Event.prototype, - command: null, - canExecute_: false, - get canExecute() { - return this.canExecute_; - }, - set canExecute(canExecute) { - this.canExecute_ = !!canExecute; - this.stopPropagation(); - this.preventDefault(); - } - }; - return { - Command: Command, - CanExecuteEvent: CanExecuteEvent - }; -}); - +cr.define("cr.ui",function(){function KeyboardShortcut(shortcut){var mods={};var ident="";shortcut.split("|").forEach(function(part){var partLc=part.toLowerCase();switch(partLc){case"alt":case"ctrl":case"meta":case"shift":mods[partLc+"Key"]=true;break;default:if(ident)throw Error("Invalid shortcut");ident=part}});this.ident_=ident;this.mods_=mods}KeyboardShortcut.prototype={matchesEvent:function(e){if(e.key==this.ident_){var mods=this.mods_;return["altKey","ctrlKey","metaKey","shiftKey"].every(function(k){return e[k]==!!mods[k]})}return false}};var Command=cr.ui.define("command");Command.prototype={__proto__:HTMLElement.prototype,decorate:function(){CommandManager.init(assert(this.ownerDocument));if(this.hasAttribute("shortcut"))this.shortcut=this.getAttribute("shortcut")},execute:function(opt_element){if(this.disabled)return;var doc=this.ownerDocument;if(doc.activeElement){var e=new Event("command",{bubbles:true});e.command=this;(opt_element||doc.activeElement).dispatchEvent(e)}},canExecuteChange:function(opt_node){dispatchCanExecuteEvent(this,opt_node||this.ownerDocument.activeElement)},shortcut_:"",get shortcut(){return this.shortcut_},set shortcut(shortcut){var oldShortcut=this.shortcut_;if(shortcut!==oldShortcut){this.keyboardShortcuts_=shortcut.split(/\s+/).map(function(shortcut){return new KeyboardShortcut(shortcut)});this.shortcut_=shortcut;cr.dispatchPropertyChange(this,"shortcut",this.shortcut_,oldShortcut)}},matchesEvent:function(e){if(!this.keyboardShortcuts_)return false;return this.keyboardShortcuts_.some(function(keyboardShortcut){return keyboardShortcut.matchesEvent(e)})}};cr.defineProperty(Command,"label",cr.PropertyKind.ATTR);cr.defineProperty(Command,"disabled",cr.PropertyKind.BOOL_ATTR);cr.defineProperty(Command,"hidden",cr.PropertyKind.BOOL_ATTR);cr.defineProperty(Command,"checked",cr.PropertyKind.BOOL_ATTR);cr.defineProperty(Command,"hideShortcutText",cr.PropertyKind.BOOL_ATTR);function dispatchCanExecuteEvent(command,target){var e=new CanExecuteEvent(command);target.dispatchEvent(e);command.disabled=!e.canExecute}var commandManagers={};function CommandManager(doc){doc.addEventListener("focus",this.handleFocus_.bind(this),true);doc.addEventListener("keydown",this.handleKeyDown_.bind(this),false)}CommandManager.init=function(doc){var uid=cr.getUid(doc);if(!(uid in commandManagers)){commandManagers[uid]=new CommandManager(doc)}};CommandManager.prototype={handleFocus_:function(e){var target=e.target;if(target.menu||target.command)return;var commands=Array.prototype.slice.call(target.ownerDocument.querySelectorAll("command"));commands.forEach(function(command){dispatchCanExecuteEvent(command,target)})},handleKeyDown_:function(e){var target=e.target;var commands=Array.prototype.slice.call(target.ownerDocument.querySelectorAll("command"));for(var i=0,command;command=commands[i];i++){if(command.matchesEvent(e)){command.canExecuteChange();if(!command.disabled){e.preventDefault();e.stopPropagation();command.execute();return}}}}};function CanExecuteEvent(command){var e=new Event("canExecute",{bubbles:true,cancelable:true});e.__proto__=CanExecuteEvent.prototype;e.command=command;return e}CanExecuteEvent.prototype={__proto__:Event.prototype,command:null,canExecute_:false,get canExecute(){return this.canExecute_},set canExecute(canExecute){this.canExecute_=!!canExecute;this.stopPropagation();this.preventDefault()}};return{Command:Command,CanExecuteEvent:CanExecuteEvent}}); // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -function $(id) { - var el = document.getElementById(id); - return el ? assertInstanceof(el, HTMLElement) : null; -} - -function getSVGElement(id) { - var el = document.getElementById(id); - return el ? assertInstanceof(el, Element) : null; -} - -function announceAccessibleMessage(msg) { - var element = document.createElement('div'); - element.setAttribute('aria-live', 'polite'); - element.style.position = 'relative'; - element.style.left = '-9999px'; - element.style.height = '0px'; - element.innerText = msg; - document.body.appendChild(element); - window.setTimeout(function() { - document.body.removeChild(element); - }, 0); -} - -function url(s) { - var s2 = s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g, '\\$1'); - if (/\\\\$/.test(s2)) { - s2 += ' '; - } - return 'url("' + s2 + '")'; -} - -function parseQueryParams(location) { - var params = {}; - var query = unescape(location.search.substring(1)); - var vars = query.split('&'); - for (var i = 0; i < vars.length; i++) { - var pair = vars[i].split('='); - params[pair[0]] = pair[1]; - } - return params; -} - -function setQueryParam(location, key, value) { - var query = parseQueryParams(location); - query[encodeURIComponent(key)] = encodeURIComponent(value); - var newQuery = ''; - for (var q in query) { - newQuery += (newQuery ? '&' : '?') + q + '=' + query[q]; - } - return location.origin + location.pathname + newQuery + location.hash; -} - -function findAncestorByClass(el, className) { - return findAncestor(el, function(el) { - return el.classList && el.classList.contains(className); - }); -} - -function findAncestor(node, predicate) { - var last = false; - while (node != null && !(last = predicate(node))) { - node = node.parentNode; - } - return last ? node : null; -} - -function swapDomNodes(a, b) { - var afterA = a.nextSibling; - if (afterA == b) { - swapDomNodes(b, a); - return; - } - var aParent = a.parentNode; - b.parentNode.replaceChild(a, b); - aParent.insertBefore(b, afterA); -} - -function disableTextSelectAndDrag(opt_allowSelectStart, opt_allowDragStart) { - document.onselectstart = function(e) { - if (!(opt_allowSelectStart && opt_allowSelectStart.call(this, e))) e.preventDefault(); - }; - document.ondragstart = function(e) { - if (!(opt_allowDragStart && opt_allowDragStart.call(this, e))) e.preventDefault(); - }; -} - -function preventDefaultOnPoundLinkClicks() { - document.addEventListener('click', function(e) { - var anchor = findAncestor(e.target, function(el) { - return el.tagName == 'A'; - }); - if (anchor && anchor.getAttribute('href') == '#') e.preventDefault(); - }); -} - -function isRTL() { - return document.documentElement.dir == 'rtl'; -} - -function getRequiredElement(id) { - return assertInstanceof($(id), HTMLElement, 'Missing required element: ' + id); -} - -function queryRequiredElement(selectors, opt_context) { - var element = (opt_context || document).querySelector(selectors); - return assertInstanceof(element, HTMLElement, 'Missing required element: ' + selectors); -} - -[ 'click', 'auxclick' ].forEach(function(eventName) { - document.addEventListener(eventName, function(e) { - if (e.button > 1) return; - if (e.defaultPrevented) return; - var eventPath = e.path; - var anchor = null; - if (eventPath) { - for (var i = 0; i < eventPath.length; i++) { - var element = eventPath[i]; - if (element.tagName === 'A' && element.href) { - anchor = element; - break; - } - } - } - var el = e.target; - if (!anchor && el.nodeType == Node.ELEMENT_NODE && el.webkitMatchesSelector('A, A *')) { - while (el.tagName != 'A') { - el = el.parentElement; - } - anchor = el; - } - if (!anchor) return; - anchor = anchor; - if ((anchor.protocol == 'file:' || anchor.protocol == 'about:') && (e.button == 0 || e.button == 1)) { - chrome.send('navigateToUrl', [ anchor.href, anchor.target, e.button, e.altKey, e.ctrlKey, e.metaKey, e.shiftKey ]); - e.preventDefault(); - } - }); -}); - -function appendParam(url, key, value) { - var param = encodeURIComponent(key) + '=' + encodeURIComponent(value); - if (url.indexOf('?') == -1) return url + '?' + param; - return url + '&' + param; -} - -function createElementWithClassName(type, className) { - var elm = document.createElement(type); - elm.className = className; - return elm; -} - -function ensureTransitionEndEvent(el, opt_timeOut) { - if (opt_timeOut === undefined) { - var style = getComputedStyle(el); - opt_timeOut = parseFloat(style.transitionDuration) * 1e3; - opt_timeOut += 50; - } - var fired = false; - el.addEventListener('webkitTransitionEnd', function f(e) { - el.removeEventListener('webkitTransitionEnd', f); - fired = true; - }); - window.setTimeout(function() { - if (!fired) cr.dispatchSimpleEvent(el, 'webkitTransitionEnd', true); - }, opt_timeOut); -} - -function scrollTopForDocument(doc) { - return doc.documentElement.scrollTop || doc.body.scrollTop; -} - -function setScrollTopForDocument(doc, value) { - doc.documentElement.scrollTop = doc.body.scrollTop = value; -} - -function scrollLeftForDocument(doc) { - return doc.documentElement.scrollLeft || doc.body.scrollLeft; -} - -function setScrollLeftForDocument(doc, value) { - doc.documentElement.scrollLeft = doc.body.scrollLeft = value; -} - -function HTMLEscape(original) { - return original.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, '''); -} - -function elide(original, maxLength) { - if (original.length <= maxLength) return original; - return original.substring(0, maxLength - 1) + '…'; -} - -function quoteString(str) { - return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1'); -} - -function listenOnce(target, eventNames, callback) { - if (!Array.isArray(eventNames)) eventNames = eventNames.split(/ +/); - var removeAllAndCallCallback = function(event) { - eventNames.forEach(function(eventName) { - target.removeEventListener(eventName, removeAllAndCallCallback, false); - }); - return callback(event); - }; - eventNames.forEach(function(eventName) { - target.addEventListener(eventName, removeAllAndCallCallback, false); - }); -} - +function $(id){var el=document.getElementById(id);return el?assertInstanceof(el,HTMLElement):null}function getSVGElement(id){var el=document.getElementById(id);return el?assertInstanceof(el,Element):null}function announceAccessibleMessage(msg){var element=document.createElement("div");element.setAttribute("aria-live","polite");element.style.position="relative";element.style.left="-9999px";element.style.height="0px";element.innerText=msg;document.body.appendChild(element);window.setTimeout(function(){document.body.removeChild(element)},0)}function url(s){var s2=s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g,"\\$1");if(/\\\\$/.test(s2)){s2+=" "}return'url("'+s2+'")'}function parseQueryParams(location){var params={};var query=unescape(location.search.substring(1));var vars=query.split("&");for(var i=0;i<vars.length;i++){var pair=vars[i].split("=");params[pair[0]]=pair[1]}return params}function setQueryParam(location,key,value){var query=parseQueryParams(location);query[encodeURIComponent(key)]=encodeURIComponent(value);var newQuery="";for(var q in query){newQuery+=(newQuery?"&":"?")+q+"="+query[q]}return location.origin+location.pathname+newQuery+location.hash}function findAncestorByClass(el,className){return findAncestor(el,function(el){return el.classList&&el.classList.contains(className)})}function findAncestor(node,predicate){var last=false;while(node!=null&&!(last=predicate(node))){node=node.parentNode}return last?node:null}function swapDomNodes(a,b){var afterA=a.nextSibling;if(afterA==b){swapDomNodes(b,a);return}var aParent=a.parentNode;b.parentNode.replaceChild(a,b);aParent.insertBefore(b,afterA)}function disableTextSelectAndDrag(opt_allowSelectStart,opt_allowDragStart){document.onselectstart=function(e){if(!(opt_allowSelectStart&&opt_allowSelectStart.call(this,e)))e.preventDefault()};document.ondragstart=function(e){if(!(opt_allowDragStart&&opt_allowDragStart.call(this,e)))e.preventDefault()}}function preventDefaultOnPoundLinkClicks(){document.addEventListener("click",function(e){var anchor=findAncestor(e.target,function(el){return el.tagName=="A"});if(anchor&&anchor.getAttribute("href")=="#")e.preventDefault()})}function isRTL(){return document.documentElement.dir=="rtl"}function getRequiredElement(id){return assertInstanceof($(id),HTMLElement,"Missing required element: "+id)}function queryRequiredElement(selectors,opt_context){var element=(opt_context||document).querySelector(selectors);return assertInstanceof(element,HTMLElement,"Missing required element: "+selectors)}["click","auxclick"].forEach(function(eventName){document.addEventListener(eventName,function(e){if(e.button>1)return;if(e.defaultPrevented)return;var eventPath=e.path;var anchor=null;if(eventPath){for(var i=0;i<eventPath.length;i++){var element=eventPath[i];if(element.tagName==="A"&&element.href){anchor=element;break}}}var el=e.target;if(!anchor&&el.nodeType==Node.ELEMENT_NODE&&el.webkitMatchesSelector("A, A *")){while(el.tagName!="A"){el=el.parentElement}anchor=el}if(!anchor)return;anchor=anchor;if((anchor.protocol=="file:"||anchor.protocol=="about:")&&(e.button==0||e.button==1)){chrome.send("navigateToUrl",[anchor.href,anchor.target,e.button,e.altKey,e.ctrlKey,e.metaKey,e.shiftKey]);e.preventDefault()}})});function appendParam(url,key,value){var param=encodeURIComponent(key)+"="+encodeURIComponent(value);if(url.indexOf("?")==-1)return url+"?"+param;return url+"&"+param}function createElementWithClassName(type,className){var elm=document.createElement(type);elm.className=className;return elm}function ensureTransitionEndEvent(el,opt_timeOut){if(opt_timeOut===undefined){var style=getComputedStyle(el);opt_timeOut=parseFloat(style.transitionDuration)*1e3;opt_timeOut+=50}var fired=false;el.addEventListener("webkitTransitionEnd",function f(e){el.removeEventListener("webkitTransitionEnd",f);fired=true});window.setTimeout(function(){if(!fired)cr.dispatchSimpleEvent(el,"webkitTransitionEnd",true)},opt_timeOut)}function scrollTopForDocument(doc){return doc.documentElement.scrollTop||doc.body.scrollTop}function setScrollTopForDocument(doc,value){doc.documentElement.scrollTop=doc.body.scrollTop=value}function scrollLeftForDocument(doc){return doc.documentElement.scrollLeft||doc.body.scrollLeft}function setScrollLeftForDocument(doc,value){doc.documentElement.scrollLeft=doc.body.scrollLeft=value}function HTMLEscape(original){return original.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function elide(original,maxLength){if(original.length<=maxLength)return original;return original.substring(0,maxLength-1)+"…"}function quoteString(str){return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g,"\\$1")}function listenOnce(target,eventNames,callback){if(!Array.isArray(eventNames))eventNames=eventNames.split(/ +/);var removeAllAndCallCallback=function(event){eventNames.forEach(function(eventName){target.removeEventListener(eventName,removeAllAndCallCallback,false)});return callback(event)};eventNames.forEach(function(eventName){target.addEventListener(eventName,removeAllAndCallCallback,false)})} // <if expr="is_ios"> -if (!('key' in KeyboardEvent.prototype)) { - Object.defineProperty(KeyboardEvent.prototype, 'key', { - get: function() { - if (this.keyCode >= 48 && this.keyCode <= 57) return String.fromCharCode(this.keyCode); - if (this.keyCode >= 65 && this.keyCode <= 90) { - var result = String.fromCharCode(this.keyCode).toLowerCase(); - if (this.shiftKey) result = result.toUpperCase(); - return result; - } - switch (this.keyCode) { - case 8: - return 'Backspace'; - - case 9: - return 'Tab'; - - case 13: - return 'Enter'; - - case 16: - return 'Shift'; - - case 17: - return 'Control'; - - case 18: - return 'Alt'; - - case 27: - return 'Escape'; - - case 32: - return ' '; - - case 33: - return 'PageUp'; - - case 34: - return 'PageDown'; - - case 35: - return 'End'; - - case 36: - return 'Home'; - - case 37: - return 'ArrowLeft'; - - case 38: - return 'ArrowUp'; - - case 39: - return 'ArrowRight'; - - case 40: - return 'ArrowDown'; - - case 45: - return 'Insert'; - - case 46: - return 'Delete'; - - case 91: - return 'Meta'; - - case 112: - return 'F1'; - - case 113: - return 'F2'; - - case 114: - return 'F3'; - - case 115: - return 'F4'; - - case 116: - return 'F5'; - - case 117: - return 'F6'; - - case 118: - return 'F7'; - - case 119: - return 'F8'; - - case 120: - return 'F9'; - - case 121: - return 'F10'; - - case 122: - return 'F11'; - - case 123: - return 'F12'; - - case 187: - return '='; - - case 189: - return '-'; - - case 219: - return '['; - - case 221: - return ']'; - } - return 'Unidentified'; - } - }); -} else { - window.console.log("KeyboardEvent.Key polyfill not required"); -} - +if(!("key"in KeyboardEvent.prototype)){Object.defineProperty(KeyboardEvent.prototype,"key",{get:function(){if(this.keyCode>=48&&this.keyCode<=57)return String.fromCharCode(this.keyCode);if(this.keyCode>=65&&this.keyCode<=90){var result=String.fromCharCode(this.keyCode).toLowerCase();if(this.shiftKey)result=result.toUpperCase();return result}switch(this.keyCode){case 8:return"Backspace";case 9:return"Tab";case 13:return"Enter";case 16:return"Shift";case 17:return"Control";case 18:return"Alt";case 27:return"Escape";case 32:return" ";case 33:return"PageUp";case 34:return"PageDown";case 35:return"End";case 36:return"Home";case 37:return"ArrowLeft";case 38:return"ArrowUp";case 39:return"ArrowRight";case 40:return"ArrowDown";case 45:return"Insert";case 46:return"Delete";case 91:return"Meta";case 112:return"F1";case 113:return"F2";case 114:return"F3";case 115:return"F4";case 116:return"F5";case 117:return"F6";case 118:return"F7";case 119:return"F8";case 120:return"F9";case 121:return"F10";case 122:return"F11";case 123:return"F12";case 187:return"=";case 189:return"-";case 219:return"[";case 221:return"]"}return"Unidentified"}})}else{window.console.log("KeyboardEvent.Key polyfill not required")} // </if> /* is_ios */ -Polymer.IronResizableBehavior = { - properties: { - _parentResizable: { - type: Object, - observer: '_parentResizableChanged' - }, - _notifyingDescendant: { - type: Boolean, - value: false - } - }, - listeners: { - 'iron-request-resize-notifications': '_onIronRequestResizeNotifications' - }, - created: function() { - 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; - }, - notifyResize: function() { - if (!this.isAttached) { - return; - } - this._interestedResizables.forEach(function(resizable) { - if (this.resizerShouldNotify(resizable)) { - this._notifyDescendant(resizable); - } - }, this); - this._fireResize(); - }, - assignParentResizable: function(parentResizable) { - this._parentResizable = parentResizable; - }, - stopResizeNotificationsFor: function(target) { - var index = this._interestedResizables.indexOf(target); - if (index > -1) { - this._interestedResizables.splice(index, 1); - this.unlisten(target, 'iron-resize', '_onDescendantIronResize'); - } - }, - resizerShouldNotify: function(element) { - return true; - }, - _onDescendantIronResize: function(event) { - if (this._notifyingDescendant) { - event.stopPropagation(); - return; - } - 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) { - if (!this.isAttached) { - return; - } - this._notifyingDescendant = true; - descendant.notifyResize(); - this._notifyingDescendant = false; - } -}; +Polymer.IronResizableBehavior={properties:{_parentResizable:{type:Object,observer:"_parentResizableChanged"},_notifyingDescendant:{type:Boolean,value:false}},listeners:{"iron-request-resize-notifications":"_onIronRequestResizeNotifications"},created:function(){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},notifyResize:function(){if(!this.isAttached){return}this._interestedResizables.forEach(function(resizable){if(this.resizerShouldNotify(resizable)){this._notifyDescendant(resizable)}},this);this._fireResize()},assignParentResizable:function(parentResizable){this._parentResizable=parentResizable},stopResizeNotificationsFor:function(target){var index=this._interestedResizables.indexOf(target);if(index>-1){this._interestedResizables.splice(index,1);this.unlisten(target,"iron-resize","_onDescendantIronResize")}},resizerShouldNotify:function(element){return true},_onDescendantIronResize:function(event){if(this._notifyingDescendant){event.stopPropagation();return}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){if(!this.isAttached){return}this._notifyingDescendant=true;descendant.notifyResize();this._notifyingDescendant=false}};(function(){"use strict";var KEY_IDENTIFIER={"U+0008":"backspace","U+0009":"tab","U+001B":"esc","U+0020":"space","U+007F":"del"};var KEY_CODE={8:"backspace",9:"tab",13:"enter",27:"esc",33:"pageup",34:"pagedown",35:"end",36:"home",32:"space",37:"left",38:"up",39:"right",40:"down",46:"del",106:"*"};var MODIFIER_KEYS={shift:"shiftKey",ctrl:"ctrlKey",alt:"altKey",meta:"metaKey"};var KEY_CHAR=/[a-z0-9*]/;var IDENT_CHAR=/U\+/;var ARROW_KEY=/^arrow/;var SPACE_KEY=/^space(bar)?/;var ESC_KEY=/^escape$/;function transformKey(key,noSpecialChars){var validKey="";if(key){var lKey=key.toLowerCase();if(lKey===" "||SPACE_KEY.test(lKey)){validKey="space"}else if(ESC_KEY.test(lKey)){validKey="esc"}else if(lKey.length==1){if(!noSpecialChars||KEY_CHAR.test(lKey)){validKey=lKey}}else if(ARROW_KEY.test(lKey)){validKey=lKey.replace("arrow","")}else if(lKey=="multiply"){validKey="*"}else{validKey=lKey}}return validKey}function transformKeyIdentifier(keyIdent){var validKey="";if(keyIdent){if(keyIdent in KEY_IDENTIFIER){validKey=KEY_IDENTIFIER[keyIdent]}else if(IDENT_CHAR.test(keyIdent)){keyIdent=parseInt(keyIdent.replace("U+","0x"),16);validKey=String.fromCharCode(keyIdent).toLowerCase()}else{validKey=keyIdent.toLowerCase()}}return validKey}function transformKeyCode(keyCode){var validKey="";if(Number(keyCode)){if(keyCode>=65&&keyCode<=90){validKey=String.fromCharCode(32+keyCode)}else if(keyCode>=112&&keyCode<=123){validKey="f"+(keyCode-112)}else if(keyCode>=48&&keyCode<=57){validKey=String(keyCode-48)}else if(keyCode>=96&&keyCode<=105){validKey=String(keyCode-96)}else{validKey=KEY_CODE[keyCode]}}return validKey}function normalizedKeyForEvent(keyEvent,noSpecialChars){if(keyEvent.key){return transformKey(keyEvent.key,noSpecialChars)}if(keyEvent.detail&&keyEvent.detail.key){return transformKey(keyEvent.detail.key,noSpecialChars)}return transformKeyIdentifier(keyEvent.keyIdentifier)||transformKeyCode(keyEvent.keyCode)||""}function keyComboMatchesEvent(keyCombo,event){var keyEvent=normalizedKeyForEvent(event,keyCombo.hasModifiers);return keyEvent===keyCombo.key&&(!keyCombo.hasModifiers||!!event.shiftKey===!!keyCombo.shiftKey&&!!event.ctrlKey===!!keyCombo.ctrlKey&&!!event.altKey===!!keyCombo.altKey&&!!event.metaKey===!!keyCombo.metaKey)}function parseKeyComboString(keyComboString){if(keyComboString.length===1){return{combo:keyComboString,key:keyComboString,event:"keydown"}}return keyComboString.split("+").reduce(function(parsedKeyCombo,keyComboPart){var eventParts=keyComboPart.split(":");var keyName=eventParts[0];var event=eventParts[1];if(keyName in MODIFIER_KEYS){parsedKeyCombo[MODIFIER_KEYS[keyName]]=true;parsedKeyCombo.hasModifiers=true}else{parsedKeyCombo.key=keyName;parsedKeyCombo.event=event||"keydown"}return parsedKeyCombo},{combo:keyComboString.split(":").shift()})}function parseEventString(eventString){return eventString.trim().split(" ").map(function(keyComboString){return parseKeyComboString(keyComboString)})}Polymer.IronA11yKeysBehavior={properties:{keyEventTarget:{type:Object,value:function(){return this}},stopKeyboardEventPropagation:{type:Boolean,value:false},_boundKeyHandlers:{type:Array,value:function(){return[]}},_imperativeKeyBindings:{type:Object,value:function(){return{}}}},observers:["_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)"],keyBindings:{},registered:function(){this._prepKeyBindings()},attached:function(){this._listenKeyEventListeners()},detached:function(){this._unlistenKeyEventListeners()},addOwnKeyBinding:function(eventString,handlerName){this._imperativeKeyBindings[eventString]=handlerName;this._prepKeyBindings();this._resetKeyEventListeners()},removeOwnKeyBindings:function(){this._imperativeKeyBindings={};this._prepKeyBindings();this._resetKeyEventListeners()},keyboardEventMatchesKeys:function(event,eventString){var keyCombos=parseEventString(eventString);for(var i=0;i<keyCombos.length;++i){if(keyComboMatchesEvent(keyCombos[i],event)){return true}}return false},_collectKeyBindings:function(){var keyBindings=this.behaviors.map(function(behavior){return behavior.keyBindings});if(keyBindings.indexOf(this.keyBindings)===-1){keyBindings.push(this.keyBindings)}return keyBindings},_prepKeyBindings:function(){this._keyBindings={};this._collectKeyBindings().forEach(function(keyBindings){for(var eventString in keyBindings){this._addKeyBinding(eventString,keyBindings[eventString])}},this);for(var eventString in this._imperativeKeyBindings){this._addKeyBinding(eventString,this._imperativeKeyBindings[eventString])}for(var eventName in this._keyBindings){this._keyBindings[eventName].sort(function(kb1,kb2){var b1=kb1[0].hasModifiers;var b2=kb2[0].hasModifiers;return b1===b2?0:b1?-1:1})}},_addKeyBinding:function(eventString,handlerName){parseEventString(eventString).forEach(function(keyCombo){this._keyBindings[keyCombo.event]=this._keyBindings[keyCombo.event]||[];this._keyBindings[keyCombo.event].push([keyCombo,handlerName])},this)},_resetKeyEventListeners:function(){this._unlistenKeyEventListeners();if(this.isAttached){this._listenKeyEventListeners()}},_listenKeyEventListeners:function(){if(!this.keyEventTarget){return}Object.keys(this._keyBindings).forEach(function(eventName){var keyBindings=this._keyBindings[eventName];var boundKeyHandler=this._onKeyBindingEvent.bind(this,keyBindings);this._boundKeyHandlers.push([this.keyEventTarget,eventName,boundKeyHandler]);this.keyEventTarget.addEventListener(eventName,boundKeyHandler)},this)},_unlistenKeyEventListeners:function(){var keyHandlerTuple;var keyEventTarget;var eventName;var boundKeyHandler;while(this._boundKeyHandlers.length){keyHandlerTuple=this._boundKeyHandlers.pop();keyEventTarget=keyHandlerTuple[0];eventName=keyHandlerTuple[1];boundKeyHandler=keyHandlerTuple[2];keyEventTarget.removeEventListener(eventName,boundKeyHandler)}},_onKeyBindingEvent:function(keyBindings,event){if(this.stopKeyboardEventPropagation){event.stopPropagation()}if(event.defaultPrevented){return}for(var i=0;i<keyBindings.length;i++){var keyCombo=keyBindings[i][0];var handlerName=keyBindings[i][1];if(keyComboMatchesEvent(keyCombo,event)){this._triggerKeyHandler(keyCombo,handlerName,event);if(event.defaultPrevented){return}}}},_triggerKeyHandler:function(keyCombo,handlerName,keyboardEvent){var detail=Object.create(keyCombo);detail.keyboardEvent=keyboardEvent;var event=new CustomEvent(keyCombo.event,{detail:detail,cancelable:true});this[handlerName].call(this,event);if(event.defaultPrevented){keyboardEvent.preventDefault()}}}})();Polymer.IronScrollTargetBehavior={properties:{scrollTarget:{type:HTMLElement,value:function(){return this._defaultScrollTarget}}},observers:["_scrollTargetChanged(scrollTarget, isAttached)"],_scrollTargetChanged:function(scrollTarget,isAttached){var eventTarget;if(this._oldScrollTarget){eventTarget=this._oldScrollTarget===this._doc?window:this._oldScrollTarget;eventTarget.removeEventListener("scroll",this._boundScrollHandler);this._oldScrollTarget=null}if(!isAttached){return}if(scrollTarget==="document"){this.scrollTarget=this._doc}else if(typeof scrollTarget==="string"){this.scrollTarget=this.domHost?this.domHost.$[scrollTarget]:Polymer.dom(this.ownerDocument).querySelector("#"+scrollTarget)}else if(this._isValidScrollTarget()){eventTarget=scrollTarget===this._doc?window:scrollTarget;this._boundScrollHandler=this._boundScrollHandler||this._scrollHandler.bind(this);this._oldScrollTarget=scrollTarget;eventTarget.addEventListener("scroll",this._boundScrollHandler)}},_scrollHandler:function scrollHandler(){},get _defaultScrollTarget(){return this._doc},get _doc(){return this.ownerDocument.documentElement},get _scrollTop(){if(this._isValidScrollTarget()){return this.scrollTarget===this._doc?window.pageYOffset:this.scrollTarget.scrollTop}return 0},get _scrollLeft(){if(this._isValidScrollTarget()){return this.scrollTarget===this._doc?window.pageXOffset:this.scrollTarget.scrollLeft}return 0},set _scrollTop(top){if(this.scrollTarget===this._doc){window.scrollTo(window.pageXOffset,top)}else if(this._isValidScrollTarget()){this.scrollTarget.scrollTop=top}},set _scrollLeft(left){if(this.scrollTarget===this._doc){window.scrollTo(left,window.pageYOffset)}else if(this._isValidScrollTarget()){this.scrollTarget.scrollLeft=left}},scroll:function(left,top){if(this.scrollTarget===this._doc){window.scrollTo(left,top)}else if(this._isValidScrollTarget()){this.scrollTarget.scrollLeft=left;this.scrollTarget.scrollTop=top}},get _scrollTargetWidth(){if(this._isValidScrollTarget()){return this.scrollTarget===this._doc?window.innerWidth:this.scrollTarget.offsetWidth}return 0},get _scrollTargetHeight(){if(this._isValidScrollTarget()){return this.scrollTarget===this._doc?window.innerHeight:this.scrollTarget.offsetHeight}return 0},_isValidScrollTarget:function(){return this.scrollTarget instanceof HTMLElement}};(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=3;var HIDDEN_Y="-10000px";var DEFAULT_GRID_SIZE=200;var SECRET_TABINDEX=-100;Polymer({is:"iron-list",properties:{items:{type:Array},maxPhysicalCount:{type:Number,value:500},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},selectedAs:{type:String,value:"selected"},grid:{type:Boolean,value:false,reflectToAttribute:true},selectionEnabled:{type:Boolean,value:false},selectedItem:{type:Object,notify:true},selectedItems:{type:Object,notify:true},multiSelection:{type:Boolean,value:false}},observers:["_itemsChanged(items.*)","_selectionEnabledChanged(selectionEnabled)","_multiSelectionChanged(multiSelection)","_setOverflow(scrollTarget)"],behaviors:[Polymer.Templatizer,Polymer.IronResizableBehavior,Polymer.IronA11yKeysBehavior,Polymer.IronScrollTargetBehavior],keyBindings:{up:"_didMoveUp",down:"_didMoveDown",enter:"_didEnter"},_ratio:.5,_scrollerPaddingTop:0,_scrollPosition:0,_physicalSize:0,_physicalAverage:0,_physicalAverageCount:0,_physicalTop:0,_virtualCount:0,_physicalIndexForKey:null,_estScrollHeight:0,_scrollHeight:0,_viewportHeight:0,_viewportWidth:0,_physicalItems:null,_physicalSizes:null,_firstVisibleIndexVal:null,_lastVisibleIndexVal:null,_collection:null,_maxPages:3,_focusedItem:null,_focusedIndex:-1,_offscreenFocusedItem:null,_focusBackfillItem:null,_itemsPerRow:1,_itemWidth:0,_rowHeight:0,_templateCost:0,get _physicalBottom(){return this._physicalTop+this._physicalSize},get _scrollBottom(){return this._scrollPosition+this._viewportHeight},get _virtualEnd(){return this._virtualStart+this._physicalCount-1},get _hiddenContentSize(){var size=this.grid?this._physicalRows*this._rowHeight:this._physicalSize;return size-this._viewportHeight},get _maxScrollTop(){return this._estScrollHeight-this._viewportHeight+this._scrollerPaddingTop},_minVirtualStart:0,get _maxVirtualStart(){return Math.max(0,this._virtualCount-this._physicalCount)},_virtualStartVal:0,set _virtualStart(val){this._virtualStartVal=Math.min(this._maxVirtualStart,Math.max(this._minVirtualStart,val))},get _virtualStart(){return this._virtualStartVal||0},_physicalStartVal:0,set _physicalStart(val){this._physicalStartVal=val%this._physicalCount;if(this._physicalStartVal<0){this._physicalStartVal=this._physicalCount+this._physicalStartVal}this._physicalEnd=(this._physicalStart+this._physicalCount-1)%this._physicalCount},get _physicalStart(){return this._physicalStartVal||0},_physicalCountVal:0,set _physicalCount(val){this._physicalCountVal=val;this._physicalEnd=(this._physicalStart+this._physicalCount-1)%this._physicalCount},get _physicalCount(){return this._physicalCountVal},_physicalEnd:0,get _optPhysicalSize(){if(this.grid){return this._estRowsInView*this._rowHeight*this._maxPages}return this._viewportHeight*this._maxPages},get _optPhysicalCount(){return this._estRowsInView*this._itemsPerRow*this._maxPages},get _isVisible(){return Boolean(this.offsetWidth||this.offsetHeight)},get firstVisibleIndex(){if(this._firstVisibleIndexVal===null){var physicalOffset=Math.floor(this._physicalTop+this._scrollerPaddingTop);this._firstVisibleIndexVal=this._iterateItems(function(pidx,vidx){physicalOffset+=this._getPhysicalSizeIncrement(pidx);if(physicalOffset>this._scrollPosition){return this.grid?vidx-vidx%this._itemsPerRow:vidx}if(this.grid&&this._virtualCount-1===vidx){return vidx-vidx%this._itemsPerRow}})||0}return this._firstVisibleIndexVal},get lastVisibleIndex(){if(this._lastVisibleIndexVal===null){if(this.grid){var lastIndex=this.firstVisibleIndex+this._estRowsInView*this._itemsPerRow-1;this._lastVisibleIndexVal=Math.min(this._virtualCount,lastIndex)}else{var physicalOffset=this._physicalTop;this._iterateItems(function(pidx,vidx){if(physicalOffset<this._scrollBottom){this._lastVisibleIndexVal=vidx}else{return true}physicalOffset+=this._getPhysicalSizeIncrement(pidx)})}}return this._lastVisibleIndexVal},get _defaultScrollTarget(){return this},get _virtualRowCount(){return Math.ceil(this._virtualCount/this._itemsPerRow)},get _estRowsInView(){return Math.ceil(this._viewportHeight/this._rowHeight)},get _physicalRows(){return Math.ceil(this._physicalCount/this._itemsPerRow)},ready:function(){this.addEventListener("focus",this._didFocus.bind(this),true)},attached:function(){this.updateViewportBoundaries();if(this._physicalCount===0){this._debounceTemplate(this._render)}this.listen(this,"iron-resize","_resizeHandler")},detached:function(){this.unlisten(this,"iron-resize","_resizeHandler")},_setOverflow:function(scrollTarget){this.style.webkitOverflowScrolling=scrollTarget===this?"touch":"";this.style.overflow=scrollTarget===this?"auto":""},updateViewportBoundaries:function(){this._scrollerPaddingTop=this.scrollTarget===this?0:parseInt(window.getComputedStyle(this)["padding-top"],10);this._viewportHeight=this._scrollTargetHeight;if(this.grid){this._updateGridMetrics()}},_scrollHandler:function(){var scrollTop=Math.max(0,Math.min(this._maxScrollTop,this._scrollTop));var delta=scrollTop-this._scrollPosition;var tileHeight,tileTop,kth,recycledTileSet,scrollBottom,physicalBottom;var ratio=this._ratio;var recycledTiles=0;var hiddenContentSize=this._hiddenContentSize;var currentRatio=ratio;var movingUp=[];this._scrollPosition=scrollTop;this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null;scrollBottom=this._scrollBottom;physicalBottom=this._physicalBottom;if(Math.abs(delta)>this._physicalSize){this._physicalTop+=delta;recycledTiles=Math.round(delta/this._physicalAverage)}else if(delta<0){var topSpace=scrollTop-this._physicalTop;var virtualStart=this._virtualStart;recycledTileSet=[];kth=this._physicalEnd;currentRatio=topSpace/hiddenContentSize;while(currentRatio<ratio&&recycledTiles<this._physicalCount&&virtualStart-recycledTiles>0&&physicalBottom-this._getPhysicalSizeIncrement(kth)>scrollBottom){tileHeight=this._getPhysicalSizeIncrement(kth);currentRatio+=tileHeight/hiddenContentSize;physicalBottom-=tileHeight;recycledTileSet.push(kth);recycledTiles++;kth=kth===0?this._physicalCount-1:kth-1}movingUp=recycledTileSet;recycledTiles=-recycledTiles}else if(delta>0){var bottomSpace=physicalBottom-scrollBottom;var virtualEnd=this._virtualEnd;var lastVirtualItemIndex=this._virtualCount-1;recycledTileSet=[];kth=this._physicalStart;currentRatio=bottomSpace/hiddenContentSize;while(currentRatio<ratio&&recycledTiles<this._physicalCount&&virtualEnd+recycledTiles<lastVirtualItemIndex&&this._physicalTop+this._getPhysicalSizeIncrement(kth)<scrollTop){tileHeight=this._getPhysicalSizeIncrement(kth);currentRatio+=tileHeight/hiddenContentSize;this._physicalTop+=tileHeight;recycledTileSet.push(kth);recycledTiles++;kth=(kth+1)%this._physicalCount}}if(recycledTiles===0){if(physicalBottom<scrollBottom||this._physicalTop>scrollTop){this._increasePoolIfNeeded()}}else{this._virtualStart=this._virtualStart+recycledTiles;this._physicalStart=this._physicalStart+recycledTiles;this._update(recycledTileSet,movingUp)}},_update:function(itemSet,movingUp){this._manageFocus();this._assignModels(itemSet);this._updateMetrics(itemSet);if(movingUp){while(movingUp.length){var idx=movingUp.pop();this._physicalTop-=this._getPhysicalSizeIncrement(idx)}}this._positionItems();this._updateScrollerSize();this._increasePoolIfNeeded()},_createPool:function(size){var physicalItems=new Array(size);this._ensureTemplatized();for(var i=0;i<size;i++){var inst=this.stamp(null);physicalItems[i]=inst.root.querySelector("*");Polymer.dom(this).appendChild(inst.root)}return physicalItems},_increasePoolIfNeeded:function(){if(this._viewportHeight===0){return false}var self=this;var isClientFull=this._physicalBottom>=this._scrollBottom&&this._physicalTop<=this._scrollPosition;if(this._physicalSize>=this._optPhysicalSize&&isClientFull){return false}var maxPoolSize=Math.round(this._physicalCount*.5);if(!isClientFull){this._debounceTemplate(this._increasePool.bind(this,maxPoolSize));return true}this._yield(function(){self._increasePool(Math.min(maxPoolSize,Math.max(1,Math.round(50/self._templateCost))))});return true},_yield:function(cb){var g=window;var handle=g.requestIdleCallback?g.requestIdleCallback(cb):g.setTimeout(cb,16);Polymer.dom.addDebouncer({complete:function(){g.cancelIdleCallback?g.cancelIdleCallback(handle):g.clearTimeout(handle);cb()}})},_increasePool:function(missingItems){var nextPhysicalCount=Math.min(this._physicalCount+missingItems,this._virtualCount-this._virtualStart,Math.max(this.maxPhysicalCount,DEFAULT_PHYSICAL_COUNT));var prevPhysicalCount=this._physicalCount;var delta=nextPhysicalCount-prevPhysicalCount;var ts=window.performance.now();if(delta<=0){return}[].push.apply(this._physicalItems,this._createPool(delta));[].push.apply(this._physicalSizes,new Array(delta));this._physicalCount=prevPhysicalCount+delta;if(this._physicalStart>this._physicalEnd&&this._isIndexRendered(this._focusedIndex)&&this._getPhysicalIndex(this._focusedIndex)<this._physicalEnd){this._physicalStart=this._physicalStart+delta}this._update();this._templateCost=(window.performance.now()-ts)/delta},_render:function(){if(this.isAttached&&this._isVisible){if(this._physicalCount===0){this._increasePool(DEFAULT_PHYSICAL_COUNT)}else{this._update()}}},_ensureTemplatized:function(){if(!this.ctor){var props={};props.__key__=true;props[this.as]=true;props[this.indexAs]=true;props[this.selectedAs]=true;props.tabIndex=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")}}},_getStampedChildren:function(){return this._physicalItems},_forwardInstancePath:function(inst,path,value){if(path.indexOf(this.as+".")===0){this.notifyPath("items."+inst.__key__+"."+path.slice(this.as.length+1),value)}},_forwardParentProp:function(prop,value){if(this._physicalItems){this._physicalItems.forEach(function(item){item._templateInstance[prop]=value},this)}},_forwardParentPath:function(path,value){if(this._physicalItems){this._physicalItems.forEach(function(item){item._templateInstance.notifyPath(path,value,true)},this)}},_forwardItemPath:function(path,value){if(!this._physicalIndexForKey){return}var dot=path.indexOf(".");var key=path.substring(0,dot<0?path.length:dot);var idx=this._physicalIndexForKey[key];var offscreenItem=this._offscreenFocusedItem;var el=offscreenItem&&offscreenItem._templateInstance.__key__===key?offscreenItem:this._physicalItems[idx];if(!el||el._templateInstance.__key__!==key){return}if(dot>=0){path=this.as+"."+path.substring(dot+1);el._templateInstance.notifyPath(path,value,true)}else{var currentItem=el._templateInstance[this.as];if(Array.isArray(this.selectedItems)){for(var i=0;i<this.selectedItems.length;i++){if(this.selectedItems[i]===currentItem){this.set("selectedItems."+i,value);break}}}else if(this.selectedItem===currentItem){this.set("selectedItem",value)}el._templateInstance[this.as]=value}},_itemsChanged:function(change){if(change.path==="items"){this._virtualStart=0;this._physicalTop=0;this._virtualCount=this.items?this.items.length:0;this._collection=this.items?Polymer.Collection.get(this.items):null;this._physicalIndexForKey={};this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null;this._physicalCount=this._physicalCount||0;this._physicalItems=this._physicalItems||[];this._physicalSizes=this._physicalSizes||[];this._physicalStart=0;this._resetScrollPosition(0);this._removeFocusedItem();this._debounceTemplate(this._render)}else if(change.path==="items.splices"){this._adjustVirtualIndex(change.value.indexSplices);this._virtualCount=this.items?this.items.length:0;this._debounceTemplate(this._render)}else{this._forwardItemPath(change.path.split(".").slice(1).join("."),change.value)}},_adjustVirtualIndex:function(splices){splices.forEach(function(splice){splice.removed.forEach(this._removeItem,this);if(splice.index<this._virtualStart){var delta=Math.max(splice.addedCount-splice.removed.length,splice.index-this._virtualStart);this._virtualStart=this._virtualStart+delta;if(this._focusedIndex>=0){this._focusedIndex=this._focusedIndex+delta}}},this)},_removeItem:function(item){this.$.selector.deselect(item);if(this._focusedItem&&this._focusedItem._templateInstance[this.as]===item){this._removeFocusedItem()}},_iterateItems:function(fn,itemSet){var pidx,vidx,rtn,i;if(arguments.length===2&&itemSet){for(i=0;i<itemSet.length;i++){pidx=itemSet[i];vidx=this._computeVidx(pidx);if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}}else{pidx=this._physicalStart;vidx=this._virtualStart;for(;pidx<this._physicalCount;pidx++,vidx++){if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}for(pidx=0;pidx<this._physicalStart;pidx++,vidx++){if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}}},_computeVidx:function(pidx){if(pidx>=this._physicalStart){return this._virtualStart+(pidx-this._physicalStart)}return this._virtualStart+(this._physicalCount-this._physicalStart)+pidx},_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!=null){inst[this.as]=item;inst.__key__=this._collection.getKey(item);inst[this.selectedAs]=this.$.selector.isSelected(item);inst[this.indexAs]=vidx;inst.tabIndex=this._focusedIndex===vidx?0:-1;this._physicalIndexForKey[inst.__key__]=pidx;el.removeAttribute("hidden")}else{inst.__key__=null;el.setAttribute("hidden","")}},itemSet)},_updateMetrics:function(itemSet){Polymer.dom.flush();var newPhysicalSize=0;var oldPhysicalSize=0;var prevAvgCount=this._physicalAverageCount;var prevPhysicalAvg=this._physicalAverage;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._viewportHeight=this._scrollTargetHeight;if(this.grid){this._updateGridMetrics();this._physicalSize=Math.ceil(this._physicalCount/this._itemsPerRow)*this._rowHeight}else{this._physicalSize=this._physicalSize+newPhysicalSize-oldPhysicalSize}if(this._physicalAverageCount!==prevAvgCount){this._physicalAverage=Math.round((prevPhysicalAvg*prevAvgCount+newPhysicalSize)/this._physicalAverageCount)}},_updateGridMetrics:function(){this._viewportWidth=this.$.items.offsetWidth;this._itemWidth=this._physicalCount>0?this._physicalItems[0].getBoundingClientRect().width:DEFAULT_GRID_SIZE;this._rowHeight=this._physicalCount>0?this._physicalItems[0].offsetHeight:DEFAULT_GRID_SIZE;this._itemsPerRow=this._itemWidth?Math.floor(this._viewportWidth/this._itemWidth):this._itemsPerRow},_positionItems:function(){this._adjustScrollPosition();var y=this._physicalTop;if(this.grid){var totalItemWidth=this._itemsPerRow*this._itemWidth;var rowOffset=(this._viewportWidth-totalItemWidth)/2;this._iterateItems(function(pidx,vidx){var modulus=vidx%this._itemsPerRow;var x=Math.floor(modulus*this._itemWidth+rowOffset);this.translate3d(x+"px",y+"px",0,this._physicalItems[pidx]);if(this._shouldRenderNextRow(vidx)){y+=this._rowHeight}})}else{this._iterateItems(function(pidx,vidx){this.translate3d(0,y+"px",0,this._physicalItems[pidx]);y+=this._physicalSizes[pidx]})}},_getPhysicalSizeIncrement:function(pidx){if(!this.grid){return this._physicalSizes[pidx]}if(this._computeVidx(pidx)%this._itemsPerRow!==this._itemsPerRow-1){return 0}return this._rowHeight},_shouldRenderNextRow:function(vidx){return vidx%this._itemsPerRow===this._itemsPerRow-1},_adjustScrollPosition:function(){var deltaHeight=this._virtualStart===0?this._physicalTop:Math.min(this._scrollPosition+this._physicalTop,0);if(deltaHeight){this._physicalTop=this._physicalTop-deltaHeight;if(!IOS_TOUCH_SCROLLING&&this._physicalTop!==0){this._resetScrollPosition(this._scrollTop-deltaHeight)}}},_resetScrollPosition:function(pos){if(this.scrollTarget){this._scrollTop=pos;this._scrollPosition=this._scrollTop}},_updateScrollerSize:function(forceUpdate){if(this.grid){this._estScrollHeight=this._virtualRowCount*this._rowHeight}else{this._estScrollHeight=this._physicalBottom+Math.max(this._virtualCount-this._physicalCount-this._virtualStart,0)*this._physicalAverage}forceUpdate=forceUpdate||this._scrollHeight===0;forceUpdate=forceUpdate||this._scrollPosition>=this._estScrollHeight-this._physicalSize;forceUpdate=forceUpdate||this.grid&&this.$.items.style.height<this._estScrollHeight;if(forceUpdate||Math.abs(this._estScrollHeight-this._scrollHeight)>=this._optPhysicalSize){this.$.items.style.height=this._estScrollHeight+"px";this._scrollHeight=this._estScrollHeight}},scrollToItem:function(item){return this.scrollToIndex(this.items.indexOf(item))},scrollToIndex:function(idx){if(typeof idx!=="number"||idx<0||idx>this.items.length-1){return}Polymer.dom.flush();if(this._physicalCount===0){return}idx=Math.min(Math.max(idx,0),this._virtualCount-1);if(!this._isIndexRendered(idx)||idx>=this._maxVirtualStart){this._virtualStart=this.grid?idx-this._itemsPerRow*2:idx-1}this._manageFocus();this._assignModels();this._updateMetrics();this._physicalTop=Math.floor(this._virtualStart/this._itemsPerRow)*this._physicalAverage;var currentTopItem=this._physicalStart;var currentVirtualItem=this._virtualStart;var targetOffsetTop=0;var hiddenContentSize=this._hiddenContentSize;while(currentVirtualItem<idx&&targetOffsetTop<=hiddenContentSize){targetOffsetTop=targetOffsetTop+this._getPhysicalSizeIncrement(currentTopItem);currentTopItem=(currentTopItem+1)%this._physicalCount;currentVirtualItem++}this._updateScrollerSize(true);this._positionItems();this._resetScrollPosition(this._physicalTop+this._scrollerPaddingTop+targetOffsetTop);this._increasePoolIfNeeded();this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null},_resetAverage:function(){this._physicalAverage=0;this._physicalAverageCount=0},_resizeHandler:function(){if(IOS&&Math.abs(this._viewportHeight-this._scrollTargetHeight)<100){return}Polymer.dom.addDebouncer(this.debounce("_debounceTemplate",function(){this.updateViewportBoundaries();this._render();if(this._physicalCount>0&&this._isVisible){this._resetAverage();this.scrollToIndex(this.firstVisibleIndex)}}.bind(this),1))},_getModelFromItem:function(item){var key=this._collection.getKey(item);var pidx=this._physicalIndexForKey[key];if(pidx!=null){return this._physicalItems[pidx]._templateInstance}return null},_getNormalizedItem:function(item){if(this._collection.getKey(item)===undefined){if(typeof item==="number"){item=this.items[item];if(!item){throw new RangeError("<item> not found")}return item}throw new TypeError("<item> should be a valid item")}return item},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);this.updateSizeForItem(item)},deselectItem:function(item){item=this._getNormalizedItem(item);var model=this._getModelFromItem(item);if(model){model[this.selectedAs]=false}this.$.selector.deselect(item);this.updateSizeForItem(item)},toggleSelectionForItem:function(item){item=this._getNormalizedItem(item);if(this.$.selector.isSelected(item)){this.deselectItem(item)}else{this.selectItem(item)}},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)}this.$.selector.clearSelection()},_selectionEnabledChanged:function(selectionEnabled){var handler=selectionEnabled?this.listen:this.unlisten;handler.call(this,this,"tap","_selectionHandler")},_selectionHandler:function(e){var model=this.modelForElement(e.target);if(!model){return}var modelTabIndex,activeElTabIndex;var target=Polymer.dom(e).path[0];var activeEl=Polymer.dom(this.domHost?this.domHost.root:document).activeElement;var physicalItem=this._physicalItems[this._getPhysicalIndex(model[this.indexAs])];if(target.localName==="input"||target.localName==="button"||target.localName==="select"){return}modelTabIndex=model.tabIndex;model.tabIndex=SECRET_TABINDEX;activeElTabIndex=activeEl?activeEl.tabIndex:-1;model.tabIndex=modelTabIndex;if(activeEl&&physicalItem!==activeEl&&physicalItem.contains(activeEl)&&activeElTabIndex!==SECRET_TABINDEX){return}this.toggleSelectionForItem(model[this.as])},_multiSelectionChanged:function(multiSelection){this.clearSelection();this.$.selector.multi=multiSelection},updateSizeForItem:function(item){item=this._getNormalizedItem(item);var key=this._collection.getKey(item);var pidx=this._physicalIndexForKey[key];if(pidx!=null){this._updateMetrics([pidx]);this._positionItems()}},_manageFocus:function(){var fidx=this._focusedIndex;if(fidx>=0&&fidx<this._virtualCount){if(this._isIndexRendered(fidx)){this._restoreFocusedItem(); -(function() { - 'use strict'; - var KEY_IDENTIFIER = { - 'U+0008': 'backspace', - 'U+0009': 'tab', - 'U+001B': 'esc', - 'U+0020': 'space', - 'U+007F': 'del' - }; - var KEY_CODE = { - 8: 'backspace', - 9: 'tab', - 13: 'enter', - 27: 'esc', - 33: 'pageup', - 34: 'pagedown', - 35: 'end', - 36: 'home', - 32: 'space', - 37: 'left', - 38: 'up', - 39: 'right', - 40: 'down', - 46: 'del', - 106: '*' - }; - var MODIFIER_KEYS = { - shift: 'shiftKey', - ctrl: 'ctrlKey', - alt: 'altKey', - meta: 'metaKey' - }; - var KEY_CHAR = /[a-z0-9*]/; - var IDENT_CHAR = /U\+/; - var ARROW_KEY = /^arrow/; - var SPACE_KEY = /^space(bar)?/; - var ESC_KEY = /^escape$/; - function transformKey(key, noSpecialChars) { - var validKey = ''; - if (key) { - var lKey = key.toLowerCase(); - if (lKey === ' ' || SPACE_KEY.test(lKey)) { - validKey = 'space'; - } else if (ESC_KEY.test(lKey)) { - validKey = 'esc'; - } else if (lKey.length == 1) { - if (!noSpecialChars || KEY_CHAR.test(lKey)) { - validKey = lKey; - } - } else if (ARROW_KEY.test(lKey)) { - validKey = lKey.replace('arrow', ''); - } else if (lKey == 'multiply') { - validKey = '*'; - } else { - validKey = lKey; - } - } - return validKey; - } - function transformKeyIdentifier(keyIdent) { - var validKey = ''; - if (keyIdent) { - if (keyIdent in KEY_IDENTIFIER) { - validKey = KEY_IDENTIFIER[keyIdent]; - } else if (IDENT_CHAR.test(keyIdent)) { - keyIdent = parseInt(keyIdent.replace('U+', '0x'), 16); - validKey = String.fromCharCode(keyIdent).toLowerCase(); - } else { - validKey = keyIdent.toLowerCase(); - } - } - return validKey; - } - function transformKeyCode(keyCode) { - var validKey = ''; - if (Number(keyCode)) { - if (keyCode >= 65 && keyCode <= 90) { - validKey = String.fromCharCode(32 + keyCode); - } else if (keyCode >= 112 && keyCode <= 123) { - validKey = 'f' + (keyCode - 112); - } else if (keyCode >= 48 && keyCode <= 57) { - validKey = String(keyCode - 48); - } else if (keyCode >= 96 && keyCode <= 105) { - validKey = String(keyCode - 96); - } else { - validKey = KEY_CODE[keyCode]; - } - } - return validKey; - } - function normalizedKeyForEvent(keyEvent, noSpecialChars) { - if (keyEvent.key) { - return transformKey(keyEvent.key, noSpecialChars); - } - if (keyEvent.detail && keyEvent.detail.key) { - return transformKey(keyEvent.detail.key, noSpecialChars); - } - return transformKeyIdentifier(keyEvent.keyIdentifier) || transformKeyCode(keyEvent.keyCode) || ''; - } - function keyComboMatchesEvent(keyCombo, event) { - var keyEvent = normalizedKeyForEvent(event, keyCombo.hasModifiers); - return keyEvent === keyCombo.key && (!keyCombo.hasModifiers || !!event.shiftKey === !!keyCombo.shiftKey && !!event.ctrlKey === !!keyCombo.ctrlKey && !!event.altKey === !!keyCombo.altKey && !!event.metaKey === !!keyCombo.metaKey); - } - function parseKeyComboString(keyComboString) { - if (keyComboString.length === 1) { - return { - combo: keyComboString, - key: keyComboString, - event: 'keydown' - }; - } - return keyComboString.split('+').reduce(function(parsedKeyCombo, keyComboPart) { - var eventParts = keyComboPart.split(':'); - var keyName = eventParts[0]; - var event = eventParts[1]; - if (keyName in MODIFIER_KEYS) { - parsedKeyCombo[MODIFIER_KEYS[keyName]] = true; - parsedKeyCombo.hasModifiers = true; - } else { - parsedKeyCombo.key = keyName; - parsedKeyCombo.event = event || 'keydown'; - } - return parsedKeyCombo; - }, { - combo: keyComboString.split(':').shift() - }); - } - function parseEventString(eventString) { - return eventString.trim().split(' ').map(function(keyComboString) { - return parseKeyComboString(keyComboString); - }); - } - Polymer.IronA11yKeysBehavior = { - properties: { - keyEventTarget: { - type: Object, - value: function() { - return this; - } - }, - stopKeyboardEventPropagation: { - type: Boolean, - value: false - }, - _boundKeyHandlers: { - type: Array, - value: function() { - return []; - } - }, - _imperativeKeyBindings: { - type: Object, - value: function() { - return {}; - } - } - }, - observers: [ '_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)' ], - keyBindings: {}, - registered: function() { - this._prepKeyBindings(); - }, - attached: function() { - this._listenKeyEventListeners(); - }, - detached: function() { - this._unlistenKeyEventListeners(); - }, - addOwnKeyBinding: function(eventString, handlerName) { - this._imperativeKeyBindings[eventString] = handlerName; - this._prepKeyBindings(); - this._resetKeyEventListeners(); - }, - removeOwnKeyBindings: function() { - this._imperativeKeyBindings = {}; - this._prepKeyBindings(); - this._resetKeyEventListeners(); - }, - keyboardEventMatchesKeys: function(event, eventString) { - var keyCombos = parseEventString(eventString); - for (var i = 0; i < keyCombos.length; ++i) { - if (keyComboMatchesEvent(keyCombos[i], event)) { - return true; - } - } - return false; - }, - _collectKeyBindings: function() { - var keyBindings = this.behaviors.map(function(behavior) { - return behavior.keyBindings; - }); - if (keyBindings.indexOf(this.keyBindings) === -1) { - keyBindings.push(this.keyBindings); - } - return keyBindings; - }, - _prepKeyBindings: function() { - this._keyBindings = {}; - this._collectKeyBindings().forEach(function(keyBindings) { - for (var eventString in keyBindings) { - this._addKeyBinding(eventString, keyBindings[eventString]); - } - }, this); - for (var eventString in this._imperativeKeyBindings) { - this._addKeyBinding(eventString, this._imperativeKeyBindings[eventString]); - } - for (var eventName in this._keyBindings) { - this._keyBindings[eventName].sort(function(kb1, kb2) { - var b1 = kb1[0].hasModifiers; - var b2 = kb2[0].hasModifiers; - return b1 === b2 ? 0 : b1 ? -1 : 1; - }); - } - }, - _addKeyBinding: function(eventString, handlerName) { - parseEventString(eventString).forEach(function(keyCombo) { - this._keyBindings[keyCombo.event] = this._keyBindings[keyCombo.event] || []; - this._keyBindings[keyCombo.event].push([ keyCombo, handlerName ]); - }, this); - }, - _resetKeyEventListeners: function() { - this._unlistenKeyEventListeners(); - if (this.isAttached) { - this._listenKeyEventListeners(); - } - }, - _listenKeyEventListeners: function() { - if (!this.keyEventTarget) { - return; - } - Object.keys(this._keyBindings).forEach(function(eventName) { - var keyBindings = this._keyBindings[eventName]; - var boundKeyHandler = this._onKeyBindingEvent.bind(this, keyBindings); - this._boundKeyHandlers.push([ this.keyEventTarget, eventName, boundKeyHandler ]); - this.keyEventTarget.addEventListener(eventName, boundKeyHandler); - }, this); - }, - _unlistenKeyEventListeners: function() { - var keyHandlerTuple; - var keyEventTarget; - var eventName; - var boundKeyHandler; - while (this._boundKeyHandlers.length) { - keyHandlerTuple = this._boundKeyHandlers.pop(); - keyEventTarget = keyHandlerTuple[0]; - eventName = keyHandlerTuple[1]; - boundKeyHandler = keyHandlerTuple[2]; - keyEventTarget.removeEventListener(eventName, boundKeyHandler); - } - }, - _onKeyBindingEvent: function(keyBindings, event) { - if (this.stopKeyboardEventPropagation) { - event.stopPropagation(); - } - if (event.defaultPrevented) { - return; - } - for (var i = 0; i < keyBindings.length; i++) { - var keyCombo = keyBindings[i][0]; - var handlerName = keyBindings[i][1]; - if (keyComboMatchesEvent(keyCombo, event)) { - this._triggerKeyHandler(keyCombo, handlerName, event); - if (event.defaultPrevented) { - return; - } - } - } - }, - _triggerKeyHandler: function(keyCombo, handlerName, keyboardEvent) { - var detail = Object.create(keyCombo); - detail.keyboardEvent = keyboardEvent; - var event = new CustomEvent(keyCombo.event, { - detail: detail, - cancelable: true - }); - this[handlerName].call(this, event); - if (event.defaultPrevented) { - keyboardEvent.preventDefault(); - } - } - }; -})(); - -Polymer.IronScrollTargetBehavior = { - properties: { - scrollTarget: { - type: HTMLElement, - value: function() { - return this._defaultScrollTarget; - } - } - }, - observers: [ '_scrollTargetChanged(scrollTarget, isAttached)' ], - _scrollTargetChanged: function(scrollTarget, isAttached) { - var eventTarget; - if (this._oldScrollTarget) { - eventTarget = this._oldScrollTarget === this._doc ? window : this._oldScrollTarget; - eventTarget.removeEventListener('scroll', this._boundScrollHandler); - this._oldScrollTarget = null; - } - if (!isAttached) { - return; - } - if (scrollTarget === 'document') { - this.scrollTarget = this._doc; - } else if (typeof scrollTarget === 'string') { - this.scrollTarget = this.domHost ? this.domHost.$[scrollTarget] : Polymer.dom(this.ownerDocument).querySelector('#' + scrollTarget); - } else if (this._isValidScrollTarget()) { - eventTarget = scrollTarget === this._doc ? window : scrollTarget; - this._boundScrollHandler = this._boundScrollHandler || this._scrollHandler.bind(this); - this._oldScrollTarget = scrollTarget; - eventTarget.addEventListener('scroll', this._boundScrollHandler); - } - }, - _scrollHandler: function scrollHandler() {}, - get _defaultScrollTarget() { - return this._doc; - }, - get _doc() { - return this.ownerDocument.documentElement; - }, - get _scrollTop() { - if (this._isValidScrollTarget()) { - return this.scrollTarget === this._doc ? window.pageYOffset : this.scrollTarget.scrollTop; - } - return 0; - }, - get _scrollLeft() { - if (this._isValidScrollTarget()) { - return this.scrollTarget === this._doc ? window.pageXOffset : this.scrollTarget.scrollLeft; - } - return 0; - }, - set _scrollTop(top) { - if (this.scrollTarget === this._doc) { - window.scrollTo(window.pageXOffset, top); - } else if (this._isValidScrollTarget()) { - this.scrollTarget.scrollTop = top; - } - }, - set _scrollLeft(left) { - if (this.scrollTarget === this._doc) { - window.scrollTo(left, window.pageYOffset); - } else if (this._isValidScrollTarget()) { - this.scrollTarget.scrollLeft = left; - } - }, - scroll: function(left, top) { - if (this.scrollTarget === this._doc) { - window.scrollTo(left, top); - } else if (this._isValidScrollTarget()) { - this.scrollTarget.scrollLeft = left; - this.scrollTarget.scrollTop = top; - } - }, - get _scrollTargetWidth() { - if (this._isValidScrollTarget()) { - return this.scrollTarget === this._doc ? window.innerWidth : this.scrollTarget.offsetWidth; - } - return 0; - }, - get _scrollTargetHeight() { - if (this._isValidScrollTarget()) { - return this.scrollTarget === this._doc ? window.innerHeight : this.scrollTarget.offsetHeight; - } - return 0; - }, - _isValidScrollTarget: function() { - return this.scrollTarget instanceof HTMLElement; - } -}; - -(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 = 3; - var HIDDEN_Y = '-10000px'; - var DEFAULT_GRID_SIZE = 200; - var SECRET_TABINDEX = -100; - Polymer({ - is: 'iron-list', - properties: { - items: { - type: Array - }, - maxPhysicalCount: { - type: Number, - value: 500 - }, - as: { - type: String, - value: 'item' - }, - indexAs: { - type: String, - value: 'index' - }, - selectedAs: { - type: String, - value: 'selected' - }, - grid: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - selectionEnabled: { - type: Boolean, - value: false - }, - selectedItem: { - type: Object, - notify: true - }, - selectedItems: { - type: Object, - notify: true - }, - multiSelection: { - type: Boolean, - value: false - } - }, - observers: [ '_itemsChanged(items.*)', '_selectionEnabledChanged(selectionEnabled)', '_multiSelectionChanged(multiSelection)', '_setOverflow(scrollTarget)' ], - behaviors: [ Polymer.Templatizer, Polymer.IronResizableBehavior, Polymer.IronA11yKeysBehavior, Polymer.IronScrollTargetBehavior ], - keyBindings: { - up: '_didMoveUp', - down: '_didMoveDown', - enter: '_didEnter' - }, - _ratio: .5, - _scrollerPaddingTop: 0, - _scrollPosition: 0, - _physicalSize: 0, - _physicalAverage: 0, - _physicalAverageCount: 0, - _physicalTop: 0, - _virtualCount: 0, - _physicalIndexForKey: null, - _estScrollHeight: 0, - _scrollHeight: 0, - _viewportHeight: 0, - _viewportWidth: 0, - _physicalItems: null, - _physicalSizes: null, - _firstVisibleIndexVal: null, - _lastVisibleIndexVal: null, - _collection: null, - _maxPages: 3, - _focusedItem: null, - _focusedIndex: -1, - _offscreenFocusedItem: null, - _focusBackfillItem: null, - _itemsPerRow: 1, - _itemWidth: 0, - _rowHeight: 0, - _templateCost: 0, - get _physicalBottom() { - return this._physicalTop + this._physicalSize; - }, - get _scrollBottom() { - return this._scrollPosition + this._viewportHeight; - }, - get _virtualEnd() { - return this._virtualStart + this._physicalCount - 1; - }, - get _hiddenContentSize() { - var size = this.grid ? this._physicalRows * this._rowHeight : this._physicalSize; - return size - this._viewportHeight; - }, - get _maxScrollTop() { - return this._estScrollHeight - this._viewportHeight + this._scrollerPaddingTop; - }, - _minVirtualStart: 0, - get _maxVirtualStart() { - return Math.max(0, this._virtualCount - this._physicalCount); - }, - _virtualStartVal: 0, - set _virtualStart(val) { - this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val)); - }, - get _virtualStart() { - return this._virtualStartVal || 0; - }, - _physicalStartVal: 0, - set _physicalStart(val) { - this._physicalStartVal = val % this._physicalCount; - if (this._physicalStartVal < 0) { - this._physicalStartVal = this._physicalCount + this._physicalStartVal; - } - this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount; - }, - get _physicalStart() { - return this._physicalStartVal || 0; - }, - _physicalCountVal: 0, - set _physicalCount(val) { - this._physicalCountVal = val; - this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount; - }, - get _physicalCount() { - return this._physicalCountVal; - }, - _physicalEnd: 0, - get _optPhysicalSize() { - if (this.grid) { - return this._estRowsInView * this._rowHeight * this._maxPages; - } - return this._viewportHeight * this._maxPages; - }, - get _optPhysicalCount() { - return this._estRowsInView * this._itemsPerRow * this._maxPages; - }, - get _isVisible() { - return Boolean(this.offsetWidth || this.offsetHeight); - }, - get firstVisibleIndex() { - if (this._firstVisibleIndexVal === null) { - var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddingTop); - this._firstVisibleIndexVal = this._iterateItems(function(pidx, vidx) { - physicalOffset += this._getPhysicalSizeIncrement(pidx); - if (physicalOffset > this._scrollPosition) { - return this.grid ? vidx - vidx % this._itemsPerRow : vidx; - } - if (this.grid && this._virtualCount - 1 === vidx) { - return vidx - vidx % this._itemsPerRow; - } - }) || 0; - } - return this._firstVisibleIndexVal; - }, - get lastVisibleIndex() { - if (this._lastVisibleIndexVal === null) { - if (this.grid) { - var lastIndex = this.firstVisibleIndex + this._estRowsInView * this._itemsPerRow - 1; - this._lastVisibleIndexVal = Math.min(this._virtualCount, lastIndex); - } else { - var physicalOffset = this._physicalTop; - this._iterateItems(function(pidx, vidx) { - if (physicalOffset < this._scrollBottom) { - this._lastVisibleIndexVal = vidx; - } else { - return true; - } - physicalOffset += this._getPhysicalSizeIncrement(pidx); - }); - } - } - return this._lastVisibleIndexVal; - }, - get _defaultScrollTarget() { - return this; - }, - get _virtualRowCount() { - return Math.ceil(this._virtualCount / this._itemsPerRow); - }, - get _estRowsInView() { - return Math.ceil(this._viewportHeight / this._rowHeight); - }, - get _physicalRows() { - return Math.ceil(this._physicalCount / this._itemsPerRow); - }, - ready: function() { - this.addEventListener('focus', this._didFocus.bind(this), true); - }, - attached: function() { - this.updateViewportBoundaries(); - if (this._physicalCount === 0) { - this._debounceTemplate(this._render); - } - this.listen(this, 'iron-resize', '_resizeHandler'); - }, - detached: function() { - this.unlisten(this, 'iron-resize', '_resizeHandler'); - }, - _setOverflow: function(scrollTarget) { - this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; - this.style.overflow = scrollTarget === this ? 'auto' : ''; - }, - updateViewportBoundaries: function() { - this._scrollerPaddingTop = this.scrollTarget === this ? 0 : parseInt(window.getComputedStyle(this)['padding-top'], 10); - this._viewportHeight = this._scrollTargetHeight; - if (this.grid) { - this._updateGridMetrics(); - } - }, - _scrollHandler: function() { - var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop)); - var delta = scrollTop - this._scrollPosition; - var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBottom; - var ratio = this._ratio; - var recycledTiles = 0; - var hiddenContentSize = this._hiddenContentSize; - var currentRatio = ratio; - var movingUp = []; - this._scrollPosition = scrollTop; - this._firstVisibleIndexVal = null; - this._lastVisibleIndexVal = null; - scrollBottom = this._scrollBottom; - physicalBottom = this._physicalBottom; - if (Math.abs(delta) > this._physicalSize) { - this._physicalTop += delta; - recycledTiles = Math.round(delta / this._physicalAverage); - } else if (delta < 0) { - var topSpace = scrollTop - this._physicalTop; - var virtualStart = this._virtualStart; - recycledTileSet = []; - kth = this._physicalEnd; - currentRatio = topSpace / hiddenContentSize; - while (currentRatio < ratio && recycledTiles < this._physicalCount && virtualStart - recycledTiles > 0 && physicalBottom - this._getPhysicalSizeIncrement(kth) > scrollBottom) { - tileHeight = this._getPhysicalSizeIncrement(kth); - currentRatio += tileHeight / hiddenContentSize; - physicalBottom -= tileHeight; - recycledTileSet.push(kth); - recycledTiles++; - kth = kth === 0 ? this._physicalCount - 1 : kth - 1; - } - movingUp = recycledTileSet; - recycledTiles = -recycledTiles; - } else if (delta > 0) { - var bottomSpace = physicalBottom - scrollBottom; - var virtualEnd = this._virtualEnd; - var lastVirtualItemIndex = this._virtualCount - 1; - recycledTileSet = []; - kth = this._physicalStart; - currentRatio = bottomSpace / hiddenContentSize; - while (currentRatio < ratio && recycledTiles < this._physicalCount && virtualEnd + recycledTiles < lastVirtualItemIndex && this._physicalTop + this._getPhysicalSizeIncrement(kth) < scrollTop) { - tileHeight = this._getPhysicalSizeIncrement(kth); - currentRatio += tileHeight / hiddenContentSize; - this._physicalTop += tileHeight; - recycledTileSet.push(kth); - recycledTiles++; - kth = (kth + 1) % this._physicalCount; - } - } - if (recycledTiles === 0) { - if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { - this._increasePoolIfNeeded(); - } - } else { - this._virtualStart = this._virtualStart + recycledTiles; - this._physicalStart = this._physicalStart + recycledTiles; - this._update(recycledTileSet, movingUp); - } - }, - _update: function(itemSet, movingUp) { - this._manageFocus(); - this._assignModels(itemSet); - this._updateMetrics(itemSet); - if (movingUp) { - while (movingUp.length) { - var idx = movingUp.pop(); - this._physicalTop -= this._getPhysicalSizeIncrement(idx); - } - } - this._positionItems(); - this._updateScrollerSize(); - this._increasePoolIfNeeded(); - }, - _createPool: function(size) { - var physicalItems = new Array(size); - this._ensureTemplatized(); - for (var i = 0; i < size; i++) { - var inst = this.stamp(null); - physicalItems[i] = inst.root.querySelector('*'); - Polymer.dom(this).appendChild(inst.root); - } - return physicalItems; - }, - _increasePoolIfNeeded: function() { - if (this._viewportHeight === 0) { - return false; - } - var self = this; - var isClientFull = this._physicalBottom >= this._scrollBottom && this._physicalTop <= this._scrollPosition; - if (this._physicalSize >= this._optPhysicalSize && isClientFull) { - return false; - } - var maxPoolSize = Math.round(this._physicalCount * .5); - if (!isClientFull) { - this._debounceTemplate(this._increasePool.bind(this, maxPoolSize)); - return true; - } - this._yield(function() { - self._increasePool(Math.min(maxPoolSize, Math.max(1, Math.round(50 / self._templateCost)))); - }); - return true; - }, - _yield: function(cb) { - var g = window; - var handle = g.requestIdleCallback ? g.requestIdleCallback(cb) : g.setTimeout(cb, 16); - Polymer.dom.addDebouncer({ - complete: function() { - g.cancelIdleCallback ? g.cancelIdleCallback(handle) : g.clearTimeout(handle); - cb(); - } - }); - }, - _increasePool: function(missingItems) { - var nextPhysicalCount = Math.min(this._physicalCount + missingItems, this._virtualCount - this._virtualStart, Math.max(this.maxPhysicalCount, DEFAULT_PHYSICAL_COUNT)); - var prevPhysicalCount = this._physicalCount; - var delta = nextPhysicalCount - prevPhysicalCount; - var ts = window.performance.now(); - if (delta <= 0) { - return; - } - [].push.apply(this._physicalItems, this._createPool(delta)); - [].push.apply(this._physicalSizes, new Array(delta)); - this._physicalCount = prevPhysicalCount + delta; - if (this._physicalStart > this._physicalEnd && this._isIndexRendered(this._focusedIndex) && this._getPhysicalIndex(this._focusedIndex) < this._physicalEnd) { - this._physicalStart = this._physicalStart + delta; - } - this._update(); - this._templateCost = (window.performance.now() - ts) / delta; - }, - _render: function() { - if (this.isAttached && this._isVisible) { - if (this._physicalCount === 0) { - this._increasePool(DEFAULT_PHYSICAL_COUNT); - } else { - this._update(); - } - } - }, - _ensureTemplatized: function() { - if (!this.ctor) { - var props = {}; - props.__key__ = true; - props[this.as] = true; - props[this.indexAs] = true; - props[this.selectedAs] = true; - props.tabIndex = 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'); - } - } - }, - _getStampedChildren: function() { - return this._physicalItems; - }, - _forwardInstancePath: function(inst, path, value) { - if (path.indexOf(this.as + '.') === 0) { - this.notifyPath('items.' + inst.__key__ + '.' + path.slice(this.as.length + 1), value); - } - }, - _forwardParentProp: function(prop, value) { - if (this._physicalItems) { - this._physicalItems.forEach(function(item) { - item._templateInstance[prop] = value; - }, this); - } - }, - _forwardParentPath: function(path, value) { - if (this._physicalItems) { - this._physicalItems.forEach(function(item) { - item._templateInstance.notifyPath(path, value, true); - }, this); - } - }, - _forwardItemPath: function(path, value) { - if (!this._physicalIndexForKey) { - return; - } - var dot = path.indexOf('.'); - var key = path.substring(0, dot < 0 ? path.length : dot); - var idx = this._physicalIndexForKey[key]; - var offscreenItem = this._offscreenFocusedItem; - var el = offscreenItem && offscreenItem._templateInstance.__key__ === key ? offscreenItem : this._physicalItems[idx]; - if (!el || el._templateInstance.__key__ !== key) { - return; - } - if (dot >= 0) { - path = this.as + '.' + path.substring(dot + 1); - el._templateInstance.notifyPath(path, value, true); - } else { - var currentItem = el._templateInstance[this.as]; - if (Array.isArray(this.selectedItems)) { - for (var i = 0; i < this.selectedItems.length; i++) { - if (this.selectedItems[i] === currentItem) { - this.set('selectedItems.' + i, value); - break; - } - } - } else if (this.selectedItem === currentItem) { - this.set('selectedItem', value); - } - el._templateInstance[this.as] = value; - } - }, - _itemsChanged: function(change) { - if (change.path === 'items') { - this._virtualStart = 0; - this._physicalTop = 0; - this._virtualCount = this.items ? this.items.length : 0; - this._collection = this.items ? Polymer.Collection.get(this.items) : null; - this._physicalIndexForKey = {}; - this._firstVisibleIndexVal = null; - this._lastVisibleIndexVal = null; - this._physicalCount = this._physicalCount || 0; - this._physicalItems = this._physicalItems || []; - this._physicalSizes = this._physicalSizes || []; - this._physicalStart = 0; - this._resetScrollPosition(0); - this._removeFocusedItem(); - this._debounceTemplate(this._render); - } else if (change.path === 'items.splices') { - this._adjustVirtualIndex(change.value.indexSplices); - this._virtualCount = this.items ? this.items.length : 0; - this._debounceTemplate(this._render); - } else { - this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.value); - } - }, - _adjustVirtualIndex: function(splices) { - splices.forEach(function(splice) { - splice.removed.forEach(this._removeItem, this); - if (splice.index < this._virtualStart) { - var delta = Math.max(splice.addedCount - splice.removed.length, splice.index - this._virtualStart); - this._virtualStart = this._virtualStart + delta; - if (this._focusedIndex >= 0) { - this._focusedIndex = this._focusedIndex + delta; - } - } - }, this); - }, - _removeItem: function(item) { - this.$.selector.deselect(item); - if (this._focusedItem && this._focusedItem._templateInstance[this.as] === item) { - this._removeFocusedItem(); - } - }, - _iterateItems: function(fn, itemSet) { - var pidx, vidx, rtn, i; - if (arguments.length === 2 && itemSet) { - for (i = 0; i < itemSet.length; i++) { - pidx = itemSet[i]; - vidx = this._computeVidx(pidx); - if ((rtn = fn.call(this, pidx, vidx)) != null) { - return rtn; - } - } - } else { - pidx = this._physicalStart; - vidx = this._virtualStart; - for (;pidx < this._physicalCount; pidx++, vidx++) { - if ((rtn = fn.call(this, pidx, vidx)) != null) { - return rtn; - } - } - for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) { - if ((rtn = fn.call(this, pidx, vidx)) != null) { - return rtn; - } - } - } - }, - _computeVidx: function(pidx) { - if (pidx >= this._physicalStart) { - return this._virtualStart + (pidx - this._physicalStart); - } - return this._virtualStart + (this._physicalCount - this._physicalStart) + pidx; - }, - _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 != null) { - inst[this.as] = item; - inst.__key__ = this._collection.getKey(item); - inst[this.selectedAs] = this.$.selector.isSelected(item); - inst[this.indexAs] = vidx; - inst.tabIndex = this._focusedIndex === vidx ? 0 : -1; - this._physicalIndexForKey[inst.__key__] = pidx; - el.removeAttribute('hidden'); - } else { - inst.__key__ = null; - el.setAttribute('hidden', ''); - } - }, itemSet); - }, - _updateMetrics: function(itemSet) { - Polymer.dom.flush(); - var newPhysicalSize = 0; - var oldPhysicalSize = 0; - var prevAvgCount = this._physicalAverageCount; - var prevPhysicalAvg = this._physicalAverage; - 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._viewportHeight = this._scrollTargetHeight; - if (this.grid) { - this._updateGridMetrics(); - this._physicalSize = Math.ceil(this._physicalCount / this._itemsPerRow) * this._rowHeight; - } else { - this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize; - } - if (this._physicalAverageCount !== prevAvgCount) { - this._physicalAverage = Math.round((prevPhysicalAvg * prevAvgCount + newPhysicalSize) / this._physicalAverageCount); - } - }, - _updateGridMetrics: function() { - this._viewportWidth = this.$.items.offsetWidth; - this._itemWidth = this._physicalCount > 0 ? this._physicalItems[0].getBoundingClientRect().width : DEFAULT_GRID_SIZE; - this._rowHeight = this._physicalCount > 0 ? this._physicalItems[0].offsetHeight : DEFAULT_GRID_SIZE; - this._itemsPerRow = this._itemWidth ? Math.floor(this._viewportWidth / this._itemWidth) : this._itemsPerRow; - }, - _positionItems: function() { - this._adjustScrollPosition(); - var y = this._physicalTop; - if (this.grid) { - var totalItemWidth = this._itemsPerRow * this._itemWidth; - var rowOffset = (this._viewportWidth - totalItemWidth) / 2; - this._iterateItems(function(pidx, vidx) { - var modulus = vidx % this._itemsPerRow; - var x = Math.floor(modulus * this._itemWidth + rowOffset); - this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]); - if (this._shouldRenderNextRow(vidx)) { - y += this._rowHeight; - } - }); - } else { - this._iterateItems(function(pidx, vidx) { - this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); - y += this._physicalSizes[pidx]; - }); - } - }, - _getPhysicalSizeIncrement: function(pidx) { - if (!this.grid) { - return this._physicalSizes[pidx]; - } - if (this._computeVidx(pidx) % this._itemsPerRow !== this._itemsPerRow - 1) { - return 0; - } - return this._rowHeight; - }, - _shouldRenderNextRow: function(vidx) { - return vidx % this._itemsPerRow === this._itemsPerRow - 1; - }, - _adjustScrollPosition: function() { - var deltaHeight = this._virtualStart === 0 ? this._physicalTop : Math.min(this._scrollPosition + this._physicalTop, 0); - if (deltaHeight) { - this._physicalTop = this._physicalTop - deltaHeight; - if (!IOS_TOUCH_SCROLLING && this._physicalTop !== 0) { - this._resetScrollPosition(this._scrollTop - deltaHeight); - } - } - }, - _resetScrollPosition: function(pos) { - if (this.scrollTarget) { - this._scrollTop = pos; - this._scrollPosition = this._scrollTop; - } - }, - _updateScrollerSize: function(forceUpdate) { - if (this.grid) { - this._estScrollHeight = this._virtualRowCount * this._rowHeight; - } else { - this._estScrollHeight = this._physicalBottom + Math.max(this._virtualCount - this._physicalCount - this._virtualStart, 0) * this._physicalAverage; - } - forceUpdate = forceUpdate || this._scrollHeight === 0; - forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize; - forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this._estScrollHeight; - if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) { - this.$.items.style.height = this._estScrollHeight + 'px'; - this._scrollHeight = this._estScrollHeight; - } - }, - scrollToItem: function(item) { - return this.scrollToIndex(this.items.indexOf(item)); - }, - scrollToIndex: function(idx) { - if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) { - return; - } - Polymer.dom.flush(); - if (this._physicalCount === 0) { - return; - } - idx = Math.min(Math.max(idx, 0), this._virtualCount - 1); - if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { - this._virtualStart = this.grid ? idx - this._itemsPerRow * 2 : idx - 1; - } - this._manageFocus(); - this._assignModels(); - this._updateMetrics(); - this._physicalTop = Math.floor(this._virtualStart / this._itemsPerRow) * this._physicalAverage; - var currentTopItem = this._physicalStart; - var currentVirtualItem = this._virtualStart; - var targetOffsetTop = 0; - var hiddenContentSize = this._hiddenContentSize; - while (currentVirtualItem < idx && targetOffsetTop <= hiddenContentSize) { - targetOffsetTop = targetOffsetTop + this._getPhysicalSizeIncrement(currentTopItem); - currentTopItem = (currentTopItem + 1) % this._physicalCount; - currentVirtualItem++; - } - this._updateScrollerSize(true); - this._positionItems(); - this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + targetOffsetTop); - this._increasePoolIfNeeded(); - this._firstVisibleIndexVal = null; - this._lastVisibleIndexVal = null; - }, - _resetAverage: function() { - this._physicalAverage = 0; - this._physicalAverageCount = 0; - }, - _resizeHandler: function() { - if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100) { - return; - } - Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() { - this.updateViewportBoundaries(); - this._render(); - if (this._physicalCount > 0 && this._isVisible) { - this._resetAverage(); - this.scrollToIndex(this.firstVisibleIndex); - } - }.bind(this), 1)); - }, - _getModelFromItem: function(item) { - var key = this._collection.getKey(item); - var pidx = this._physicalIndexForKey[key]; - if (pidx != null) { - return this._physicalItems[pidx]._templateInstance; - } - return null; - }, - _getNormalizedItem: function(item) { - if (this._collection.getKey(item) === undefined) { - if (typeof item === 'number') { - item = this.items[item]; - if (!item) { - throw new RangeError('<item> not found'); - } - return item; - } - throw new TypeError('<item> should be a valid item'); - } - return item; - }, - 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); - this.updateSizeForItem(item); - }, - deselectItem: function(item) { - item = this._getNormalizedItem(item); - var model = this._getModelFromItem(item); - if (model) { - model[this.selectedAs] = false; - } - this.$.selector.deselect(item); - this.updateSizeForItem(item); - }, - toggleSelectionForItem: function(item) { - item = this._getNormalizedItem(item); - if (this.$.selector.isSelected(item)) { - this.deselectItem(item); - } else { - this.selectItem(item); - } - }, - 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); - } - this.$.selector.clearSelection(); - }, - _selectionEnabledChanged: function(selectionEnabled) { - var handler = selectionEnabled ? this.listen : this.unlisten; - handler.call(this, this, 'tap', '_selectionHandler'); - }, - _selectionHandler: function(e) { - var model = this.modelForElement(e.target); - if (!model) { - return; - } - var modelTabIndex, activeElTabIndex; - var target = Polymer.dom(e).path[0]; - var activeEl = Polymer.dom(this.domHost ? this.domHost.root : document).activeElement; - var physicalItem = this._physicalItems[this._getPhysicalIndex(model[this.indexAs])]; - if (target.localName === 'input' || target.localName === 'button' || target.localName === 'select') { - return; - } - modelTabIndex = model.tabIndex; - model.tabIndex = SECRET_TABINDEX; - activeElTabIndex = activeEl ? activeEl.tabIndex : -1; - model.tabIndex = modelTabIndex; - if (activeEl && physicalItem !== activeEl && physicalItem.contains(activeEl) && activeElTabIndex !== SECRET_TABINDEX) { - return; - } - this.toggleSelectionForItem(model[this.as]); - }, - _multiSelectionChanged: function(multiSelection) { - this.clearSelection(); - this.$.selector.multi = multiSelection; - }, - updateSizeForItem: function(item) { - item = this._getNormalizedItem(item); - var key = this._collection.getKey(item); - var pidx = this._physicalIndexForKey[key]; - if (pidx != null) { - this._updateMetrics([ pidx ]); - this._positionItems(); - } - }, - _manageFocus: function() { - var fidx = this._focusedIndex; - if (fidx >= 0 && fidx < this._virtualCount) { - if (this._isIndexRendered(fidx)) { - this._restoreFocusedItem(); - } else { - this._createFocusBackfillItem(); - } - } else if (this._virtualCount > 0 && this._physicalCount > 0) { - this._focusedIndex = this._virtualStart; - this._focusedItem = this._physicalItems[this._physicalStart]; - } - }, - _isIndexRendered: function(idx) { - return idx >= this._virtualStart && idx <= this._virtualEnd; - }, - _isIndexVisible: function(idx) { - return idx >= this.firstVisibleIndex && idx <= this.lastVisibleIndex; - }, - _getPhysicalIndex: function(idx) { - return this._physicalIndexForKey[this._collection.getKey(this._getNormalizedItem(idx))]; - }, - _focusPhysicalItem: function(idx) { - if (idx < 0 || idx >= this._virtualCount) { - return; - } - this._restoreFocusedItem(); - if (!this._isIndexRendered(idx)) { - this.scrollToIndex(idx); - } - var physicalItem = this._physicalItems[this._getPhysicalIndex(idx)]; - var model = physicalItem._templateInstance; - var focusable; - model.tabIndex = SECRET_TABINDEX; - if (physicalItem.tabIndex === SECRET_TABINDEX) { - focusable = physicalItem; - } - if (!focusable) { - focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECRET_TABINDEX + '"]'); - } - model.tabIndex = 0; - this._focusedIndex = idx; - focusable && focusable.focus(); - }, - _removeFocusedItem: function() { - if (this._offscreenFocusedItem) { - Polymer.dom(this).removeChild(this._offscreenFocusedItem); - } - this._offscreenFocusedItem = null; - this._focusBackfillItem = null; - this._focusedItem = null; - this._focusedIndex = -1; - }, - _createFocusBackfillItem: function() { - var pidx, fidx = this._focusedIndex; - if (this._offscreenFocusedItem || fidx < 0) { - return; - } - if (!this._focusBackfillItem) { - var stampedTemplate = this.stamp(null); - this._focusBackfillItem = stampedTemplate.root.querySelector('*'); - Polymer.dom(this).appendChild(stampedTemplate.root); - } - pidx = this._getPhysicalIndex(fidx); - if (pidx != null) { - this._offscreenFocusedItem = this._physicalItems[pidx]; - this._physicalItems[pidx] = this._focusBackfillItem; - this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem); - } - }, - _restoreFocusedItem: function() { - var pidx, fidx = this._focusedIndex; - if (!this._offscreenFocusedItem || this._focusedIndex < 0) { - return; - } - this._assignModels(); - pidx = this._getPhysicalIndex(fidx); - if (pidx != null) { - this._focusBackfillItem = this._physicalItems[pidx]; - this._physicalItems[pidx] = this._offscreenFocusedItem; - this._offscreenFocusedItem = null; - this.translate3d(0, HIDDEN_Y, 0, this._focusBackfillItem); - } - }, - _didFocus: function(e) { - var targetModel = this.modelForElement(e.target); - var focusedModel = this._focusedItem ? this._focusedItem._templateInstance : null; - var hasOffscreenFocusedItem = this._offscreenFocusedItem !== null; - var fidx = this._focusedIndex; - if (!targetModel || !focusedModel) { - return; - } - if (focusedModel === targetModel) { - if (!this._isIndexVisible(fidx)) { - this.scrollToIndex(fidx); - } - } else { - this._restoreFocusedItem(); - focusedModel.tabIndex = -1; - targetModel.tabIndex = 0; - fidx = targetModel[this.indexAs]; - this._focusedIndex = fidx; - this._focusedItem = this._physicalItems[this._getPhysicalIndex(fidx)]; - if (hasOffscreenFocusedItem && !this._offscreenFocusedItem) { - this._update(); - } - } - }, - _didMoveUp: function() { - this._focusPhysicalItem(this._focusedIndex - 1); - }, - _didMoveDown: function(e) { - e.detail.keyboardEvent.preventDefault(); - this._focusPhysicalItem(this._focusedIndex + 1); - }, - _didEnter: function(e) { - this._focusPhysicalItem(this._focusedIndex); - this._selectionHandler(e.detail.keyboardEvent); - } - }); -})(); - +}else{this._createFocusBackfillItem()}}else if(this._virtualCount>0&&this._physicalCount>0){this._focusedIndex=this._virtualStart;this._focusedItem=this._physicalItems[this._physicalStart]}},_isIndexRendered:function(idx){return idx>=this._virtualStart&&idx<=this._virtualEnd},_isIndexVisible:function(idx){return idx>=this.firstVisibleIndex&&idx<=this.lastVisibleIndex},_getPhysicalIndex:function(idx){return this._physicalIndexForKey[this._collection.getKey(this._getNormalizedItem(idx))]},_focusPhysicalItem:function(idx){if(idx<0||idx>=this._virtualCount){return}this._restoreFocusedItem();if(!this._isIndexRendered(idx)){this.scrollToIndex(idx)}var physicalItem=this._physicalItems[this._getPhysicalIndex(idx)];var model=physicalItem._templateInstance;var focusable;model.tabIndex=SECRET_TABINDEX;if(physicalItem.tabIndex===SECRET_TABINDEX){focusable=physicalItem}if(!focusable){focusable=Polymer.dom(physicalItem).querySelector('[tabindex="'+SECRET_TABINDEX+'"]')}model.tabIndex=0;this._focusedIndex=idx;focusable&&focusable.focus()},_removeFocusedItem:function(){if(this._offscreenFocusedItem){Polymer.dom(this).removeChild(this._offscreenFocusedItem)}this._offscreenFocusedItem=null;this._focusBackfillItem=null;this._focusedItem=null;this._focusedIndex=-1},_createFocusBackfillItem:function(){var pidx,fidx=this._focusedIndex;if(this._offscreenFocusedItem||fidx<0){return}if(!this._focusBackfillItem){var stampedTemplate=this.stamp(null);this._focusBackfillItem=stampedTemplate.root.querySelector("*");Polymer.dom(this).appendChild(stampedTemplate.root)}pidx=this._getPhysicalIndex(fidx);if(pidx!=null){this._offscreenFocusedItem=this._physicalItems[pidx];this._physicalItems[pidx]=this._focusBackfillItem;this.translate3d(0,HIDDEN_Y,0,this._offscreenFocusedItem)}},_restoreFocusedItem:function(){var pidx,fidx=this._focusedIndex;if(!this._offscreenFocusedItem||this._focusedIndex<0){return}this._assignModels();pidx=this._getPhysicalIndex(fidx);if(pidx!=null){this._focusBackfillItem=this._physicalItems[pidx];this._physicalItems[pidx]=this._offscreenFocusedItem;this._offscreenFocusedItem=null;this.translate3d(0,HIDDEN_Y,0,this._focusBackfillItem)}},_didFocus:function(e){var targetModel=this.modelForElement(e.target);var focusedModel=this._focusedItem?this._focusedItem._templateInstance:null;var hasOffscreenFocusedItem=this._offscreenFocusedItem!==null;var fidx=this._focusedIndex;if(!targetModel||!focusedModel){return}if(focusedModel===targetModel){if(!this._isIndexVisible(fidx)){this.scrollToIndex(fidx)}}else{this._restoreFocusedItem();focusedModel.tabIndex=-1;targetModel.tabIndex=0;fidx=targetModel[this.indexAs];this._focusedIndex=fidx;this._focusedItem=this._physicalItems[this._getPhysicalIndex(fidx)];if(hasOffscreenFocusedItem&&!this._offscreenFocusedItem){this._update()}}},_didMoveUp:function(){this._focusPhysicalItem(this._focusedIndex-1)},_didMoveDown:function(e){e.detail.keyboardEvent.preventDefault();this._focusPhysicalItem(this._focusedIndex+1)},_didEnter:function(e){this._focusPhysicalItem(this._focusedIndex);this._selectionHandler(e.detail.keyboardEvent)}})})(); // 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. -cr.define('downloads', function() { - function chromeSendWithId(chromeSendName) { - return function(id) { - chrome.send(chromeSendName, [ id ]); - }; - } - function ActionService() { - this.searchTerms_ = []; - } - function trim(s) { - return s.trim(); - } - function truthy(value) { - return !!value; - } - ActionService.splitTerms = function(searchText) { - return searchText.split(/"([^"]*)"/).map(trim).filter(truthy); - }; - ActionService.prototype = { - cancel: chromeSendWithId('cancel'), - clearAll: function() { - if (loadTimeData.getBoolean('allowDeletingHistory')) { - chrome.send('clearAll'); - this.search(''); - } - }, - discardDangerous: chromeSendWithId('discardDangerous'), - download: function(url) { - var a = document.createElement('a'); - a.href = url; - a.setAttribute('download', ''); - a.click(); - }, - drag: chromeSendWithId('drag'), - loadMore: function() { - chrome.send('getDownloads', this.searchTerms_); - }, - isSearching: function() { - return this.searchTerms_.length > 0; - }, - openDownloadsFolder: chrome.send.bind(chrome, 'openDownloadsFolder'), - openFile: chromeSendWithId('openFile'), - pause: chromeSendWithId('pause'), - remove: chromeSendWithId('remove'), - resume: chromeSendWithId('resume'), - saveDangerous: chromeSendWithId('saveDangerous'), - search: function(searchText) { - var searchTerms = ActionService.splitTerms(searchText); - var sameTerms = searchTerms.length == this.searchTerms_.length; - for (var i = 0; sameTerms && i < searchTerms.length; ++i) { - if (searchTerms[i] != this.searchTerms_[i]) sameTerms = false; - } - if (sameTerms) return false; - this.searchTerms_ = searchTerms; - this.loadMore(); - return true; - }, - show: chromeSendWithId('show'), - undo: chrome.send.bind(chrome, 'undo') - }; - cr.addSingletonGetter(ActionService); - return { - ActionService: ActionService - }; -}); - +cr.define("downloads",function(){function chromeSendWithId(chromeSendName){return function(id){chrome.send(chromeSendName,[id])}}function ActionService(){this.searchTerms_=[]}function trim(s){return s.trim()}function truthy(value){return!!value}ActionService.splitTerms=function(searchText){return searchText.split(/"([^"]*)"/).map(trim).filter(truthy)};ActionService.prototype={cancel:chromeSendWithId("cancel"),clearAll:function(){if(loadTimeData.getBoolean("allowDeletingHistory")){chrome.send("clearAll");this.search("")}},discardDangerous:chromeSendWithId("discardDangerous"),download:function(url){var a=document.createElement("a");a.href=url;a.setAttribute("download","");a.click()},drag:chromeSendWithId("drag"),loadMore:function(){chrome.send("getDownloads",this.searchTerms_)},isSearching:function(){return this.searchTerms_.length>0},openDownloadsFolder:chrome.send.bind(chrome,"openDownloadsFolder"),openFile:chromeSendWithId("openFile"),pause:chromeSendWithId("pause"),remove:chromeSendWithId("remove"),resume:chromeSendWithId("resume"),saveDangerous:chromeSendWithId("saveDangerous"),search:function(searchText){var searchTerms=ActionService.splitTerms(searchText);var sameTerms=searchTerms.length==this.searchTerms_.length;for(var i=0;sameTerms&&i<searchTerms.length;++i){if(searchTerms[i]!=this.searchTerms_[i])sameTerms=false}if(sameTerms)return false;this.searchTerms_=searchTerms;this.loadMore();return true},show:chromeSendWithId("show"),undo:chrome.send.bind(chrome,"undo")};cr.addSingletonGetter(ActionService);return{ActionService:ActionService}}); // 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. -cr.define('downloads', function() { - var DangerType = { - NOT_DANGEROUS: 'NOT_DANGEROUS', - DANGEROUS_FILE: 'DANGEROUS_FILE', - DANGEROUS_URL: 'DANGEROUS_URL', - DANGEROUS_CONTENT: 'DANGEROUS_CONTENT', - UNCOMMON_CONTENT: 'UNCOMMON_CONTENT', - DANGEROUS_HOST: 'DANGEROUS_HOST', - POTENTIALLY_UNWANTED: 'POTENTIALLY_UNWANTED' - }; - var States = { - IN_PROGRESS: 'IN_PROGRESS', - CANCELLED: 'CANCELLED', - COMPLETE: 'COMPLETE', - PAUSED: 'PAUSED', - DANGEROUS: 'DANGEROUS', - INTERRUPTED: 'INTERRUPTED' - }; - return { - DangerType: DangerType, - States: States - }; -}); - +cr.define("downloads",function(){var DangerType={NOT_DANGEROUS:"NOT_DANGEROUS",DANGEROUS_FILE:"DANGEROUS_FILE",DANGEROUS_URL:"DANGEROUS_URL",DANGEROUS_CONTENT:"DANGEROUS_CONTENT",UNCOMMON_CONTENT:"UNCOMMON_CONTENT",DANGEROUS_HOST:"DANGEROUS_HOST",POTENTIALLY_UNWANTED:"POTENTIALLY_UNWANTED"};var States={IN_PROGRESS:"IN_PROGRESS",CANCELLED:"CANCELLED",COMPLETE:"COMPLETE",PAUSED:"PAUSED",DANGEROUS:"DANGEROUS",INTERRUPTED:"INTERRUPTED"};return{DangerType:DangerType,States:States}}); // 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. -var ActionLink = document.registerElement('action-link', { - prototype: { - __proto__: HTMLAnchorElement.prototype, - createdCallback: function() { - this.tabIndex = this.disabled ? -1 : 0; - if (!this.hasAttribute('role')) this.setAttribute('role', 'link'); - this.addEventListener('keydown', function(e) { - if (!this.disabled && e.key == 'Enter' && !this.href) { - window.setTimeout(this.click.bind(this), 0); - } - }); - function preventDefault(e) { - e.preventDefault(); - } - function removePreventDefault() { - document.removeEventListener('selectstart', preventDefault); - document.removeEventListener('mouseup', removePreventDefault); - } - this.addEventListener('mousedown', function() { - document.addEventListener('selectstart', preventDefault); - document.addEventListener('mouseup', removePreventDefault); - if (document.activeElement != this) this.classList.add('no-outline'); - }); - this.addEventListener('blur', function() { - this.classList.remove('no-outline'); - }); - }, - set disabled(disabled) { - if (disabled) HTMLAnchorElement.prototype.setAttribute.call(this, 'disabled', ''); else HTMLAnchorElement.prototype.removeAttribute.call(this, 'disabled'); - this.tabIndex = disabled ? -1 : 0; - }, - get disabled() { - return this.hasAttribute('disabled'); - }, - setAttribute: function(attr, val) { - if (attr.toLowerCase() == 'disabled') this.disabled = true; else HTMLAnchorElement.prototype.setAttribute.apply(this, arguments); - }, - removeAttribute: function(attr) { - if (attr.toLowerCase() == 'disabled') this.disabled = false; else HTMLAnchorElement.prototype.removeAttribute.apply(this, arguments); - } - }, - "extends": 'a' -}); - -(function() { - var metaDatas = {}; - var metaArrays = {}; - var singleton = null; - Polymer.IronMeta = Polymer({ - is: 'iron-meta', - properties: { - type: { - type: String, - value: 'default', - observer: '_typeChanged' - }, - key: { - type: String, - observer: '_keyChanged' - }, - value: { - type: Object, - notify: true, - observer: '_valueChanged' - }, - self: { - type: Boolean, - observer: '_selfChanged' - }, - list: { - type: Array, - notify: true - } - }, - hostAttributes: { - hidden: true - }, - 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() { - 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); - }, - 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); - } - } - } - }); - Polymer.IronMeta.getIronMeta = function getIronMeta() { - if (singleton === null) { - singleton = new Polymer.IronMeta(); - } - return singleton; - }; - Polymer.IronMetaQuery = Polymer({ - is: 'iron-meta-query', - properties: { - type: { - type: String, - value: 'default', - observer: '_typeChanged' - }, - key: { - type: String, - observer: '_keyChanged' - }, - value: { - type: Object, - notify: true, - readOnly: true - }, - list: { - type: Array, - notify: true - } - }, - factoryImpl: function(config) { - if (config) { - for (var n in config) { - switch (n) { - case 'type': - case 'key': - this[n] = config[n]; - break; - } - } - } - }, - created: function() { - 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); - } - }, - byKey: function(key) { - return this._metaData && this._metaData[key]; - } - }); -})(); - -Polymer({ - is: 'iron-icon', - properties: { - icon: { - type: String, - observer: '_iconChanged' - }, - theme: { - type: String, - observer: '_updateIcon' - }, - src: { - type: String, - observer: '_srcChanged' - }, - _meta: { - value: Polymer.Base.create('iron-meta', { - type: 'iconset' - }), - observer: '_updateIcon' - } - }, - _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; - }, - _updateIcon: function() { - if (this._usesIconset()) { - if (this._img && this._img.parentNode) { - Polymer.dom(this.root).removeChild(this._img); - } - if (this._iconName === "") { - if (this._iconset) { - this._iconset.removeIcon(this); - } - } else if (this._iconsetName && this._meta) { - this._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._iconset) { - this._iconset.removeIcon(this); - } - 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); - } - } -}); - -Polymer.IronControlState = { - properties: { - focused: { - type: Boolean, - value: false, - notify: true, - readOnly: true, - reflectToAttribute: true - }, - disabled: { - type: Boolean, - value: false, - notify: true, - observer: '_disabledChanged', - reflectToAttribute: true - }, - _oldTabIndex: { - type: Number - }, - _boundFocusBlurHandler: { - type: Function, - value: function() { - return this._focusBlurHandler.bind(this); - } - } - }, - observers: [ '_changedControlState(focused, disabled)' ], - ready: function() { - this.addEventListener('focus', this._boundFocusBlurHandler, true); - this.addEventListener('blur', this._boundFocusBlurHandler, true); - }, - _focusBlurHandler: function(event) { - if (event.target === this) { - this._setFocused(event.type === 'focus'); - } else if (!this.shadowRoot) { - var target = Polymer.dom(event).localTarget; - if (!this.isLightDescendant(target)) { - this.fire(event.type, { - sourceEvent: event - }, { - node: this, - bubbles: event.bubbles, - cancelable: event.cancelable - }); - } - } - }, - _disabledChanged: function(disabled, old) { - this.setAttribute('aria-disabled', disabled ? 'true' : 'false'); - this.style.pointerEvents = disabled ? 'none' : ''; - if (disabled) { - this._oldTabIndex = this.tabIndex; - this._setFocused(false); - this.tabIndex = -1; - this.blur(); - } else if (this._oldTabIndex !== undefined) { - this.tabIndex = this._oldTabIndex; - } - }, - _changedControlState: function() { - if (this._controlStateChanged) { - this._controlStateChanged(); - } - } -}; - -Polymer.IronButtonStateImpl = { - properties: { - pressed: { - type: Boolean, - readOnly: true, - value: false, - reflectToAttribute: true, - observer: '_pressedChanged' - }, - toggles: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - active: { - type: Boolean, - value: false, - notify: true, - reflectToAttribute: true - }, - pointerDown: { - type: Boolean, - readOnly: true, - value: false - }, - receivedFocusFromKeyboard: { - type: Boolean, - readOnly: true - }, - ariaActiveAttribute: { - type: String, - value: 'aria-pressed', - observer: '_ariaActiveAttributeChanged' - } - }, - listeners: { - down: '_downHandler', - up: '_upHandler', - tap: '_tapHandler' - }, - observers: [ '_detectKeyboardFocus(focused)', '_activeChanged(active, ariaActiveAttribute)' ], - keyBindings: { - 'enter:keydown': '_asyncClick', - 'space:keydown': '_spaceKeyDownHandler', - 'space:keyup': '_spaceKeyUpHandler' - }, - _mouseEventRe: /^mouse/, - _tapHandler: function() { - if (this.toggles) { - this._userActivate(!this.active); - } else { - this.active = false; - } - }, - _detectKeyboardFocus: function(focused) { - this._setReceivedFocusFromKeyboard(!this.pointerDown && focused); - }, - _userActivate: function(active) { - if (this.active !== active) { - this.active = active; - this.fire('change'); - } - }, - _downHandler: function(event) { - this._setPointerDown(true); - this._setPressed(true); - this._setReceivedFocusFromKeyboard(false); - }, - _upHandler: function() { - this._setPointerDown(false); - this._setPressed(false); - }, - _spaceKeyDownHandler: function(event) { - var keyboardEvent = event.detail.keyboardEvent; - var target = Polymer.dom(keyboardEvent).localTarget; - if (this.isLightDescendant(target)) return; - keyboardEvent.preventDefault(); - keyboardEvent.stopImmediatePropagation(); - this._setPressed(true); - }, - _spaceKeyUpHandler: function(event) { - var keyboardEvent = event.detail.keyboardEvent; - var target = Polymer.dom(keyboardEvent).localTarget; - if (this.isLightDescendant(target)) return; - if (this.pressed) { - this._asyncClick(); - } - this._setPressed(false); - }, - _asyncClick: function() { - this.async(function() { - this.click(); - }, 1); - }, - _pressedChanged: function(pressed) { - this._changedButtonState(); - }, - _ariaActiveAttributeChanged: function(value, oldValue) { - if (oldValue && oldValue != value && this.hasAttribute(oldValue)) { - this.removeAttribute(oldValue); - } - }, - _activeChanged: function(active, ariaActiveAttribute) { - if (this.toggles) { - this.setAttribute(this.ariaActiveAttribute, active ? 'true' : 'false'); - } else { - this.removeAttribute(this.ariaActiveAttribute); - } - this._changedButtonState(); - }, - _controlStateChanged: function() { - if (this.disabled) { - this._setPressed(false); - } else { - this._changedButtonState(); - } - }, - _changedButtonState: function() { - if (this._buttonStateChanged) { - this._buttonStateChanged(); - } - } -}; - -Polymer.IronButtonState = [ Polymer.IronA11yKeysBehavior, Polymer.IronButtonStateImpl ]; - -(function() { - var Utility = { - distance: function(x1, y1, x2, y2) { - var xDelta = x1 - x2; - var yDelta = y1 - y2; - return Math.sqrt(xDelta * xDelta + yDelta * yDelta); - }, - now: window.performance && window.performance.now ? window.performance.now.bind(window.performance) : Date.now - }; - function ElementMetrics(element) { - this.element = element; - this.width = this.boundingRect.width; - this.height = this.boundingRect.height; - this.size = Math.max(this.width, this.height); - } - ElementMetrics.prototype = { - get boundingRect() { - return this.element.getBoundingClientRect(); - }, - furthestCornerDistanceFrom: function(x, y) { - var topLeft = Utility.distance(x, y, 0, 0); - var topRight = Utility.distance(x, y, this.width, 0); - var bottomLeft = Utility.distance(x, y, 0, this.height); - var bottomRight = Utility.distance(x, y, this.width, this.height); - return Math.max(topLeft, topRight, bottomLeft, bottomRight); - } - }; - function Ripple(element) { - this.element = element; - this.color = window.getComputedStyle(element).color; - this.wave = document.createElement('div'); - this.waveContainer = document.createElement('div'); - this.wave.style.backgroundColor = this.color; - this.wave.classList.add('wave'); - this.waveContainer.classList.add('wave-container'); - Polymer.dom(this.waveContainer).appendChild(this.wave); - this.resetInteractionState(); - } - Ripple.MAX_RADIUS = 300; - Ripple.prototype = { - get recenters() { - return this.element.recenters; - }, - get center() { - return this.element.center; - }, - get mouseDownElapsed() { - var elapsed; - if (!this.mouseDownStart) { - return 0; - } - elapsed = Utility.now() - this.mouseDownStart; - if (this.mouseUpStart) { - elapsed -= this.mouseUpElapsed; - } - return elapsed; - }, - get mouseUpElapsed() { - return this.mouseUpStart ? Utility.now() - this.mouseUpStart : 0; - }, - get mouseDownElapsedSeconds() { - return this.mouseDownElapsed / 1e3; - }, - get mouseUpElapsedSeconds() { - return this.mouseUpElapsed / 1e3; - }, - get mouseInteractionSeconds() { - return this.mouseDownElapsedSeconds + this.mouseUpElapsedSeconds; - }, - get initialOpacity() { - return this.element.initialOpacity; - }, - get opacityDecayVelocity() { - return this.element.opacityDecayVelocity; - }, - get radius() { - var width2 = this.containerMetrics.width * this.containerMetrics.width; - var height2 = this.containerMetrics.height * this.containerMetrics.height; - var waveRadius = Math.min(Math.sqrt(width2 + height2), Ripple.MAX_RADIUS) * 1.1 + 5; - var duration = 1.1 - .2 * (waveRadius / Ripple.MAX_RADIUS); - var timeNow = this.mouseInteractionSeconds / duration; - var size = waveRadius * (1 - Math.pow(80, -timeNow)); - return Math.abs(size); - }, - get opacity() { - if (!this.mouseUpStart) { - return this.initialOpacity; - } - return Math.max(0, this.initialOpacity - this.mouseUpElapsedSeconds * this.opacityDecayVelocity); - }, - get outerOpacity() { - var outerOpacity = this.mouseUpElapsedSeconds * .3; - var waveOpacity = this.opacity; - return Math.max(0, Math.min(outerOpacity, waveOpacity)); - }, - get isOpacityFullyDecayed() { - return this.opacity < .01 && this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS); - }, - get isRestingAtMaxRadius() { - return this.opacity >= this.initialOpacity && this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS); - }, - get isAnimationComplete() { - return this.mouseUpStart ? this.isOpacityFullyDecayed : this.isRestingAtMaxRadius; - }, - get translationFraction() { - return Math.min(1, this.radius / this.containerMetrics.size * 2 / Math.sqrt(2)); - }, - get xNow() { - if (this.xEnd) { - return this.xStart + this.translationFraction * (this.xEnd - this.xStart); - } - return this.xStart; - }, - get yNow() { - if (this.yEnd) { - return this.yStart + this.translationFraction * (this.yEnd - this.yStart); - } - return this.yStart; - }, - get isMouseDown() { - return this.mouseDownStart && !this.mouseUpStart; - }, - resetInteractionState: function() { - this.maxRadius = 0; - this.mouseDownStart = 0; - this.mouseUpStart = 0; - this.xStart = 0; - this.yStart = 0; - this.xEnd = 0; - this.yEnd = 0; - this.slideDistance = 0; - this.containerMetrics = new ElementMetrics(this.element); - }, - draw: function() { - var scale; - var translateString; - var dx; - var dy; - this.wave.style.opacity = this.opacity; - scale = this.radius / (this.containerMetrics.size / 2); - dx = this.xNow - this.containerMetrics.width / 2; - dy = this.yNow - this.containerMetrics.height / 2; - this.waveContainer.style.webkitTransform = 'translate(' + dx + 'px, ' + dy + 'px)'; - this.waveContainer.style.transform = 'translate3d(' + dx + 'px, ' + dy + 'px, 0)'; - this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')'; - this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)'; - }, - downAction: function(event) { - var xCenter = this.containerMetrics.width / 2; - var yCenter = this.containerMetrics.height / 2; - this.resetInteractionState(); - this.mouseDownStart = Utility.now(); - if (this.center) { - this.xStart = xCenter; - this.yStart = yCenter; - this.slideDistance = Utility.distance(this.xStart, this.yStart, this.xEnd, this.yEnd); - } else { - this.xStart = event ? event.detail.x - this.containerMetrics.boundingRect.left : this.containerMetrics.width / 2; - this.yStart = event ? event.detail.y - this.containerMetrics.boundingRect.top : this.containerMetrics.height / 2; - } - if (this.recenters) { - this.xEnd = xCenter; - this.yEnd = yCenter; - this.slideDistance = Utility.distance(this.xStart, this.yStart, this.xEnd, this.yEnd); - } - this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom(this.xStart, this.yStart); - this.waveContainer.style.top = (this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px'; - this.waveContainer.style.left = (this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px'; - this.waveContainer.style.width = this.containerMetrics.size + 'px'; - this.waveContainer.style.height = this.containerMetrics.size + 'px'; - }, - upAction: function(event) { - if (!this.isMouseDown) { - return; - } - this.mouseUpStart = Utility.now(); - }, - remove: function() { - Polymer.dom(this.waveContainer.parentNode).removeChild(this.waveContainer); - } - }; - Polymer({ - is: 'paper-ripple', - behaviors: [ Polymer.IronA11yKeysBehavior ], - properties: { - initialOpacity: { - type: Number, - value: .25 - }, - opacityDecayVelocity: { - type: Number, - value: .8 - }, - recenters: { - type: Boolean, - value: false - }, - center: { - type: Boolean, - value: false - }, - ripples: { - type: Array, - value: function() { - return []; - } - }, - animating: { - type: Boolean, - readOnly: true, - reflectToAttribute: true, - value: false - }, - holdDown: { - type: Boolean, - value: false, - observer: '_holdDownChanged' - }, - noink: { - type: Boolean, - value: false - }, - _animating: { - type: Boolean - }, - _boundAnimate: { - type: Function, - value: function() { - return this.animate.bind(this); - } - } - }, - get target() { - return this.keyEventTarget; - }, - keyBindings: { - 'enter:keydown': '_onEnterKeydown', - 'space:keydown': '_onSpaceKeydown', - 'space:keyup': '_onSpaceKeyup' - }, - attached: function() { - if (this.parentNode.nodeType == 11) { - this.keyEventTarget = Polymer.dom(this).getOwnerRoot().host; - } else { - this.keyEventTarget = this.parentNode; - } - var keyEventTarget = this.keyEventTarget; - this.listen(keyEventTarget, 'up', 'uiUpAction'); - this.listen(keyEventTarget, 'down', 'uiDownAction'); - }, - detached: function() { - this.unlisten(this.keyEventTarget, 'up', 'uiUpAction'); - this.unlisten(this.keyEventTarget, 'down', 'uiDownAction'); - this.keyEventTarget = null; - }, - get shouldKeepAnimating() { - for (var index = 0; index < this.ripples.length; ++index) { - if (!this.ripples[index].isAnimationComplete) { - return true; - } - } - return false; - }, - simulatedRipple: function() { - this.downAction(null); - this.async(function() { - this.upAction(); - }, 1); - }, - uiDownAction: function(event) { - if (!this.noink) { - this.downAction(event); - } - }, - downAction: function(event) { - if (this.holdDown && this.ripples.length > 0) { - return; - } - var ripple = this.addRipple(); - ripple.downAction(event); - if (!this._animating) { - this._animating = true; - this.animate(); - } - }, - uiUpAction: function(event) { - if (!this.noink) { - this.upAction(event); - } - }, - upAction: function(event) { - if (this.holdDown) { - return; - } - this.ripples.forEach(function(ripple) { - ripple.upAction(event); - }); - this._animating = true; - this.animate(); - }, - onAnimationComplete: function() { - this._animating = false; - this.$.background.style.backgroundColor = null; - this.fire('transitionend'); - }, - addRipple: function() { - var ripple = new Ripple(this); - Polymer.dom(this.$.waves).appendChild(ripple.waveContainer); - this.$.background.style.backgroundColor = ripple.color; - this.ripples.push(ripple); - this._setAnimating(true); - return ripple; - }, - removeRipple: function(ripple) { - var rippleIndex = this.ripples.indexOf(ripple); - if (rippleIndex < 0) { - return; - } - this.ripples.splice(rippleIndex, 1); - ripple.remove(); - if (!this.ripples.length) { - this._setAnimating(false); - } - }, - animate: function() { - if (!this._animating) { - return; - } - var index; - var ripple; - for (index = 0; index < this.ripples.length; ++index) { - ripple = this.ripples[index]; - ripple.draw(); - this.$.background.style.opacity = ripple.outerOpacity; - if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) { - this.removeRipple(ripple); - } - } - if (!this.shouldKeepAnimating && this.ripples.length === 0) { - this.onAnimationComplete(); - } else { - window.requestAnimationFrame(this._boundAnimate); - } - }, - _onEnterKeydown: function() { - this.uiDownAction(); - this.async(this.uiUpAction, 1); - }, - _onSpaceKeydown: function() { - this.uiDownAction(); - }, - _onSpaceKeyup: function() { - this.uiUpAction(); - }, - _holdDownChanged: function(newVal, oldVal) { - if (oldVal === undefined) { - return; - } - if (newVal) { - this.downAction(); - } else { - this.upAction(); - } - } - }); -})(); - -Polymer.PaperRippleBehavior = { - properties: { - noink: { - type: Boolean, - observer: '_noinkChanged' - }, - _rippleContainer: { - type: Object - } - }, - _buttonStateChanged: function() { - if (this.focused) { - this.ensureRipple(); - } - }, - _downHandler: function(event) { - Polymer.IronButtonStateImpl._downHandler.call(this, event); - if (this.pressed) { - this.ensureRipple(event); - } - }, - ensureRipple: function(optTriggeringEvent) { - 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); - } - if (optTriggeringEvent) { - var domContainer = Polymer.dom(this._rippleContainer || this); - var target = Polymer.dom(optTriggeringEvent).rootTarget; - if (domContainer.deepContains(target)) { - this._ripple.uiDownAction(optTriggeringEvent); - } - } - } - }, - getRipple: function() { - this.ensureRipple(); - return this._ripple; - }, - hasRipple: function() { - return Boolean(this._ripple); - }, - _createRipple: function() { - return document.createElement('paper-ripple'); - }, - _noinkChanged: function(noink) { - if (this.hasRipple()) { - this._ripple.noink = noink; - } - } -}; - -Polymer.PaperButtonBehaviorImpl = { - properties: { - elevation: { - type: Number, - reflectToAttribute: true, - readOnly: true - } - }, - observers: [ '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)', '_computeKeyboardClass(receivedFocusFromKeyboard)' ], - hostAttributes: { - role: 'button', - tabindex: '0', - animated: true - }, - _calculateElevation: function() { - var e = 1; - if (this.disabled) { - e = 0; - } else if (this.active || this.pressed) { - e = 4; - } else if (this.receivedFocusFromKeyboard) { - e = 3; - } - this._setElevation(e); - }, - _computeKeyboardClass: function(receivedFocusFromKeyboard) { - this.toggleClass('keyboard-focus', receivedFocusFromKeyboard); - }, - _spaceKeyDownHandler: function(event) { - Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event); - if (this.hasRipple() && this.getRipple().ripples.length < 1) { - this._ripple.uiDownAction(); - } - }, - _spaceKeyUpHandler: function(event) { - Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event); - if (this.hasRipple()) { - this._ripple.uiUpAction(); - } - } -}; - -Polymer.PaperButtonBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, Polymer.PaperRippleBehavior, Polymer.PaperButtonBehaviorImpl ]; - -Polymer({ - is: 'paper-button', - behaviors: [ Polymer.PaperButtonBehavior ], - properties: { - raised: { - type: Boolean, - reflectToAttribute: true, - value: false, - observer: '_calculateElevation' - } - }, - _calculateElevation: function() { - if (!this.raised) { - this._setElevation(0); - } else { - Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this); - } - } -}); - -Polymer({ - is: 'paper-icon-button-light', - "extends": 'button', - behaviors: [ Polymer.PaperRippleBehavior ], - listeners: { - down: '_rippleDown', - up: '_rippleUp', - focus: '_rippleDown', - blur: '_rippleUp' - }, - _rippleDown: function() { - this.getRipple().downAction(); - }, - _rippleUp: function() { - this.getRipple().upAction(); - }, - ensureRipple: function(var_args) { - var lastRipple = this._ripple; - Polymer.PaperRippleBehavior.ensureRipple.apply(this, arguments); - if (this._ripple && this._ripple !== lastRipple) { - this._ripple.center = true; - this._ripple.classList.add('circle'); - } - } -}); - -Polymer.IronRangeBehavior = { - properties: { - value: { - type: Number, - value: 0, - notify: true, - reflectToAttribute: true - }, - min: { - type: Number, - value: 0, - notify: true - }, - max: { - type: Number, - value: 100, - notify: true - }, - step: { - type: Number, - value: 1, - notify: true - }, - ratio: { - type: Number, - value: 0, - readOnly: true, - notify: true - } - }, - observers: [ '_update(value, min, max, step)' ], - _calcRatio: function(value) { - return (this._clampValue(value) - this.min) / (this.max - this.min); - }, - _clampValue: function(value) { - return Math.min(this.max, Math.max(this.min, this._calcStep(value))); - }, - _calcStep: function(value) { - value = parseFloat(value); - if (!this.step) { - return value; - } - var numSteps = Math.round((value - this.min) / this.step); - if (this.step < 1) { - return numSteps / (1 / this.step) + this.min; - } else { - return numSteps * this.step + this.min; - } - }, - _validateValue: function() { - var v = this._clampValue(this.value); - this.value = this.oldValue = isNaN(v) ? this.oldValue : v; - return this.value !== v; - }, - _update: function() { - this._validateValue(); - this._setRatio(this._calcRatio(this.value) * 100); - } -}; - -Polymer({ - is: 'paper-progress', - behaviors: [ Polymer.IronRangeBehavior ], - properties: { - secondaryProgress: { - type: Number, - value: 0 - }, - secondaryRatio: { - type: Number, - value: 0, - readOnly: true - }, - indeterminate: { - type: Boolean, - value: false, - observer: '_toggleIndeterminate' - }, - disabled: { - type: Boolean, - value: false, - reflectToAttribute: true, - observer: '_disabledChanged' - } - }, - observers: [ '_progressChanged(secondaryProgress, value, min, max)' ], - hostAttributes: { - role: 'progressbar' - }, - _toggleIndeterminate: function(indeterminate) { - this.toggleClass('indeterminate', indeterminate, this.$.primaryProgress); - }, - _transformProgress: function(progress, ratio) { - var transform = 'scaleX(' + ratio / 100 + ')'; - progress.style.transform = progress.style.webkitTransform = transform; - }, - _mainRatioChanged: function(ratio) { - this._transformProgress(this.$.primaryProgress, ratio); - }, - _progressChanged: function(secondaryProgress, value, min, max) { - secondaryProgress = this._clampValue(secondaryProgress); - value = this._clampValue(value); - var secondaryRatio = this._calcRatio(secondaryProgress) * 100; - var mainRatio = this._calcRatio(value) * 100; - this._setSecondaryRatio(secondaryRatio); - this._transformProgress(this.$.secondaryProgress, secondaryRatio); - this._transformProgress(this.$.primaryProgress, mainRatio); - this.secondaryProgress = secondaryProgress; - this.setAttribute('aria-valuenow', value); - this.setAttribute('aria-valuemin', min); - this.setAttribute('aria-valuemax', max); - }, - _disabledChanged: function(disabled) { - this.setAttribute('aria-disabled', disabled ? 'true' : 'false'); - }, - _hideSecondaryProgress: function(secondaryRatio) { - return secondaryRatio === 0; - } -}); - -Polymer({ - is: 'iron-iconset-svg', - properties: { - name: { - type: String, - observer: '_nameChanged' - }, - size: { - type: Number, - value: 24 - } - }, - attached: function() { - this.style.display = 'none'; - }, - getIconNames: function() { - this._icons = this._createIconMap(); - return Object.keys(this._icons).map(function(n) { - return this.name + ':' + n; - }, this); - }, - applyIcon: function(element, iconName) { - element = element.root || element; - this.removeIcon(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; - }, - removeIcon: function(element) { - if (element._svgIcon) { - Polymer.dom(element).removeChild(element._svgIcon); - element._svgIcon = null; - } - }, - _nameChanged: function() { - new Polymer.IronMeta({ - type: 'iconset', - key: this.name, - value: this - }); - this.async(function() { - this.fire('iron-iconset-added', this, { - node: window - }); - }); - }, - _createIconMap: function() { - var icons = Object.create(null); - Polymer.dom(this).querySelectorAll('[id]').forEach(function(icon) { - icons[icon.id] = icon; - }); - return icons; - }, - _cloneIcon: function(id) { - this._icons = this._icons || this._createIconMap(); - return this._prepareSvgClone(this._icons[id], this.size); - }, - _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'); - svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;'; - svg.appendChild(content).removeAttribute('id'); - return svg; - } - return null; - } -}); - +var ActionLink=document.registerElement("action-link",{prototype:{__proto__:HTMLAnchorElement.prototype,createdCallback:function(){this.tabIndex=this.disabled?-1:0;if(!this.hasAttribute("role"))this.setAttribute("role","link");this.addEventListener("keydown",function(e){if(!this.disabled&&e.key=="Enter"&&!this.href){window.setTimeout(this.click.bind(this),0)}});function preventDefault(e){e.preventDefault()}function removePreventDefault(){document.removeEventListener("selectstart",preventDefault);document.removeEventListener("mouseup",removePreventDefault)}this.addEventListener("mousedown",function(){document.addEventListener("selectstart",preventDefault);document.addEventListener("mouseup",removePreventDefault);if(document.activeElement!=this)this.classList.add("no-outline")});this.addEventListener("blur",function(){this.classList.remove("no-outline")})},set disabled(disabled){if(disabled)HTMLAnchorElement.prototype.setAttribute.call(this,"disabled","");else HTMLAnchorElement.prototype.removeAttribute.call(this,"disabled");this.tabIndex=disabled?-1:0},get disabled(){return this.hasAttribute("disabled")},setAttribute:function(attr,val){if(attr.toLowerCase()=="disabled")this.disabled=true;else HTMLAnchorElement.prototype.setAttribute.apply(this,arguments)},removeAttribute:function(attr){if(attr.toLowerCase()=="disabled")this.disabled=false;else HTMLAnchorElement.prototype.removeAttribute.apply(this,arguments)}},"extends":"a"});(function(){var metaDatas={};var metaArrays={};var singleton=null;Polymer.IronMeta=Polymer({is:"iron-meta",properties:{type:{type:String,value:"default",observer:"_typeChanged"},key:{type:String,observer:"_keyChanged"},value:{type:Object,notify:true,observer:"_valueChanged"},self:{type:Boolean,observer:"_selfChanged"},list:{type:Array,notify:true}},hostAttributes:{hidden:true},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(){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)},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)}}}});Polymer.IronMeta.getIronMeta=function getIronMeta(){if(singleton===null){singleton=new Polymer.IronMeta}return singleton};Polymer.IronMetaQuery=Polymer({is:"iron-meta-query",properties:{type:{type:String,value:"default",observer:"_typeChanged"},key:{type:String,observer:"_keyChanged"},value:{type:Object,notify:true,readOnly:true},list:{type:Array,notify:true}},factoryImpl:function(config){if(config){for(var n in config){switch(n){case"type":case"key":this[n]=config[n];break}}}},created:function(){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)}},byKey:function(key){return this._metaData&&this._metaData[key]}})})();Polymer({is:"iron-icon",properties:{icon:{type:String,observer:"_iconChanged"},theme:{type:String,observer:"_updateIcon"},src:{type:String,observer:"_srcChanged"},_meta:{value:Polymer.Base.create("iron-meta",{type:"iconset"}),observer:"_updateIcon"}},_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},_updateIcon:function(){if(this._usesIconset()){if(this._img&&this._img.parentNode){Polymer.dom(this.root).removeChild(this._img)}if(this._iconName===""){if(this._iconset){this._iconset.removeIcon(this)}}else if(this._iconsetName&&this._meta){this._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._iconset){this._iconset.removeIcon(this)}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)}}});Polymer.IronControlState={properties:{focused:{type:Boolean,value:false,notify:true,readOnly:true,reflectToAttribute:true},disabled:{type:Boolean,value:false,notify:true,observer:"_disabledChanged",reflectToAttribute:true},_oldTabIndex:{type:Number},_boundFocusBlurHandler:{type:Function,value:function(){return this._focusBlurHandler.bind(this)}}},observers:["_changedControlState(focused, disabled)"],ready:function(){this.addEventListener("focus",this._boundFocusBlurHandler,true);this.addEventListener("blur",this._boundFocusBlurHandler,true)},_focusBlurHandler:function(event){if(event.target===this){this._setFocused(event.type==="focus")}else if(!this.shadowRoot){var target=Polymer.dom(event).localTarget;if(!this.isLightDescendant(target)){this.fire(event.type,{sourceEvent:event},{node:this,bubbles:event.bubbles,cancelable:event.cancelable})}}},_disabledChanged:function(disabled,old){this.setAttribute("aria-disabled",disabled?"true":"false");this.style.pointerEvents=disabled?"none":"";if(disabled){this._oldTabIndex=this.tabIndex;this._setFocused(false);this.tabIndex=-1;this.blur()}else if(this._oldTabIndex!==undefined){this.tabIndex=this._oldTabIndex}},_changedControlState:function(){if(this._controlStateChanged){this._controlStateChanged()}}};Polymer.IronButtonStateImpl={properties:{pressed:{type:Boolean,readOnly:true,value:false,reflectToAttribute:true,observer:"_pressedChanged"},toggles:{type:Boolean,value:false,reflectToAttribute:true},active:{type:Boolean,value:false,notify:true,reflectToAttribute:true},pointerDown:{type:Boolean,readOnly:true,value:false},receivedFocusFromKeyboard:{type:Boolean,readOnly:true},ariaActiveAttribute:{type:String,value:"aria-pressed",observer:"_ariaActiveAttributeChanged"}},listeners:{down:"_downHandler",up:"_upHandler",tap:"_tapHandler"},observers:["_detectKeyboardFocus(focused)","_activeChanged(active, ariaActiveAttribute)"],keyBindings:{"enter:keydown":"_asyncClick","space:keydown":"_spaceKeyDownHandler","space:keyup":"_spaceKeyUpHandler"},_mouseEventRe:/^mouse/,_tapHandler:function(){if(this.toggles){this._userActivate(!this.active)}else{this.active=false}},_detectKeyboardFocus:function(focused){this._setReceivedFocusFromKeyboard(!this.pointerDown&&focused)},_userActivate:function(active){if(this.active!==active){this.active=active;this.fire("change")}},_downHandler:function(event){this._setPointerDown(true);this._setPressed(true);this._setReceivedFocusFromKeyboard(false)},_upHandler:function(){this._setPointerDown(false);this._setPressed(false)},_spaceKeyDownHandler:function(event){var keyboardEvent=event.detail.keyboardEvent;var target=Polymer.dom(keyboardEvent).localTarget;if(this.isLightDescendant(target))return;keyboardEvent.preventDefault();keyboardEvent.stopImmediatePropagation();this._setPressed(true)},_spaceKeyUpHandler:function(event){var keyboardEvent=event.detail.keyboardEvent;var target=Polymer.dom(keyboardEvent).localTarget;if(this.isLightDescendant(target))return;if(this.pressed){this._asyncClick()}this._setPressed(false)},_asyncClick:function(){this.async(function(){this.click()},1)},_pressedChanged:function(pressed){this._changedButtonState()},_ariaActiveAttributeChanged:function(value,oldValue){if(oldValue&&oldValue!=value&&this.hasAttribute(oldValue)){this.removeAttribute(oldValue)}},_activeChanged:function(active,ariaActiveAttribute){if(this.toggles){this.setAttribute(this.ariaActiveAttribute,active?"true":"false")}else{this.removeAttribute(this.ariaActiveAttribute)}this._changedButtonState()},_controlStateChanged:function(){if(this.disabled){this._setPressed(false)}else{this._changedButtonState()}},_changedButtonState:function(){if(this._buttonStateChanged){this._buttonStateChanged()}}};Polymer.IronButtonState=[Polymer.IronA11yKeysBehavior,Polymer.IronButtonStateImpl];(function(){var Utility={distance:function(x1,y1,x2,y2){var xDelta=x1-x2;var yDelta=y1-y2;return Math.sqrt(xDelta*xDelta+yDelta*yDelta)},now:window.performance&&window.performance.now?window.performance.now.bind(window.performance):Date.now};function ElementMetrics(element){this.element=element;this.width=this.boundingRect.width;this.height=this.boundingRect.height;this.size=Math.max(this.width,this.height)}ElementMetrics.prototype={get boundingRect(){return this.element.getBoundingClientRect()},furthestCornerDistanceFrom:function(x,y){var topLeft=Utility.distance(x,y,0,0);var topRight=Utility.distance(x,y,this.width,0);var bottomLeft=Utility.distance(x,y,0,this.height);var bottomRight=Utility.distance(x,y,this.width,this.height);return Math.max(topLeft,topRight,bottomLeft,bottomRight)}};function Ripple(element){this.element=element;this.color=window.getComputedStyle(element).color;this.wave=document.createElement("div");this.waveContainer=document.createElement("div");this.wave.style.backgroundColor=this.color;this.wave.classList.add("wave");this.waveContainer.classList.add("wave-container");Polymer.dom(this.waveContainer).appendChild(this.wave);this.resetInteractionState()}Ripple.MAX_RADIUS=300;Ripple.prototype={get recenters(){return this.element.recenters},get center(){return this.element.center},get mouseDownElapsed(){var elapsed;if(!this.mouseDownStart){return 0}elapsed=Utility.now()-this.mouseDownStart;if(this.mouseUpStart){elapsed-=this.mouseUpElapsed}return elapsed},get mouseUpElapsed(){return this.mouseUpStart?Utility.now()-this.mouseUpStart:0},get mouseDownElapsedSeconds(){return this.mouseDownElapsed/1e3},get mouseUpElapsedSeconds(){return this.mouseUpElapsed/1e3},get mouseInteractionSeconds(){return this.mouseDownElapsedSeconds+this.mouseUpElapsedSeconds},get initialOpacity(){return this.element.initialOpacity},get opacityDecayVelocity(){return this.element.opacityDecayVelocity},get radius(){var width2=this.containerMetrics.width*this.containerMetrics.width;var height2=this.containerMetrics.height*this.containerMetrics.height;var waveRadius=Math.min(Math.sqrt(width2+height2),Ripple.MAX_RADIUS)*1.1+5;var duration=1.1-.2*(waveRadius/Ripple.MAX_RADIUS);var timeNow=this.mouseInteractionSeconds/duration;var size=waveRadius*(1-Math.pow(80,-timeNow));return Math.abs(size)},get opacity(){if(!this.mouseUpStart){return this.initialOpacity}return Math.max(0,this.initialOpacity-this.mouseUpElapsedSeconds*this.opacityDecayVelocity)},get outerOpacity(){var outerOpacity=this.mouseUpElapsedSeconds*.3;var waveOpacity=this.opacity;return Math.max(0,Math.min(outerOpacity,waveOpacity))},get isOpacityFullyDecayed(){return this.opacity<.01&&this.radius>=Math.min(this.maxRadius,Ripple.MAX_RADIUS)},get isRestingAtMaxRadius(){return this.opacity>=this.initialOpacity&&this.radius>=Math.min(this.maxRadius,Ripple.MAX_RADIUS)},get isAnimationComplete(){return this.mouseUpStart?this.isOpacityFullyDecayed:this.isRestingAtMaxRadius},get translationFraction(){return Math.min(1,this.radius/this.containerMetrics.size*2/Math.sqrt(2))},get xNow(){if(this.xEnd){return this.xStart+this.translationFraction*(this.xEnd-this.xStart)}return this.xStart},get yNow(){if(this.yEnd){return this.yStart+this.translationFraction*(this.yEnd-this.yStart)}return this.yStart},get isMouseDown(){return this.mouseDownStart&&!this.mouseUpStart},resetInteractionState:function(){this.maxRadius=0;this.mouseDownStart=0;this.mouseUpStart=0;this.xStart=0;this.yStart=0;this.xEnd=0;this.yEnd=0;this.slideDistance=0;this.containerMetrics=new ElementMetrics(this.element)},draw:function(){var scale;var translateString;var dx;var dy;this.wave.style.opacity=this.opacity;scale=this.radius/(this.containerMetrics.size/2);dx=this.xNow-this.containerMetrics.width/2;dy=this.yNow-this.containerMetrics.height/2;this.waveContainer.style.webkitTransform="translate("+dx+"px, "+dy+"px)";this.waveContainer.style.transform="translate3d("+dx+"px, "+dy+"px, 0)";this.wave.style.webkitTransform="scale("+scale+","+scale+")";this.wave.style.transform="scale3d("+scale+","+scale+",1)"},downAction:function(event){var xCenter=this.containerMetrics.width/2;var yCenter=this.containerMetrics.height/2;this.resetInteractionState();this.mouseDownStart=Utility.now();if(this.center){this.xStart=xCenter;this.yStart=yCenter;this.slideDistance=Utility.distance(this.xStart,this.yStart,this.xEnd,this.yEnd)}else{this.xStart=event?event.detail.x-this.containerMetrics.boundingRect.left:this.containerMetrics.width/2;this.yStart=event?event.detail.y-this.containerMetrics.boundingRect.top:this.containerMetrics.height/2}if(this.recenters){this.xEnd=xCenter;this.yEnd=yCenter;this.slideDistance=Utility.distance(this.xStart,this.yStart,this.xEnd,this.yEnd)}this.maxRadius=this.containerMetrics.furthestCornerDistanceFrom(this.xStart,this.yStart);this.waveContainer.style.top=(this.containerMetrics.height-this.containerMetrics.size)/2+"px";this.waveContainer.style.left=(this.containerMetrics.width-this.containerMetrics.size)/2+"px";this.waveContainer.style.width=this.containerMetrics.size+"px";this.waveContainer.style.height=this.containerMetrics.size+"px"},upAction:function(event){if(!this.isMouseDown){return}this.mouseUpStart=Utility.now()},remove:function(){Polymer.dom(this.waveContainer.parentNode).removeChild(this.waveContainer)}};Polymer({is:"paper-ripple",behaviors:[Polymer.IronA11yKeysBehavior],properties:{initialOpacity:{type:Number,value:.25},opacityDecayVelocity:{type:Number,value:.8},recenters:{type:Boolean,value:false},center:{type:Boolean,value:false},ripples:{type:Array,value:function(){return[]}},animating:{type:Boolean,readOnly:true,reflectToAttribute:true,value:false},holdDown:{type:Boolean,value:false,observer:"_holdDownChanged"},noink:{type:Boolean,value:false},_animating:{type:Boolean},_boundAnimate:{type:Function,value:function(){return this.animate.bind(this)}}},get target(){return this.keyEventTarget},keyBindings:{"enter:keydown":"_onEnterKeydown","space:keydown":"_onSpaceKeydown","space:keyup":"_onSpaceKeyup"},attached:function(){if(this.parentNode.nodeType==11){this.keyEventTarget=Polymer.dom(this).getOwnerRoot().host}else{this.keyEventTarget=this.parentNode}var keyEventTarget=this.keyEventTarget;this.listen(keyEventTarget,"up","uiUpAction");this.listen(keyEventTarget,"down","uiDownAction")},detached:function(){this.unlisten(this.keyEventTarget,"up","uiUpAction");this.unlisten(this.keyEventTarget,"down","uiDownAction");this.keyEventTarget=null},get shouldKeepAnimating(){for(var index=0;index<this.ripples.length;++index){if(!this.ripples[index].isAnimationComplete){return true}}return false},simulatedRipple:function(){this.downAction(null);this.async(function(){this.upAction()},1)},uiDownAction:function(event){if(!this.noink){this.downAction(event)}},downAction:function(event){if(this.holdDown&&this.ripples.length>0){return}var ripple=this.addRipple();ripple.downAction(event);if(!this._animating){this._animating=true;this.animate()}},uiUpAction:function(event){if(!this.noink){this.upAction(event)}},upAction:function(event){if(this.holdDown){return}this.ripples.forEach(function(ripple){ripple.upAction(event)});this._animating=true;this.animate()},onAnimationComplete:function(){this._animating=false;this.$.background.style.backgroundColor=null;this.fire("transitionend")},addRipple:function(){var ripple=new Ripple(this);Polymer.dom(this.$.waves).appendChild(ripple.waveContainer);this.$.background.style.backgroundColor=ripple.color;this.ripples.push(ripple);this._setAnimating(true);return ripple},removeRipple:function(ripple){var rippleIndex=this.ripples.indexOf(ripple);if(rippleIndex<0){return}this.ripples.splice(rippleIndex,1);ripple.remove();if(!this.ripples.length){this._setAnimating(false)}},animate:function(){if(!this._animating){return}var index;var ripple;for(index=0;index<this.ripples.length;++index){ripple=this.ripples[index];ripple.draw();this.$.background.style.opacity=ripple.outerOpacity;if(ripple.isOpacityFullyDecayed&&!ripple.isRestingAtMaxRadius){this.removeRipple(ripple)}}if(!this.shouldKeepAnimating&&this.ripples.length===0){this.onAnimationComplete()}else{window.requestAnimationFrame(this._boundAnimate)}},_onEnterKeydown:function(){this.uiDownAction();this.async(this.uiUpAction,1)},_onSpaceKeydown:function(){this.uiDownAction()},_onSpaceKeyup:function(){this.uiUpAction()},_holdDownChanged:function(newVal,oldVal){if(oldVal===undefined){return}if(newVal){this.downAction()}else{this.upAction()}}})})();Polymer.PaperRippleBehavior={properties:{noink:{type:Boolean,observer:"_noinkChanged"},_rippleContainer:{type:Object}},_buttonStateChanged:function(){if(this.focused){this.ensureRipple()}},_downHandler:function(event){Polymer.IronButtonStateImpl._downHandler.call(this,event);if(this.pressed){this.ensureRipple(event)}},ensureRipple:function(optTriggeringEvent){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)}if(optTriggeringEvent){var domContainer=Polymer.dom(this._rippleContainer||this);var target=Polymer.dom(optTriggeringEvent).rootTarget;if(domContainer.deepContains(target)){this._ripple.uiDownAction(optTriggeringEvent)}}}},getRipple:function(){this.ensureRipple();return this._ripple},hasRipple:function(){return Boolean(this._ripple)},_createRipple:function(){return document.createElement("paper-ripple")},_noinkChanged:function(noink){if(this.hasRipple()){this._ripple.noink=noink}}};Polymer.PaperButtonBehaviorImpl={properties:{elevation:{type:Number,reflectToAttribute:true,readOnly:true}},observers:["_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)","_computeKeyboardClass(receivedFocusFromKeyboard)"],hostAttributes:{role:"button",tabindex:"0",animated:true},_calculateElevation:function(){var e=1;if(this.disabled){e=0}else if(this.active||this.pressed){e=4}else if(this.receivedFocusFromKeyboard){e=3}this._setElevation(e)},_computeKeyboardClass:function(receivedFocusFromKeyboard){this.toggleClass("keyboard-focus",receivedFocusFromKeyboard)},_spaceKeyDownHandler:function(event){Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this,event);if(this.hasRipple()&&this.getRipple().ripples.length<1){this._ripple.uiDownAction()}},_spaceKeyUpHandler:function(event){Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this,event);if(this.hasRipple()){this._ripple.uiUpAction()}}};Polymer.PaperButtonBehavior=[Polymer.IronButtonState,Polymer.IronControlState,Polymer.PaperRippleBehavior,Polymer.PaperButtonBehaviorImpl];Polymer({is:"paper-button",behaviors:[Polymer.PaperButtonBehavior],properties:{raised:{type:Boolean,reflectToAttribute:true,value:false,observer:"_calculateElevation"}},_calculateElevation:function(){if(!this.raised){this._setElevation(0)}else{Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this)}}});Polymer({is:"paper-icon-button-light","extends":"button",behaviors:[Polymer.PaperRippleBehavior],listeners:{down:"_rippleDown",up:"_rippleUp",focus:"_rippleDown",blur:"_rippleUp"},_rippleDown:function(){this.getRipple().downAction()},_rippleUp:function(){this.getRipple().upAction()},ensureRipple:function(var_args){var lastRipple=this._ripple;Polymer.PaperRippleBehavior.ensureRipple.apply(this,arguments);if(this._ripple&&this._ripple!==lastRipple){this._ripple.center=true;this._ripple.classList.add("circle")}}});Polymer.IronRangeBehavior={properties:{value:{type:Number,value:0,notify:true,reflectToAttribute:true},min:{type:Number,value:0,notify:true},max:{type:Number,value:100,notify:true},step:{type:Number,value:1,notify:true},ratio:{type:Number,value:0,readOnly:true,notify:true}},observers:["_update(value, min, max, step)"],_calcRatio:function(value){return(this._clampValue(value)-this.min)/(this.max-this.min)},_clampValue:function(value){return Math.min(this.max,Math.max(this.min,this._calcStep(value)))},_calcStep:function(value){value=parseFloat(value);if(!this.step){return value}var numSteps=Math.round((value-this.min)/this.step);if(this.step<1){return numSteps/(1/this.step)+this.min}else{return numSteps*this.step+this.min}},_validateValue:function(){var v=this._clampValue(this.value);this.value=this.oldValue=isNaN(v)?this.oldValue:v;return this.value!==v},_update:function(){this._validateValue();this._setRatio(this._calcRatio(this.value)*100)}};Polymer({is:"paper-progress",behaviors:[Polymer.IronRangeBehavior],properties:{secondaryProgress:{type:Number,value:0},secondaryRatio:{type:Number,value:0,readOnly:true},indeterminate:{type:Boolean,value:false,observer:"_toggleIndeterminate"},disabled:{type:Boolean,value:false,reflectToAttribute:true,observer:"_disabledChanged"}},observers:["_progressChanged(secondaryProgress, value, min, max)"],hostAttributes:{role:"progressbar"},_toggleIndeterminate:function(indeterminate){this.toggleClass("indeterminate",indeterminate,this.$.primaryProgress)},_transformProgress:function(progress,ratio){var transform="scaleX("+ratio/100+")";progress.style.transform=progress.style.webkitTransform=transform},_mainRatioChanged:function(ratio){this._transformProgress(this.$.primaryProgress,ratio)},_progressChanged:function(secondaryProgress,value,min,max){secondaryProgress=this._clampValue(secondaryProgress);value=this._clampValue(value);var secondaryRatio=this._calcRatio(secondaryProgress)*100;var mainRatio=this._calcRatio(value)*100;this._setSecondaryRatio(secondaryRatio);this._transformProgress(this.$.secondaryProgress,secondaryRatio);this._transformProgress(this.$.primaryProgress,mainRatio);this.secondaryProgress=secondaryProgress;this.setAttribute("aria-valuenow",value);this.setAttribute("aria-valuemin",min);this.setAttribute("aria-valuemax",max)},_disabledChanged:function(disabled){this.setAttribute("aria-disabled",disabled?"true":"false")},_hideSecondaryProgress:function(secondaryRatio){return secondaryRatio===0}});Polymer({is:"iron-iconset-svg",properties:{name:{type:String,observer:"_nameChanged"},size:{type:Number,value:24}},attached:function(){this.style.display="none"},getIconNames:function(){this._icons=this._createIconMap();return Object.keys(this._icons).map(function(n){return this.name+":"+n},this)},applyIcon:function(element,iconName){element=element.root||element;this.removeIcon(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},removeIcon:function(element){if(element._svgIcon){Polymer.dom(element).removeChild(element._svgIcon);element._svgIcon=null}},_nameChanged:function(){new Polymer.IronMeta({type:"iconset",key:this.name,value:this});this.async(function(){this.fire("iron-iconset-added",this,{node:window})})},_createIconMap:function(){var icons=Object.create(null);Polymer.dom(this).querySelectorAll("[id]").forEach(function(icon){icons[icon.id]=icon});return icons},_cloneIcon:function(id){this._icons=this._icons||this._createIconMap();return this._prepareSvgClone(this._icons[id],this.size)},_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");svg.style.cssText="pointer-events: none; display: block; width: 100%; height: 100%;";svg.appendChild(content).removeAttribute("id");return svg}return null}}); // 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. -cr.define('downloads', function() { - var Item = Polymer({ - is: 'downloads-item', - properties: { - data: { - type: Object - }, - completelyOnDisk_: { - computed: 'computeCompletelyOnDisk_(' + 'data.state, data.file_externally_removed)', - type: Boolean, - value: true - }, - controlledBy_: { - computed: 'computeControlledBy_(data.by_ext_id, data.by_ext_name)', - type: String, - value: '' - }, - isActive_: { - computed: 'computeIsActive_(' + 'data.state, data.file_externally_removed)', - type: Boolean, - value: true - }, - isDangerous_: { - computed: 'computeIsDangerous_(data.state)', - type: Boolean, - value: false - }, - isMalware_: { - computed: 'computeIsMalware_(isDangerous_, data.danger_type)', - type: Boolean, - value: false - }, - isInProgress_: { - computed: 'computeIsInProgress_(data.state)', - type: Boolean, - value: false - }, - pauseOrResumeText_: { - computed: 'computePauseOrResumeText_(isInProgress_, data.resume)', - type: String - }, - showCancel_: { - computed: 'computeShowCancel_(data.state)', - type: Boolean, - value: false - }, - showProgress_: { - computed: 'computeShowProgress_(showCancel_, data.percent)', - type: Boolean, - value: false - } - }, - observers: [ 'observeControlledBy_(controlledBy_)', 'observeIsDangerous_(isDangerous_, data)' ], - ready: function() { - this.content = this.$.content; - }, - computeClass_: function() { - var classes = []; - if (this.isActive_) classes.push('is-active'); - if (this.isDangerous_) classes.push('dangerous'); - if (this.showProgress_) classes.push('show-progress'); - return classes.join(' '); - }, - computeCompletelyOnDisk_: function() { - return this.data.state == downloads.States.COMPLETE && !this.data.file_externally_removed; - }, - computeControlledBy_: function() { - if (!this.data.by_ext_id || !this.data.by_ext_name) return ''; - var url = 'chrome://extensions#' + this.data.by_ext_id; - var name = this.data.by_ext_name; - return loadTimeData.getStringF('controlledByUrl', url, name); - }, - 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 'downloads:remove-circle'; - - default: - return 'cr:warning'; - } - }, - computeDate_: function() { - assert(typeof this.data.hideDate == 'boolean'); - if (this.data.hideDate) return ''; - return assert(this.data.since_string || this.data.date_string); - }, - computeDescription_: function() { - var data = this.data; - switch (data.state) { - case downloads.States.DANGEROUS: - var fileName = data.file_name; - switch (data.danger_type) { - case downloads.DangerType.DANGEROUS_FILE: - return loadTimeData.getString('dangerFileDesc'); - - case downloads.DangerType.DANGEROUS_URL: - case downloads.DangerType.DANGEROUS_CONTENT: - case downloads.DangerType.DANGEROUS_HOST: - return loadTimeData.getString('dangerDownloadDesc'); - - case downloads.DangerType.UNCOMMON_CONTENT: - return loadTimeData.getString('dangerUncommonDesc'); - - case downloads.DangerType.POTENTIALLY_UNWANTED: - return loadTimeData.getString('dangerSettingsDesc'); - } - break; - - case downloads.States.IN_PROGRESS: - case downloads.States.PAUSED: - return data.progress_status_text; - } - return ''; - }, - computeIsActive_: function() { - return this.data.state != downloads.States.CANCELLED && this.data.state != downloads.States.INTERRUPTED && !this.data.file_externally_removed; - }, - computeIsDangerous_: function() { - return this.data.state == downloads.States.DANGEROUS; - }, - computeIsInProgress_: function() { - return this.data.state == downloads.States.IN_PROGRESS; - }, - computeIsMalware_: function() { - return this.isDangerous_ && (this.data.danger_type == downloads.DangerType.DANGEROUS_CONTENT || this.data.danger_type == downloads.DangerType.DANGEROUS_HOST || this.data.danger_type == downloads.DangerType.DANGEROUS_URL || this.data.danger_type == downloads.DangerType.POTENTIALLY_UNWANTED); - }, - computePauseOrResumeText_: function() { - if (this.isInProgress_) return loadTimeData.getString('controlPause'); - if (this.data.resume) return loadTimeData.getString('controlResume'); - return ''; - }, - computeRemoveStyle_: function() { - var canDelete = loadTimeData.getBoolean('allowDeletingHistory'); - var hideRemove = this.isDangerous_ || this.showCancel_ || !canDelete; - return hideRemove ? 'visibility: hidden' : ''; - }, - computeShowCancel_: function() { - return this.data.state == downloads.States.IN_PROGRESS || this.data.state == downloads.States.PAUSED; - }, - computeShowProgress_: function() { - return this.showCancel_ && this.data.percent >= -1; - }, - computeTag_: function() { - switch (this.data.state) { - case downloads.States.CANCELLED: - return loadTimeData.getString('statusCancelled'); - - case downloads.States.INTERRUPTED: - return this.data.last_reason_text; - - case downloads.States.COMPLETE: - return this.data.file_externally_removed ? loadTimeData.getString('statusRemoved') : ''; - } - return ''; - }, - isIndeterminate_: function() { - return this.data.percent == -1; - }, - observeControlledBy_: function() { - this.$['controlled-by'].innerHTML = this.controlledBy_; - }, - observeIsDangerous_: function() { - if (!this.data) return; - if (this.isDangerous_) { - this.$.url.removeAttribute('href'); - } else { - this.$.url.href = assert(this.data.url); - var filePath = encodeURIComponent(this.data.file_path); - var scaleFactor = '?scale=' + window.devicePixelRatio + 'x'; - this.$['file-icon'].src = 'chrome://fileicon/' + filePath + scaleFactor; - } - }, - onCancelTap_: function() { - downloads.ActionService.getInstance().cancel(this.data.id); - }, - onDiscardDangerousTap_: function() { - downloads.ActionService.getInstance().discardDangerous(this.data.id); - }, - onDragStart_: function(e) { - e.preventDefault(); - downloads.ActionService.getInstance().drag(this.data.id); - }, - onFileLinkTap_: function(e) { - e.preventDefault(); - downloads.ActionService.getInstance().openFile(this.data.id); - }, - onPauseOrResumeTap_: function() { - if (this.isInProgress_) downloads.ActionService.getInstance().pause(this.data.id); else downloads.ActionService.getInstance().resume(this.data.id); - }, - onRemoveTap_: function() { - downloads.ActionService.getInstance().remove(this.data.id); - }, - onRetryTap_: function() { - downloads.ActionService.getInstance().download(this.data.url); - }, - onSaveDangerousTap_: function() { - downloads.ActionService.getInstance().saveDangerous(this.data.id); - }, - onShowTap_: function() { - downloads.ActionService.getInstance().show(this.data.id); - } - }); - return { - Item: Item - }; -}); - -Polymer.PaperItemBehaviorImpl = { - hostAttributes: { - role: 'option', - tabindex: '0' - } -}; - -Polymer.PaperItemBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, Polymer.PaperItemBehaviorImpl ]; - -Polymer({ - is: 'paper-item', - behaviors: [ Polymer.PaperItemBehavior ] -}); - -Polymer.IronSelection = function(selectCallback) { - this.selection = []; - this.selectCallback = selectCallback; -}; - -Polymer.IronSelection.prototype = { - get: function() { - return this.multi ? this.selection.slice() : this.selection[0]; - }, - clear: function(excludes) { - this.selection.slice().forEach(function(item) { - if (!excludes || excludes.indexOf(item) < 0) { - this.setItemSelected(item, false); - } - }, this); - }, - isSelected: function(item) { - return this.selection.indexOf(item) >= 0; - }, - setItemSelected: function(item, isSelected) { - if (item != null) { - if (isSelected !== this.isSelected(item)) { - if (isSelected) { - this.selection.push(item); - } else { - var i = this.selection.indexOf(item); - if (i >= 0) { - this.selection.splice(i, 1); - } - } - if (this.selectCallback) { - this.selectCallback(item, isSelected); - } - } - } - }, - select: function(item) { - if (this.multi) { - this.toggle(item); - } else if (this.get() !== item) { - this.setItemSelected(this.get(), false); - this.setItemSelected(item, true); - } - }, - toggle: function(item) { - this.setItemSelected(item, !this.isSelected(item)); - } -}; - -Polymer.IronSelectableBehavior = { - properties: { - attrForSelected: { - type: String, - value: null - }, - selected: { - type: String, - notify: true - }, - selectedItem: { - type: Object, - readOnly: true, - notify: true - }, - activateEvent: { - type: String, - value: 'tap', - observer: '_activateEventChanged' - }, - selectable: String, - selectedClass: { - type: String, - value: 'iron-selected' - }, - selectedAttribute: { - type: String, - value: null - }, - fallbackSelection: { - type: String, - value: null - }, - items: { - type: Array, - readOnly: true, - notify: true, - value: function() { - return []; - } - }, - _excludedLocalNames: { - type: Object, - value: function() { - return { - template: 1 - }; - } - } - }, - observers: [ '_updateAttrForSelected(attrForSelected)', '_updateSelected(selected)', '_checkFallback(fallbackSelection)' ], - created: function() { - this._bindFilterItem = this._filterItem.bind(this); - this._selection = new Polymer.IronSelection(this._applySelection.bind(this)); - }, - attached: function() { - this._observer = this._observeItems(this); - this._updateItems(); - if (!this._shouldUpdateSelection) { - this._updateSelected(); - } - this._addListener(this.activateEvent); - }, - detached: function() { - if (this._observer) { - Polymer.dom(this).unobserveNodes(this._observer); - } - this._removeListener(this.activateEvent); - }, - indexOf: function(item) { - return this.items.indexOf(item); - }, - select: function(value) { - this.selected = value; - }, - selectPrevious: function() { - var length = this.items.length; - var index = (Number(this._valueToIndex(this.selected)) - 1 + length) % length; - this.selected = this._indexToValue(index); - }, - selectNext: function() { - var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.length; - this.selected = this._indexToValue(index); - }, - selectIndex: function(index) { - this.select(this._indexToValue(index)); - }, - forceSynchronousItemUpdate: function() { - this._updateItems(); - }, - get _shouldUpdateSelection() { - return this.selected != null; - }, - _checkFallback: function() { - if (this._shouldUpdateSelection) { - this._updateSelected(); - } - }, - _addListener: function(eventName) { - this.listen(this, eventName, '_activateHandler'); - }, - _removeListener: function(eventName) { - this.unlisten(this, eventName, '_activateHandler'); - }, - _activateEventChanged: function(eventName, old) { - this._removeListener(old); - this._addListener(eventName); - }, - _updateItems: function() { - var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '*'); - nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); - this._setItems(nodes); - }, - _updateAttrForSelected: function() { - if (this._shouldUpdateSelection) { - this.selected = this._indexToValue(this.indexOf(this.selectedItem)); - } - }, - _updateSelected: function() { - this._selectSelected(this.selected); - }, - _selectSelected: function(selected) { - this._selection.select(this._valueToItem(this.selected)); - if (this.fallbackSelection && this.items.length && this._selection.get() === undefined) { - this.selected = this.fallbackSelection; - } - }, - _filterItem: function(node) { - return !this._excludedLocalNames[node.localName]; - }, - _valueToItem: function(value) { - return value == null ? null : this.items[this._valueToIndex(value)]; - }, - _valueToIndex: function(value) { - if (this.attrForSelected) { - for (var i = 0, item; item = this.items[i]; i++) { - if (this._valueForItem(item) == value) { - return i; - } - } - } else { - return Number(value); - } - }, - _indexToValue: function(index) { - if (this.attrForSelected) { - var item = this.items[index]; - if (item) { - return this._valueForItem(item); - } - } else { - return index; - } - }, - _valueForItem: function(item) { - var propValue = item[Polymer.CaseMap.dashToCamelCase(this.attrForSelected)]; - return propValue != undefined ? propValue : item.getAttribute(this.attrForSelected); - }, - _applySelection: function(item, isSelected) { - if (this.selectedClass) { - this.toggleClass(this.selectedClass, isSelected, item); - } - if (this.selectedAttribute) { - this.toggleAttribute(this.selectedAttribute, isSelected, item); - } - this._selectionChange(); - this.fire('iron-' + (isSelected ? 'select' : 'deselect'), { - item: item - }); - }, - _selectionChange: function() { - this._setSelectedItem(this._selection.get()); - }, - _observeItems: function(node) { - return Polymer.dom(node).observeNodes(function(mutation) { - this._updateItems(); - if (this._shouldUpdateSelection) { - this._updateSelected(); - } - this.fire('iron-items-changed', mutation, { - bubbles: false, - cancelable: false - }); - }); - }, - _activateHandler: function(e) { - var t = e.target; - var items = this.items; - while (t && t != this) { - var i = items.indexOf(t); - if (i >= 0) { - var value = this._indexToValue(i); - this._itemActivate(value, t); - return; - } - t = t.parentNode; - } - }, - _itemActivate: function(value, item) { - if (!this.fire('iron-activate', { - selected: value, - item: item - }, { - cancelable: true - }).defaultPrevented) { - this.select(value); - } - } -}; - -Polymer.IronMultiSelectableBehaviorImpl = { - properties: { - multi: { - type: Boolean, - value: false, - observer: 'multiChanged' - }, - selectedValues: { - type: Array, - notify: true - }, - selectedItems: { - type: Array, - readOnly: true, - notify: true - } - }, - observers: [ '_updateSelected(selectedValues.splices)' ], - select: function(value) { - if (this.multi) { - if (this.selectedValues) { - this._toggleSelected(value); - } else { - this.selectedValues = [ value ]; - } - } else { - this.selected = value; - } - }, - multiChanged: function(multi) { - this._selection.multi = multi; - }, - get _shouldUpdateSelection() { - return this.selected != null || this.selectedValues != null && this.selectedValues.length; - }, - _updateAttrForSelected: function() { - if (!this.multi) { - Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this); - } else if (this._shouldUpdateSelection) { - this.selectedValues = this.selectedItems.map(function(selectedItem) { - return this._indexToValue(this.indexOf(selectedItem)); - }, this).filter(function(unfilteredValue) { - return unfilteredValue != null; - }, this); - } - }, - _updateSelected: function() { - if (this.multi) { - this._selectMulti(this.selectedValues); - } else { - this._selectSelected(this.selected); - } - }, - _selectMulti: function(values) { - if (values) { - var selectedItems = this._valuesToItems(values); - this._selection.clear(selectedItems); - for (var i = 0; i < selectedItems.length; i++) { - this._selection.setItemSelected(selectedItems[i], true); - } - if (this.fallbackSelection && this.items.length && !this._selection.get().length) { - var fallback = this._valueToItem(this.fallbackSelection); - if (fallback) { - this.selectedValues = [ this.fallbackSelection ]; - } - } - } else { - this._selection.clear(); - } - }, - _selectionChange: function() { - var s = this._selection.get(); - if (this.multi) { - this._setSelectedItems(s); - } else { - this._setSelectedItems([ s ]); - this._setSelectedItem(s); - } - }, - _toggleSelected: function(value) { - var i = this.selectedValues.indexOf(value); - var unselected = i < 0; - if (unselected) { - this.push('selectedValues', value); - } else { - this.splice('selectedValues', i, 1); - } - }, - _valuesToItems: function(values) { - return values == null ? null : values.map(function(value) { - return this._valueToItem(value); - }, this); - } -}; - -Polymer.IronMultiSelectableBehavior = [ Polymer.IronSelectableBehavior, Polymer.IronMultiSelectableBehaviorImpl ]; - -Polymer.IronMenuBehaviorImpl = { - properties: { - focusedItem: { - observer: '_focusedItemChanged', - readOnly: true, - type: Object - }, - attrForItemTitle: { - type: String - } - }, - hostAttributes: { - role: 'menu', - tabindex: '0' - }, - observers: [ '_updateMultiselectable(multi)' ], - listeners: { - focus: '_onFocus', - keydown: '_onKeydown', - 'iron-items-changed': '_onIronItemsChanged' - }, - keyBindings: { - up: '_onUpKey', - down: '_onDownKey', - esc: '_onEscKey', - 'shift+tab:keydown': '_onShiftTabDown' - }, - attached: function() { - this._resetTabindices(); - }, - select: function(value) { - if (this._defaultFocusAsync) { - this.cancelAsync(this._defaultFocusAsync); - this._defaultFocusAsync = null; - } - var item = this._valueToItem(value); - if (item && item.hasAttribute('disabled')) return; - this._setFocusedItem(item); - Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments); - }, - _resetTabindices: function() { - var selectedItem = this.multi ? this.selectedItems && this.selectedItems[0] : this.selectedItem; - this.items.forEach(function(item) { - item.setAttribute('tabindex', item === selectedItem ? '0' : '-1'); - }, this); - }, - _updateMultiselectable: function(multi) { - if (multi) { - this.setAttribute('aria-multiselectable', 'true'); - } else { - this.removeAttribute('aria-multiselectable'); - } - }, - _focusWithKeyboardEvent: function(event) { - for (var i = 0, item; item = this.items[i]; i++) { - var attr = this.attrForItemTitle || 'textContent'; - var title = item[attr] || item.getAttribute(attr); - if (!item.hasAttribute('disabled') && title && title.trim().charAt(0).toLowerCase() === String.fromCharCode(event.keyCode).toLowerCase()) { - this._setFocusedItem(item); - break; - } - } - }, - _focusPrevious: function() { - var length = this.items.length; - var curFocusIndex = Number(this.indexOf(this.focusedItem)); - for (var i = 1; i < length + 1; i++) { - var item = this.items[(curFocusIndex - i + length) % length]; - if (!item.hasAttribute('disabled')) { - var owner = Polymer.dom(item).getOwnerRoot() || document; - this._setFocusedItem(item); - if (Polymer.dom(owner).activeElement == item) { - return; - } - } - } - }, - _focusNext: function() { - var length = this.items.length; - var curFocusIndex = Number(this.indexOf(this.focusedItem)); - for (var i = 1; i < length + 1; i++) { - var item = this.items[(curFocusIndex + i) % length]; - if (!item.hasAttribute('disabled')) { - var owner = Polymer.dom(item).getOwnerRoot() || document; - this._setFocusedItem(item); - if (Polymer.dom(owner).activeElement == item) { - return; - } - } - } - }, - _applySelection: function(item, isSelected) { - if (isSelected) { - item.setAttribute('aria-selected', 'true'); - } else { - item.removeAttribute('aria-selected'); - } - Polymer.IronSelectableBehavior._applySelection.apply(this, arguments); - }, - _focusedItemChanged: function(focusedItem, old) { - old && old.setAttribute('tabindex', '-1'); - if (focusedItem) { - focusedItem.setAttribute('tabindex', '0'); - focusedItem.focus(); - } - }, - _onIronItemsChanged: function(event) { - if (event.detail.addedNodes.length) { - this._resetTabindices(); - } - }, - _onShiftTabDown: function(event) { - var oldTabIndex = this.getAttribute('tabindex'); - Polymer.IronMenuBehaviorImpl._shiftTabPressed = true; - this._setFocusedItem(null); - this.setAttribute('tabindex', '-1'); - this.async(function() { - this.setAttribute('tabindex', oldTabIndex); - Polymer.IronMenuBehaviorImpl._shiftTabPressed = false; - }, 1); - }, - _onFocus: function(event) { - if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) { - return; - } - var rootTarget = Polymer.dom(event).rootTarget; - if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !this.isLightDescendant(rootTarget)) { - return; - } - this._defaultFocusAsync = this.async(function() { - var selectedItem = this.multi ? this.selectedItems && this.selectedItems[0] : this.selectedItem; - this._setFocusedItem(null); - if (selectedItem) { - this._setFocusedItem(selectedItem); - } else if (this.items[0]) { - this._focusNext(); - } - }); - }, - _onUpKey: function(event) { - this._focusPrevious(); - event.detail.keyboardEvent.preventDefault(); - }, - _onDownKey: function(event) { - this._focusNext(); - event.detail.keyboardEvent.preventDefault(); - }, - _onEscKey: function(event) { - this.focusedItem.blur(); - }, - _onKeydown: function(event) { - if (!this.keyboardEventMatchesKeys(event, 'up down esc')) { - this._focusWithKeyboardEvent(event); - } - event.stopPropagation(); - }, - _activateHandler: function(event) { - Polymer.IronSelectableBehavior._activateHandler.call(this, event); - event.stopPropagation(); - } -}; - -Polymer.IronMenuBehaviorImpl._shiftTabPressed = false; - -Polymer.IronMenuBehavior = [ Polymer.IronMultiSelectableBehavior, Polymer.IronA11yKeysBehavior, Polymer.IronMenuBehaviorImpl ]; - -(function() { - Polymer({ - is: 'paper-menu', - behaviors: [ Polymer.IronMenuBehavior ] - }); -})(); - -Polymer.IronFitBehavior = { - properties: { - sizingTarget: { - type: Object, - value: function() { - return this; - } - }, - fitInto: { - type: Object, - value: window - }, - noOverlap: { - type: Boolean - }, - positionTarget: { - type: Element - }, - horizontalAlign: { - type: String - }, - verticalAlign: { - type: String - }, - dynamicAlign: { - type: Boolean - }, - horizontalOffset: { - type: Number, - value: 0, - notify: true - }, - verticalOffset: { - type: Number, - value: 0, - notify: true - }, - autoFitOnAttach: { - type: Boolean, - value: false - }, - _fitInfo: { - type: Object - } - }, - get _fitWidth() { - var fitWidth; - if (this.fitInto === window) { - fitWidth = this.fitInto.innerWidth; - } else { - fitWidth = this.fitInto.getBoundingClientRect().width; - } - return fitWidth; - }, - get _fitHeight() { - var fitHeight; - if (this.fitInto === window) { - fitHeight = this.fitInto.innerHeight; - } else { - fitHeight = this.fitInto.getBoundingClientRect().height; - } - return fitHeight; - }, - get _fitLeft() { - var fitLeft; - if (this.fitInto === window) { - fitLeft = 0; - } else { - fitLeft = this.fitInto.getBoundingClientRect().left; - } - return fitLeft; - }, - get _fitTop() { - var fitTop; - if (this.fitInto === window) { - fitTop = 0; - } else { - fitTop = this.fitInto.getBoundingClientRect().top; - } - return fitTop; - }, - get _defaultPositionTarget() { - var parent = Polymer.dom(this).parentNode; - if (parent && parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { - parent = parent.host; - } - return parent; - }, - get _localeHorizontalAlign() { - if (this._isRTL) { - if (this.horizontalAlign === 'right') { - return 'left'; - } - if (this.horizontalAlign === 'left') { - return 'right'; - } - } - return this.horizontalAlign; - }, - attached: function() { - this._isRTL = window.getComputedStyle(this).direction == 'rtl'; - this.positionTarget = this.positionTarget || this._defaultPositionTarget; - if (this.autoFitOnAttach) { - if (window.getComputedStyle(this).display === 'none') { - setTimeout(function() { - this.fit(); - }.bind(this)); - } else { - this.fit(); - } - } - }, - fit: function() { - this.position(); - this.constrain(); - this.center(); - }, - _discoverInfo: function() { - if (this._fitInfo) { - return; - } - var target = window.getComputedStyle(this); - var sizer = window.getComputedStyle(this.sizingTarget); - this._fitInfo = { - inlineStyle: { - top: this.style.top || '', - left: this.style.left || '', - position: this.style.position || '' - }, - sizerInlineStyle: { - maxWidth: this.sizingTarget.style.maxWidth || '', - maxHeight: this.sizingTarget.style.maxHeight || '', - boxSizing: this.sizingTarget.style.boxSizing || '' - }, - positionedBy: { - vertically: target.top !== 'auto' ? 'top' : target.bottom !== 'auto' ? 'bottom' : null, - horizontally: target.left !== 'auto' ? 'left' : target.right !== 'auto' ? 'right' : null - }, - sizedBy: { - height: sizer.maxHeight !== 'none', - width: sizer.maxWidth !== 'none', - minWidth: parseInt(sizer.minWidth, 10) || 0, - minHeight: parseInt(sizer.minHeight, 10) || 0 - }, - margin: { - top: parseInt(target.marginTop, 10) || 0, - right: parseInt(target.marginRight, 10) || 0, - bottom: parseInt(target.marginBottom, 10) || 0, - left: parseInt(target.marginLeft, 10) || 0 - } - }; - if (this.verticalOffset) { - this._fitInfo.margin.top = this._fitInfo.margin.bottom = this.verticalOffset; - this._fitInfo.inlineStyle.marginTop = this.style.marginTop || ''; - this._fitInfo.inlineStyle.marginBottom = this.style.marginBottom || ''; - this.style.marginTop = this.style.marginBottom = this.verticalOffset + 'px'; - } - if (this.horizontalOffset) { - this._fitInfo.margin.left = this._fitInfo.margin.right = this.horizontalOffset; - this._fitInfo.inlineStyle.marginLeft = this.style.marginLeft || ''; - this._fitInfo.inlineStyle.marginRight = this.style.marginRight || ''; - this.style.marginLeft = this.style.marginRight = this.horizontalOffset + 'px'; - } - }, - resetFit: function() { - var info = this._fitInfo || {}; - for (var property in info.sizerInlineStyle) { - this.sizingTarget.style[property] = info.sizerInlineStyle[property]; - } - for (var property in info.inlineStyle) { - this.style[property] = info.inlineStyle[property]; - } - this._fitInfo = null; - }, - refit: function() { - var scrollLeft = this.sizingTarget.scrollLeft; - var scrollTop = this.sizingTarget.scrollTop; - this.resetFit(); - this.fit(); - this.sizingTarget.scrollLeft = scrollLeft; - this.sizingTarget.scrollTop = scrollTop; - }, - position: function() { - if (!this.horizontalAlign && !this.verticalAlign) { - return; - } - this._discoverInfo(); - this.style.position = 'fixed'; - this.sizingTarget.style.boxSizing = 'border-box'; - this.style.left = '0px'; - this.style.top = '0px'; - var rect = this.getBoundingClientRect(); - var positionRect = this.__getNormalizedRect(this.positionTarget); - var fitRect = this.__getNormalizedRect(this.fitInto); - var margin = this._fitInfo.margin; - var size = { - width: rect.width + margin.left + margin.right, - height: rect.height + margin.top + margin.bottom - }; - var position = this.__getPosition(this._localeHorizontalAlign, this.verticalAlign, size, positionRect, fitRect); - var left = position.left + margin.left; - var top = position.top + margin.top; - var right = Math.min(fitRect.right - margin.right, left + rect.width); - var bottom = Math.min(fitRect.bottom - margin.bottom, top + rect.height); - var minWidth = this._fitInfo.sizedBy.minWidth; - var minHeight = this._fitInfo.sizedBy.minHeight; - if (left < margin.left) { - left = margin.left; - if (right - left < minWidth) { - left = right - minWidth; - } - } - if (top < margin.top) { - top = margin.top; - if (bottom - top < minHeight) { - top = bottom - minHeight; - } - } - this.sizingTarget.style.maxWidth = right - left + 'px'; - this.sizingTarget.style.maxHeight = bottom - top + 'px'; - this.style.left = left - rect.left + 'px'; - this.style.top = top - rect.top + 'px'; - }, - constrain: function() { - if (this.horizontalAlign || this.verticalAlign) { - return; - } - this._discoverInfo(); - var info = this._fitInfo; - if (!info.positionedBy.vertically) { - this.style.position = 'fixed'; - this.style.top = '0px'; - } - if (!info.positionedBy.horizontally) { - this.style.position = 'fixed'; - this.style.left = '0px'; - } - this.sizingTarget.style.boxSizing = 'border-box'; - var rect = this.getBoundingClientRect(); - if (!info.sizedBy.height) { - this.__sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height'); - } - if (!info.sizedBy.width) { - this.__sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right', 'Width'); - } - }, - _sizeDimension: function(rect, positionedBy, start, end, extent) { - this.__sizeDimension(rect, positionedBy, start, end, extent); - }, - __sizeDimension: function(rect, positionedBy, start, end, extent) { - var info = this._fitInfo; - var fitRect = this.__getNormalizedRect(this.fitInto); - var max = extent === 'Width' ? fitRect.width : fitRect.height; - var flip = positionedBy === end; - var offset = flip ? max - rect[end] : rect[start]; - var margin = info.margin[flip ? start : end]; - var offsetExtent = 'offset' + extent; - var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent]; - this.sizingTarget.style['max' + extent] = max - margin - offset - sizingOffset + 'px'; - }, - center: function() { - if (this.horizontalAlign || this.verticalAlign) { - return; - } - this._discoverInfo(); - var positionedBy = this._fitInfo.positionedBy; - if (positionedBy.vertically && positionedBy.horizontally) { - return; - } - this.style.position = 'fixed'; - if (!positionedBy.vertically) { - this.style.top = '0px'; - } - if (!positionedBy.horizontally) { - this.style.left = '0px'; - } - var rect = this.getBoundingClientRect(); - var fitRect = this.__getNormalizedRect(this.fitInto); - if (!positionedBy.vertically) { - var top = fitRect.top - rect.top + (fitRect.height - rect.height) / 2; - this.style.top = top + 'px'; - } - if (!positionedBy.horizontally) { - var left = fitRect.left - rect.left + (fitRect.width - rect.width) / 2; - this.style.left = left + 'px'; - } - }, - __getNormalizedRect: function(target) { - if (target === document.documentElement || target === window) { - return { - top: 0, - left: 0, - width: window.innerWidth, - height: window.innerHeight, - right: window.innerWidth, - bottom: window.innerHeight - }; - } - return target.getBoundingClientRect(); - }, - __getCroppedArea: function(position, size, fitRect) { - var verticalCrop = Math.min(0, position.top) + Math.min(0, fitRect.bottom - (position.top + size.height)); - var horizontalCrop = Math.min(0, position.left) + Math.min(0, fitRect.right - (position.left + size.width)); - return Math.abs(verticalCrop) * size.width + Math.abs(horizontalCrop) * size.height; - }, - __getPosition: function(hAlign, vAlign, size, positionRect, fitRect) { - var positions = [ { - verticalAlign: 'top', - horizontalAlign: 'left', - top: positionRect.top, - left: positionRect.left - }, { - verticalAlign: 'top', - horizontalAlign: 'right', - top: positionRect.top, - left: positionRect.right - size.width - }, { - verticalAlign: 'bottom', - horizontalAlign: 'left', - top: positionRect.bottom - size.height, - left: positionRect.left - }, { - verticalAlign: 'bottom', - horizontalAlign: 'right', - top: positionRect.bottom - size.height, - left: positionRect.right - size.width - } ]; - if (this.noOverlap) { - for (var i = 0, l = positions.length; i < l; i++) { - var copy = {}; - for (var key in positions[i]) { - copy[key] = positions[i][key]; - } - positions.push(copy); - } - positions[0].top = positions[1].top += positionRect.height; - positions[2].top = positions[3].top -= positionRect.height; - positions[4].left = positions[6].left += positionRect.width; - positions[5].left = positions[7].left -= positionRect.width; - } - vAlign = vAlign === 'auto' ? null : vAlign; - hAlign = hAlign === 'auto' ? null : hAlign; - var position; - for (var i = 0; i < positions.length; i++) { - var pos = positions[i]; - if (!this.dynamicAlign && !this.noOverlap && pos.verticalAlign === vAlign && pos.horizontalAlign === hAlign) { - position = pos; - break; - } - var alignOk = (!vAlign || pos.verticalAlign === vAlign) && (!hAlign || pos.horizontalAlign === hAlign); - if (!this.dynamicAlign && !alignOk) { - continue; - } - position = position || pos; - pos.croppedArea = this.__getCroppedArea(pos, size, fitRect); - var diff = pos.croppedArea - position.croppedArea; - if (diff < 0 || diff === 0 && alignOk) { - position = pos; - } - if (position.croppedArea === 0 && alignOk) { - break; - } - } - return position; - } -}; - -(function() { - 'use strict'; - Polymer({ - is: 'iron-overlay-backdrop', - properties: { - opened: { - reflectToAttribute: true, - type: Boolean, - value: false, - observer: '_openedChanged' - } - }, - listeners: { - transitionend: '_onTransitionend' - }, - created: function() { - this.__openedRaf = null; - }, - attached: function() { - this.opened && this._openedChanged(this.opened); - }, - prepare: function() { - if (this.opened && !this.parentNode) { - Polymer.dom(document.body).appendChild(this); - } - }, - open: function() { - this.opened = true; - }, - close: function() { - this.opened = false; - }, - complete: function() { - if (!this.opened && this.parentNode === document.body) { - Polymer.dom(this.parentNode).removeChild(this); - } - }, - _onTransitionend: function(event) { - if (event && event.target === this) { - this.complete(); - } - }, - _openedChanged: function(opened) { - if (opened) { - this.prepare(); - } else { - var cs = window.getComputedStyle(this); - if (cs.transitionDuration === '0s' || cs.opacity == 0) { - this.complete(); - } - } - if (!this.isAttached) { - return; - } - if (this.__openedRaf) { - window.cancelAnimationFrame(this.__openedRaf); - this.__openedRaf = null; - } - this.scrollTop = this.scrollTop; - this.__openedRaf = window.requestAnimationFrame(function() { - this.__openedRaf = null; - this.toggleClass('opened', this.opened); - }.bind(this)); - } - }); -})(); - -Polymer.IronOverlayManagerClass = function() { - this._overlays = []; - this._minimumZ = 101; - this._backdropElement = null; - Polymer.Gestures.add(document, 'tap', this._onCaptureClick.bind(this)); - document.addEventListener('focus', this._onCaptureFocus.bind(this), true); - document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true); -}; - -Polymer.IronOverlayManagerClass.prototype = { - constructor: Polymer.IronOverlayManagerClass, - get backdropElement() { - if (!this._backdropElement) { - this._backdropElement = document.createElement('iron-overlay-backdrop'); - } - return this._backdropElement; - }, - get deepActiveElement() { - var active = document.activeElement || document.body; - while (active.root && Polymer.dom(active.root).activeElement) { - active = Polymer.dom(active.root).activeElement; - } - return active; - }, - _bringOverlayAtIndexToFront: function(i) { - var overlay = this._overlays[i]; - if (!overlay) { - return; - } - var lastI = this._overlays.length - 1; - var currentOverlay = this._overlays[lastI]; - if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) { - lastI--; - } - if (i >= lastI) { - return; - } - var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); - if (this._getZ(overlay) <= minimumZ) { - this._applyOverlayZ(overlay, minimumZ); - } - while (i < lastI) { - this._overlays[i] = this._overlays[i + 1]; - i++; - } - this._overlays[lastI] = overlay; - }, - addOrRemoveOverlay: function(overlay) { - if (overlay.opened) { - this.addOverlay(overlay); - } else { - this.removeOverlay(overlay); - } - }, - addOverlay: function(overlay) { - var i = this._overlays.indexOf(overlay); - if (i >= 0) { - this._bringOverlayAtIndexToFront(i); - this.trackBackdrop(); - return; - } - var insertionIndex = this._overlays.length; - var currentOverlay = this._overlays[insertionIndex - 1]; - var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ); - var newZ = this._getZ(overlay); - if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) { - this._applyOverlayZ(currentOverlay, minimumZ); - insertionIndex--; - var previousOverlay = this._overlays[insertionIndex - 1]; - minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ); - } - if (newZ <= minimumZ) { - this._applyOverlayZ(overlay, minimumZ); - } - this._overlays.splice(insertionIndex, 0, overlay); - this.trackBackdrop(); - }, - removeOverlay: function(overlay) { - var i = this._overlays.indexOf(overlay); - if (i === -1) { - return; - } - this._overlays.splice(i, 1); - this.trackBackdrop(); - }, - currentOverlay: function() { - var i = this._overlays.length - 1; - return this._overlays[i]; - }, - currentOverlayZ: function() { - return this._getZ(this.currentOverlay()); - }, - ensureMinimumZ: function(minimumZ) { - this._minimumZ = Math.max(this._minimumZ, minimumZ); - }, - focusOverlay: function() { - var current = this.currentOverlay(); - if (current) { - current._applyFocus(); - } - }, - trackBackdrop: function() { - var overlay = this._overlayWithBackdrop(); - if (!overlay && !this._backdropElement) { - return; - } - this.backdropElement.style.zIndex = this._getZ(overlay) - 1; - this.backdropElement.opened = !!overlay; - }, - getBackdrops: function() { - var backdrops = []; - for (var i = 0; i < this._overlays.length; i++) { - if (this._overlays[i].withBackdrop) { - backdrops.push(this._overlays[i]); - } - } - return backdrops; - }, - backdropZ: function() { - return this._getZ(this._overlayWithBackdrop()) - 1; - }, - _overlayWithBackdrop: function() { - for (var i = 0; i < this._overlays.length; i++) { - if (this._overlays[i].withBackdrop) { - return this._overlays[i]; - } - } - }, - _getZ: function(overlay) { - var z = this._minimumZ; - if (overlay) { - var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay).zIndex); - if (z1 === z1) { - z = z1; - } - } - return z; - }, - _setZ: function(element, z) { - element.style.zIndex = z; - }, - _applyOverlayZ: function(overlay, aboveZ) { - this._setZ(overlay, aboveZ + 2); - }, - _overlayInPath: function(path) { - path = path || []; - for (var i = 0; i < path.length; i++) { - if (path[i]._manager === this) { - return path[i]; - } - } - }, - _onCaptureClick: function(event) { - var overlay = this.currentOverlay(); - if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) { - overlay._onCaptureClick(event); - } - }, - _onCaptureFocus: function(event) { - var overlay = this.currentOverlay(); - if (overlay) { - overlay._onCaptureFocus(event); - } - }, - _onCaptureKeyDown: function(event) { - var overlay = this.currentOverlay(); - if (overlay) { - if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc')) { - overlay._onCaptureEsc(event); - } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'tab')) { - overlay._onCaptureTab(event); - } - } - }, - _shouldBeBehindOverlay: function(overlay1, overlay2) { - return !overlay1.alwaysOnTop && overlay2.alwaysOnTop; - } -}; - -Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass(); - -(function() { - 'use strict'; - Polymer.IronOverlayBehaviorImpl = { - properties: { - opened: { - observer: '_openedChanged', - type: Boolean, - value: false, - notify: true - }, - canceled: { - observer: '_canceledChanged', - readOnly: true, - type: Boolean, - value: false - }, - withBackdrop: { - observer: '_withBackdropChanged', - type: Boolean - }, - noAutoFocus: { - type: Boolean, - value: false - }, - noCancelOnEscKey: { - type: Boolean, - value: false - }, - noCancelOnOutsideClick: { - type: Boolean, - value: false - }, - closingReason: { - type: Object - }, - restoreFocusOnClose: { - type: Boolean, - value: false - }, - alwaysOnTop: { - type: Boolean - }, - _manager: { - type: Object, - value: Polymer.IronOverlayManager - }, - _focusedChild: { - type: Object - } - }, - listeners: { - 'iron-resize': '_onIronResize' - }, - get backdropElement() { - return this._manager.backdropElement; - }, - get _focusNode() { - return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]') || this; - }, - get _focusableNodes() { - var FOCUSABLE_WITH_DISABLED = [ 'a[href]', 'area[href]', 'iframe', '[tabindex]', '[contentEditable=true]' ]; - var FOCUSABLE_WITHOUT_DISABLED = [ 'input', 'select', 'textarea', 'button' ]; - var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') + ':not([tabindex="-1"]),' + FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),') + ':not([disabled]):not([tabindex="-1"])'; - var focusables = Polymer.dom(this).querySelectorAll(selector); - if (this.tabIndex >= 0) { - focusables.splice(0, 0, this); - } - return focusables.sort(function(a, b) { - if (a.tabIndex === b.tabIndex) { - return 0; - } - if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { - return 1; - } - return -1; - }); - }, - ready: function() { - this.__isAnimating = false; - this.__shouldRemoveTabIndex = false; - this.__firstFocusableNode = this.__lastFocusableNode = null; - this.__raf = null; - this.__restoreFocusNode = null; - this._ensureSetup(); - }, - attached: function() { - if (this.opened) { - this._openedChanged(this.opened); - } - this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); - }, - detached: function() { - Polymer.dom(this).unobserveNodes(this._observer); - this._observer = null; - if (this.__raf) { - window.cancelAnimationFrame(this.__raf); - this.__raf = null; - } - this._manager.removeOverlay(this); - }, - toggle: function() { - this._setCanceled(false); - this.opened = !this.opened; - }, - open: function() { - this._setCanceled(false); - this.opened = true; - }, - close: function() { - this._setCanceled(false); - this.opened = false; - }, - cancel: function(event) { - var cancelEvent = this.fire('iron-overlay-canceled', event, { - cancelable: true - }); - if (cancelEvent.defaultPrevented) { - return; - } - this._setCanceled(true); - this.opened = false; - }, - _ensureSetup: function() { - if (this._overlaySetup) { - return; - } - this._overlaySetup = true; - this.style.outline = 'none'; - this.style.display = 'none'; - }, - _openedChanged: function(opened) { - if (opened) { - this.removeAttribute('aria-hidden'); - } else { - this.setAttribute('aria-hidden', 'true'); - } - if (!this.isAttached) { - return; - } - this.__isAnimating = true; - this.__onNextAnimationFrame(this.__openedChanged); - }, - _canceledChanged: function() { - this.closingReason = this.closingReason || {}; - this.closingReason.canceled = this.canceled; - }, - _withBackdropChanged: function() { - if (this.withBackdrop && !this.hasAttribute('tabindex')) { - this.setAttribute('tabindex', '-1'); - this.__shouldRemoveTabIndex = true; - } else if (this.__shouldRemoveTabIndex) { - this.removeAttribute('tabindex'); - this.__shouldRemoveTabIndex = false; - } - if (this.opened && this.isAttached) { - this._manager.trackBackdrop(); - } - }, - _prepareRenderOpened: function() { - this.__restoreFocusNode = this._manager.deepActiveElement; - this._preparePositioning(); - this.refit(); - this._finishPositioning(); - if (this.noAutoFocus && document.activeElement === this._focusNode) { - this._focusNode.blur(); - this.__restoreFocusNode.focus(); - } - }, - _renderOpened: function() { - this._finishRenderOpened(); - }, - _renderClosed: function() { - this._finishRenderClosed(); - }, - _finishRenderOpened: function() { - this.notifyResize(); - this.__isAnimating = false; - var focusableNodes = this._focusableNodes; - this.__firstFocusableNode = focusableNodes[0]; - this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; - this.fire('iron-overlay-opened'); - }, - _finishRenderClosed: function() { - this.style.display = 'none'; - this.style.zIndex = ''; - this.notifyResize(); - this.__isAnimating = false; - this.fire('iron-overlay-closed', this.closingReason); - }, - _preparePositioning: function() { - this.style.transition = this.style.webkitTransition = 'none'; - this.style.transform = this.style.webkitTransform = 'none'; - this.style.display = ''; - }, - _finishPositioning: function() { - this.style.display = 'none'; - this.scrollTop = this.scrollTop; - this.style.transition = this.style.webkitTransition = ''; - this.style.transform = this.style.webkitTransform = ''; - this.style.display = ''; - this.scrollTop = this.scrollTop; - }, - _applyFocus: function() { - if (this.opened) { - if (!this.noAutoFocus) { - this._focusNode.focus(); - } - } else { - this._focusNode.blur(); - this._focusedChild = null; - if (this.restoreFocusOnClose && this.__restoreFocusNode) { - this.__restoreFocusNode.focus(); - } - this.__restoreFocusNode = null; - var currentOverlay = this._manager.currentOverlay(); - if (currentOverlay && this !== currentOverlay) { - currentOverlay._applyFocus(); - } - } - }, - _onCaptureClick: function(event) { - if (!this.noCancelOnOutsideClick) { - this.cancel(event); - } - }, - _onCaptureFocus: function(event) { - if (!this.withBackdrop) { - return; - } - var path = Polymer.dom(event).path; - if (path.indexOf(this) === -1) { - event.stopPropagation(); - this._applyFocus(); - } else { - this._focusedChild = path[0]; - } - }, - _onCaptureEsc: function(event) { - if (!this.noCancelOnEscKey) { - this.cancel(event); - } - }, - _onCaptureTab: function(event) { - if (!this.withBackdrop) { - return; - } - var shift = event.shiftKey; - var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode; - var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode; - var shouldWrap = false; - if (nodeToCheck === nodeToSet) { - shouldWrap = true; - } else { - var focusedNode = this._manager.deepActiveElement; - shouldWrap = focusedNode === nodeToCheck || focusedNode === this; - } - if (shouldWrap) { - event.preventDefault(); - this._focusedChild = nodeToSet; - this._applyFocus(); - } - }, - _onIronResize: function() { - if (this.opened && !this.__isAnimating) { - this.__onNextAnimationFrame(this.refit); - } - }, - _onNodesChange: function() { - if (this.opened && !this.__isAnimating) { - this.notifyResize(); - } - }, - __openedChanged: function() { - if (this.opened) { - this._prepareRenderOpened(); - this._manager.addOverlay(this); - this._applyFocus(); - this._renderOpened(); - } else { - this._manager.removeOverlay(this); - this._applyFocus(); - this._renderClosed(); - } - }, - __onNextAnimationFrame: function(callback) { - if (this.__raf) { - window.cancelAnimationFrame(this.__raf); - } - var self = this; - this.__raf = window.requestAnimationFrame(function nextAnimationFrame() { - self.__raf = null; - callback.call(self); - }); - } - }; - Polymer.IronOverlayBehavior = [ Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl ]; -})(); - -Polymer.NeonAnimatableBehavior = { - properties: { - animationConfig: { - type: Object - }, - entryAnimation: { - observer: '_entryAnimationChanged', - type: String - }, - exitAnimation: { - observer: '_exitAnimationChanged', - type: String - } - }, - _entryAnimationChanged: function() { - this.animationConfig = this.animationConfig || {}; - this.animationConfig['entry'] = [ { - name: this.entryAnimation, - node: this - } ]; - }, - _exitAnimationChanged: function() { - this.animationConfig = this.animationConfig || {}; - this.animationConfig['exit'] = [ { - name: this.exitAnimation, - node: this - } ]; - }, - _copyProperties: function(config1, config2) { - for (var property in config2) { - config1[property] = config2[property]; - } - }, - _cloneConfig: function(config) { - var clone = { - isClone: true - }; - this._copyProperties(clone, config); - return clone; - }, - _getAnimationConfigRecursive: function(type, map, allConfigs) { - if (!this.animationConfig) { - return; - } - if (this.animationConfig.value && typeof this.animationConfig.value === 'function') { - this._warn(this._logf('playAnimation', "Please put 'animationConfig' inside of your components 'properties' object instead of outside of it.")); - return; - } - var thisConfig; - if (type) { - thisConfig = this.animationConfig[type]; - } else { - thisConfig = this.animationConfig; - } - if (!Array.isArray(thisConfig)) { - thisConfig = [ thisConfig ]; - } - if (thisConfig) { - for (var config, index = 0; config = thisConfig[index]; index++) { - if (config.animatable) { - config.animatable._getAnimationConfigRecursive(config.type || type, map, allConfigs); - } else { - if (config.id) { - var cachedConfig = map[config.id]; - if (cachedConfig) { - if (!cachedConfig.isClone) { - map[config.id] = this._cloneConfig(cachedConfig); - cachedConfig = map[config.id]; - } - this._copyProperties(cachedConfig, config); - } else { - map[config.id] = config; - } - } else { - allConfigs.push(config); - } - } - } - } - }, - getAnimationConfig: function(type) { - var map = {}; - var allConfigs = []; - this._getAnimationConfigRecursive(type, map, allConfigs); - for (var key in map) { - allConfigs.push(map[key]); - } - return allConfigs; - } -}; - -Polymer.NeonAnimationRunnerBehaviorImpl = { - _configureAnimations: function(configs) { - var results = []; - if (configs.length > 0) { - for (var config, index = 0; config = configs[index]; index++) { - var neonAnimation = document.createElement(config.name); - if (neonAnimation.isNeonAnimation) { - var result = null; - try { - result = neonAnimation.configure(config); - if (typeof result.cancel != 'function') { - result = document.timeline.play(result); - } - } catch (e) { - result = null; - console.warn('Couldnt play', '(', config.name, ').', e); - } - if (result) { - results.push({ - neonAnimation: neonAnimation, - config: config, - animation: result - }); - } - } else { - console.warn(this.is + ':', config.name, 'not found!'); - } - } - } - return results; - }, - _shouldComplete: function(activeEntries) { - var finished = true; - for (var i = 0; i < activeEntries.length; i++) { - if (activeEntries[i].animation.playState != 'finished') { - finished = false; - break; - } - } - return finished; - }, - _complete: function(activeEntries) { - for (var i = 0; i < activeEntries.length; i++) { - activeEntries[i].neonAnimation.complete(activeEntries[i].config); - } - for (var i = 0; i < activeEntries.length; i++) { - activeEntries[i].animation.cancel(); - } - }, - playAnimation: function(type, cookie) { - var configs = this.getAnimationConfig(type); - if (!configs) { - return; - } - this._active = this._active || {}; - if (this._active[type]) { - this._complete(this._active[type]); - delete this._active[type]; - } - var activeEntries = this._configureAnimations(configs); - if (activeEntries.length == 0) { - this.fire('neon-animation-finish', cookie, { - bubbles: false - }); - return; - } - this._active[type] = activeEntries; - for (var i = 0; i < activeEntries.length; i++) { - activeEntries[i].animation.onfinish = function() { - if (this._shouldComplete(activeEntries)) { - this._complete(activeEntries); - delete this._active[type]; - this.fire('neon-animation-finish', cookie, { - bubbles: false - }); - } - }.bind(this); - } - }, - cancelAnimation: function() { - for (var k in this._animations) { - this._animations[k].cancel(); - } - this._animations = {}; - } -}; - -Polymer.NeonAnimationRunnerBehavior = [ Polymer.NeonAnimatableBehavior, Polymer.NeonAnimationRunnerBehaviorImpl ]; - -Polymer.NeonAnimationBehavior = { - properties: { - animationTiming: { - type: Object, - value: function() { - return { - duration: 500, - easing: 'cubic-bezier(0.4, 0, 0.2, 1)', - fill: 'both' - }; - } - } - }, - isNeonAnimation: true, - timingFromConfig: function(config) { - if (config.timing) { - for (var property in config.timing) { - this.animationTiming[property] = config.timing[property]; - } - } - return this.animationTiming; - }, - setPrefixedProperty: function(node, property, value) { - var map = { - transform: [ 'webkitTransform' ], - transformOrigin: [ 'mozTransformOrigin', 'webkitTransformOrigin' ] - }; - var prefixes = map[property]; - for (var prefix, index = 0; prefix = prefixes[index]; index++) { - node.style[prefix] = value; - } - node.style[property] = value; - }, - complete: function() {} -}; - -Polymer({ - is: 'opaque-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - this._effect = new KeyframeEffect(node, [ { - opacity: '1' - }, { - opacity: '1' - } ], this.timingFromConfig(config)); - node.style.opacity = '0'; - return this._effect; - }, - complete: function(config) { - config.node.style.opacity = ''; - } -}); - -(function() { - 'use strict'; - var LAST_TOUCH_POSITION = { - pageX: 0, - pageY: 0 - }; - var ROOT_TARGET = null; - var SCROLLABLE_NODES = []; - Polymer.IronDropdownScrollManager = { - get currentLockingElement() { - return this._lockingElements[this._lockingElements.length - 1]; - }, - elementIsScrollLocked: function(element) { - var currentLockingElement = this.currentLockingElement; - if (currentLockingElement === undefined) return false; - var scrollLocked; - if (this._hasCachedLockedElement(element)) { - return true; - } - if (this._hasCachedUnlockedElement(element)) { - return false; - } - scrollLocked = !!currentLockingElement && currentLockingElement !== element && !this._composedTreeContains(currentLockingElement, element); - if (scrollLocked) { - this._lockedElementCache.push(element); - } else { - this._unlockedElementCache.push(element); - } - return scrollLocked; - }, - pushScrollLock: function(element) { - if (this._lockingElements.indexOf(element) >= 0) { - return; - } - if (this._lockingElements.length === 0) { - this._lockScrollInteractions(); - } - this._lockingElements.push(element); - this._lockedElementCache = []; - this._unlockedElementCache = []; - }, - removeScrollLock: function(element) { - var index = this._lockingElements.indexOf(element); - if (index === -1) { - return; - } - this._lockingElements.splice(index, 1); - this._lockedElementCache = []; - this._unlockedElementCache = []; - if (this._lockingElements.length === 0) { - this._unlockScrollInteractions(); - } - }, - _lockingElements: [], - _lockedElementCache: null, - _unlockedElementCache: null, - _hasCachedLockedElement: function(element) { - return this._lockedElementCache.indexOf(element) > -1; - }, - _hasCachedUnlockedElement: function(element) { - return this._unlockedElementCache.indexOf(element) > -1; - }, - _composedTreeContains: function(element, child) { - var contentElements; - var distributedNodes; - var contentIndex; - var nodeIndex; - if (element.contains(child)) { - return true; - } - contentElements = Polymer.dom(element).querySelectorAll('content'); - for (contentIndex = 0; contentIndex < contentElements.length; ++contentIndex) { - distributedNodes = Polymer.dom(contentElements[contentIndex]).getDistributedNodes(); - for (nodeIndex = 0; nodeIndex < distributedNodes.length; ++nodeIndex) { - if (this._composedTreeContains(distributedNodes[nodeIndex], child)) { - return true; - } - } - } - return false; - }, - _scrollInteractionHandler: function(event) { - if (event.cancelable && this._shouldPreventScrolling(event)) { - event.preventDefault(); - } - if (event.targetTouches) { - var touch = event.targetTouches[0]; - LAST_TOUCH_POSITION.pageX = touch.pageX; - LAST_TOUCH_POSITION.pageY = touch.pageY; - } - }, - _lockScrollInteractions: function() { - this._boundScrollHandler = this._boundScrollHandler || this._scrollInteractionHandler.bind(this); - document.addEventListener('wheel', this._boundScrollHandler, true); - document.addEventListener('mousewheel', this._boundScrollHandler, true); - document.addEventListener('DOMMouseScroll', this._boundScrollHandler, true); - document.addEventListener('touchstart', this._boundScrollHandler, true); - document.addEventListener('touchmove', this._boundScrollHandler, true); - }, - _unlockScrollInteractions: function() { - document.removeEventListener('wheel', this._boundScrollHandler, true); - document.removeEventListener('mousewheel', this._boundScrollHandler, true); - document.removeEventListener('DOMMouseScroll', this._boundScrollHandler, true); - document.removeEventListener('touchstart', this._boundScrollHandler, true); - document.removeEventListener('touchmove', this._boundScrollHandler, true); - }, - _shouldPreventScrolling: function(event) { - var target = Polymer.dom(event).rootTarget; - if (event.type !== 'touchmove' && ROOT_TARGET !== target) { - ROOT_TARGET = target; - SCROLLABLE_NODES = this._getScrollableNodes(Polymer.dom(event).path); - } - if (!SCROLLABLE_NODES.length) { - return true; - } - if (event.type === 'touchstart') { - return false; - } - var info = this._getScrollInfo(event); - return !this._getScrollingNode(SCROLLABLE_NODES, info.deltaX, info.deltaY); - }, - _getScrollableNodes: function(nodes) { - var scrollables = []; - var lockingIndex = nodes.indexOf(this.currentLockingElement); - for (var i = 0; i <= lockingIndex; i++) { - var node = nodes[i]; - if (node.nodeType === 11) { - continue; - } - var style = node.style; - if (style.overflow !== 'scroll' && style.overflow !== 'auto') { - style = window.getComputedStyle(node); - } - if (style.overflow === 'scroll' || style.overflow === 'auto') { - scrollables.push(node); - } - } - return scrollables; - }, - _getScrollingNode: function(nodes, deltaX, deltaY) { - if (!deltaX && !deltaY) { - return; - } - var verticalScroll = Math.abs(deltaY) >= Math.abs(deltaX); - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - var canScroll = false; - if (verticalScroll) { - canScroll = deltaY < 0 ? node.scrollTop > 0 : node.scrollTop < node.scrollHeight - node.clientHeight; - } else { - canScroll = deltaX < 0 ? node.scrollLeft > 0 : node.scrollLeft < node.scrollWidth - node.clientWidth; - } - if (canScroll) { - return node; - } - } - }, - _getScrollInfo: function(event) { - var info = { - deltaX: event.deltaX, - deltaY: event.deltaY - }; - if ('deltaX' in event) {} else if ('wheelDeltaX' in event) { - info.deltaX = -event.wheelDeltaX; - info.deltaY = -event.wheelDeltaY; - } else if ('axis' in event) { - info.deltaX = event.axis === 1 ? event.detail : 0; - info.deltaY = event.axis === 2 ? event.detail : 0; - } else if (event.targetTouches) { - var touch = event.targetTouches[0]; - info.deltaX = LAST_TOUCH_POSITION.pageX - touch.pageX; - info.deltaY = LAST_TOUCH_POSITION.pageY - touch.pageY; - } - return info; - } - }; -})(); - -(function() { - 'use strict'; - Polymer({ - is: 'iron-dropdown', - behaviors: [ Polymer.IronControlState, Polymer.IronA11yKeysBehavior, Polymer.IronOverlayBehavior, Polymer.NeonAnimationRunnerBehavior ], - properties: { - horizontalAlign: { - type: String, - value: 'left', - reflectToAttribute: true - }, - verticalAlign: { - type: String, - value: 'top', - reflectToAttribute: true - }, - openAnimationConfig: { - type: Object - }, - closeAnimationConfig: { - type: Object - }, - focusTarget: { - type: Object - }, - noAnimations: { - type: Boolean, - value: false - }, - allowOutsideScroll: { - type: Boolean, - value: false - }, - _boundOnCaptureScroll: { - type: Function, - value: function() { - return this._onCaptureScroll.bind(this); - } - } - }, - listeners: { - 'neon-animation-finish': '_onNeonAnimationFinish' - }, - observers: [ '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)' ], - get containedElement() { - return Polymer.dom(this.$.content).getDistributedNodes()[0]; - }, - get _focusTarget() { - return this.focusTarget || this.containedElement; - }, - ready: function() { - this._scrollTop = 0; - this._scrollLeft = 0; - this._refitOnScrollRAF = null; - }, - attached: function() { - if (!this.sizingTarget || this.sizingTarget === this) { - this.sizingTarget = this.containedElement; - } - }, - detached: function() { - this.cancelAnimation(); - document.removeEventListener('scroll', this._boundOnCaptureScroll); - Polymer.IronDropdownScrollManager.removeScrollLock(this); - }, - _openedChanged: function() { - if (this.opened && this.disabled) { - this.cancel(); - } else { - this.cancelAnimation(); - this._updateAnimationConfig(); - this._saveScrollPosition(); - if (this.opened) { - document.addEventListener('scroll', this._boundOnCaptureScroll); - !this.allowOutsideScroll && Polymer.IronDropdownScrollManager.pushScrollLock(this); - } else { - document.removeEventListener('scroll', this._boundOnCaptureScroll); - Polymer.IronDropdownScrollManager.removeScrollLock(this); - } - Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments); - } - }, - _renderOpened: function() { - if (!this.noAnimations && this.animationConfig.open) { - this.$.contentWrapper.classList.add('animating'); - this.playAnimation('open'); - } else { - Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments); - } - }, - _renderClosed: function() { - if (!this.noAnimations && this.animationConfig.close) { - this.$.contentWrapper.classList.add('animating'); - this.playAnimation('close'); - } else { - Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments); - } - }, - _onNeonAnimationFinish: function() { - this.$.contentWrapper.classList.remove('animating'); - if (this.opened) { - this._finishRenderOpened(); - } else { - this._finishRenderClosed(); - } - }, - _onCaptureScroll: function() { - if (!this.allowOutsideScroll) { - this._restoreScrollPosition(); - } else { - this._refitOnScrollRAF && window.cancelAnimationFrame(this._refitOnScrollRAF); - this._refitOnScrollRAF = window.requestAnimationFrame(this.refit.bind(this)); - } - }, - _saveScrollPosition: function() { - if (document.scrollingElement) { - this._scrollTop = document.scrollingElement.scrollTop; - this._scrollLeft = document.scrollingElement.scrollLeft; - } else { - this._scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop); - this._scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); - } - }, - _restoreScrollPosition: function() { - if (document.scrollingElement) { - document.scrollingElement.scrollTop = this._scrollTop; - document.scrollingElement.scrollLeft = this._scrollLeft; - } else { - document.documentElement.scrollTop = this._scrollTop; - document.documentElement.scrollLeft = this._scrollLeft; - document.body.scrollTop = this._scrollTop; - document.body.scrollLeft = this._scrollLeft; - } - }, - _updateAnimationConfig: function() { - var animations = (this.openAnimationConfig || []).concat(this.closeAnimationConfig || []); - for (var i = 0; i < animations.length; i++) { - animations[i].node = this.containedElement; - } - this.animationConfig = { - open: this.openAnimationConfig, - close: this.closeAnimationConfig - }; - }, - _updateOverlayPosition: function() { - if (this.isAttached) { - this.notifyResize(); - } - }, - _applyFocus: function() { - var focusTarget = this.focusTarget || this.containedElement; - if (focusTarget && this.opened && !this.noAutoFocus) { - focusTarget.focus(); - } else { - Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); - } - } - }); -})(); - -Polymer({ - is: 'fade-in-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - this._effect = new KeyframeEffect(node, [ { - opacity: '0' - }, { - opacity: '1' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'fade-out-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - this._effect = new KeyframeEffect(node, [ { - opacity: '1' - }, { - opacity: '0' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'paper-menu-grow-height-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - var rect = node.getBoundingClientRect(); - var height = rect.height; - this._effect = new KeyframeEffect(node, [ { - height: height / 2 + 'px' - }, { - height: height + 'px' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'paper-menu-grow-width-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - var rect = node.getBoundingClientRect(); - var width = rect.width; - this._effect = new KeyframeEffect(node, [ { - width: width / 2 + 'px' - }, { - width: width + 'px' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'paper-menu-shrink-width-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - var rect = node.getBoundingClientRect(); - var width = rect.width; - this._effect = new KeyframeEffect(node, [ { - width: width + 'px' - }, { - width: width - width / 20 + 'px' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'paper-menu-shrink-height-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - var rect = node.getBoundingClientRect(); - var height = rect.height; - var top = rect.top; - this.setPrefixedProperty(node, 'transformOrigin', '0 0'); - this._effect = new KeyframeEffect(node, [ { - height: height + 'px', - transform: 'translateY(0)' - }, { - height: height / 2 + 'px', - transform: 'translateY(-20px)' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -(function() { - 'use strict'; - var config = { - ANIMATION_CUBIC_BEZIER: 'cubic-bezier(.3,.95,.5,1)', - MAX_ANIMATION_TIME_MS: 400 - }; - var PaperMenuButton = Polymer({ - is: 'paper-menu-button', - behaviors: [ Polymer.IronA11yKeysBehavior, Polymer.IronControlState ], - properties: { - opened: { - type: Boolean, - value: false, - notify: true, - observer: '_openedChanged' - }, - horizontalAlign: { - type: String, - value: 'left', - reflectToAttribute: true - }, - verticalAlign: { - type: String, - value: 'top', - reflectToAttribute: true - }, - dynamicAlign: { - type: Boolean - }, - horizontalOffset: { - type: Number, - value: 0, - notify: true - }, - verticalOffset: { - type: Number, - value: 0, - notify: true - }, - noOverlap: { - type: Boolean - }, - noAnimations: { - type: Boolean, - value: false - }, - ignoreSelect: { - type: Boolean, - value: false - }, - closeOnActivate: { - type: Boolean, - value: false - }, - openAnimationConfig: { - type: Object, - value: function() { - return [ { - name: 'fade-in-animation', - timing: { - delay: 100, - duration: 200 - } - }, { - name: 'paper-menu-grow-width-animation', - timing: { - delay: 100, - duration: 150, - easing: config.ANIMATION_CUBIC_BEZIER - } - }, { - name: 'paper-menu-grow-height-animation', - timing: { - delay: 100, - duration: 275, - easing: config.ANIMATION_CUBIC_BEZIER - } - } ]; - } - }, - closeAnimationConfig: { - type: Object, - value: function() { - return [ { - name: 'fade-out-animation', - timing: { - duration: 150 - } - }, { - name: 'paper-menu-shrink-width-animation', - timing: { - delay: 100, - duration: 50, - easing: config.ANIMATION_CUBIC_BEZIER - } - }, { - name: 'paper-menu-shrink-height-animation', - timing: { - duration: 200, - easing: 'ease-in' - } - } ]; - } - }, - allowOutsideScroll: { - type: Boolean, - value: false - }, - restoreFocusOnClose: { - type: Boolean, - value: true - }, - _dropdownContent: { - type: Object - } - }, - hostAttributes: { - role: 'group', - 'aria-haspopup': 'true' - }, - listeners: { - 'iron-activate': '_onIronActivate', - 'iron-select': '_onIronSelect' - }, - get contentElement() { - return Polymer.dom(this.$.content).getDistributedNodes()[0]; - }, - toggle: function() { - if (this.opened) { - this.close(); - } else { - this.open(); - } - }, - open: function() { - if (this.disabled) { - return; - } - this.$.dropdown.open(); - }, - close: function() { - this.$.dropdown.close(); - }, - _onIronSelect: function(event) { - if (!this.ignoreSelect) { - this.close(); - } - }, - _onIronActivate: function(event) { - if (this.closeOnActivate) { - this.close(); - } - }, - _openedChanged: function(opened, oldOpened) { - if (opened) { - this._dropdownContent = this.contentElement; - this.fire('paper-dropdown-open'); - } else if (oldOpened != null) { - this.fire('paper-dropdown-close'); - } - }, - _disabledChanged: function(disabled) { - Polymer.IronControlState._disabledChanged.apply(this, arguments); - if (disabled && this.opened) { - this.close(); - } - }, - __onIronOverlayCanceled: function(event) { - var uiEvent = event.detail; - var target = Polymer.dom(uiEvent).rootTarget; - var trigger = this.$.trigger; - var path = Polymer.dom(uiEvent).path; - if (path.indexOf(trigger) > -1) { - event.preventDefault(); - } - } - }); - Object.keys(config).forEach(function(key) { - PaperMenuButton[key] = config[key]; - }); - Polymer.PaperMenuButton = PaperMenuButton; -})(); - -Polymer.PaperInkyFocusBehaviorImpl = { - observers: [ '_focusedChanged(receivedFocusFromKeyboard)' ], - _focusedChanged: function(receivedFocusFromKeyboard) { - if (receivedFocusFromKeyboard) { - this.ensureRipple(); - } - if (this.hasRipple()) { - this._ripple.holdDown = receivedFocusFromKeyboard; - } - }, - _createRipple: function() { - var ripple = Polymer.PaperRippleBehavior._createRipple(); - ripple.id = 'ink'; - ripple.setAttribute('center', ''); - ripple.classList.add('circle'); - return ripple; - } -}; - -Polymer.PaperInkyFocusBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, Polymer.PaperRippleBehavior, Polymer.PaperInkyFocusBehaviorImpl ]; - -Polymer({ - is: 'paper-icon-button', - hostAttributes: { - role: 'button', - tabindex: '0' - }, - behaviors: [ Polymer.PaperInkyFocusBehavior ], - properties: { - src: { - type: String - }, - icon: { - type: String - }, - alt: { - type: String, - observer: "_altChanged" - } - }, - _altChanged: function(newValue, oldValue) { - var label = this.getAttribute('aria-label'); - if (!label || oldValue == label) { - this.setAttribute('aria-label', newValue); - } - } -}); - -Polymer({ - is: 'iron-media-query', - properties: { - queryMatches: { - type: Boolean, - value: false, - readOnly: true, - notify: true - }, - query: { - type: String, - observer: 'queryChanged' - }, - full: { - type: Boolean, - value: false - }, - _boundMQHandler: { - value: function() { - return this.queryHandler.bind(this); - } - }, - _mq: { - value: null - } - }, - attached: function() { - this.style.display = 'none'; - this.queryChanged(); - }, - detached: function() { - this._remove(); - }, - _add: function() { - if (this._mq) { - this._mq.addListener(this._boundMQHandler); - } - }, - _remove: function() { - if (this._mq) { - this._mq.removeListener(this._boundMQHandler); - } - this._mq = null; - }, - queryChanged: function() { - this._remove(); - var query = this.query; - if (!query) { - return; - } - if (!this.full && query[0] !== '(') { - query = '(' + query + ')'; - } - this._mq = window.matchMedia(query); - this._add(); - this.queryHandler(this._mq); - }, - queryHandler: function(mq) { - this._setQueryMatches(mq.matches); - } -}); - -(function() { - 'use strict'; - Polymer.IronA11yAnnouncer = Polymer({ - is: 'iron-a11y-announcer', - properties: { - mode: { - type: String, - value: 'polite' - }, - _text: { - type: String, - value: '' - } - }, - created: function() { - if (!Polymer.IronA11yAnnouncer.instance) { - Polymer.IronA11yAnnouncer.instance = this; - } - document.body.addEventListener('iron-announce', this._onIronAnnounce.bind(this)); - }, - announce: function(text) { - this._text = ''; - this.async(function() { - this._text = text; - }, 100); - }, - _onIronAnnounce: function(event) { - if (event.detail && event.detail.text) { - this.announce(event.detail.text); - } - } - }); - Polymer.IronA11yAnnouncer.instance = null; - Polymer.IronA11yAnnouncer.requestAvailability = function() { - if (!Polymer.IronA11yAnnouncer.instance) { - Polymer.IronA11yAnnouncer.instance = document.createElement('iron-a11y-announcer'); - } - document.body.appendChild(Polymer.IronA11yAnnouncer.instance); - }; -})(); - -Polymer.IronValidatableBehaviorMeta = null; - -Polymer.IronValidatableBehavior = { - properties: { - validator: { - type: String - }, - invalid: { - notify: true, - reflectToAttribute: true, - type: Boolean, - value: false - }, - _validatorMeta: { - type: Object - }, - validatorType: { - type: String, - value: 'validator' - }, - _validator: { - type: Object, - computed: '__computeValidator(validator)' - } - }, - observers: [ '_invalidChanged(invalid)' ], - registered: function() { - Polymer.IronValidatableBehaviorMeta = new Polymer.IronMeta({ - type: 'validator' - }); - }, - _invalidChanged: function() { - if (this.invalid) { - this.setAttribute('aria-invalid', 'true'); - } else { - this.removeAttribute('aria-invalid'); - } - }, - hasValidator: function() { - return this._validator != null; - }, - validate: function(value) { - this.invalid = !this._getValidity(value); - return !this.invalid; - }, - _getValidity: function(value) { - if (this.hasValidator()) { - return this._validator.validate(value); - } - return true; - }, - __computeValidator: function() { - return Polymer.IronValidatableBehaviorMeta && Polymer.IronValidatableBehaviorMeta.byKey(this.validator); - } -}; - -Polymer({ - is: 'iron-input', - "extends": 'input', - behaviors: [ Polymer.IronValidatableBehavior ], - properties: { - bindValue: { - observer: '_bindValueChanged', - type: String - }, - preventInvalidInput: { - type: Boolean - }, - allowedPattern: { - type: String, - observer: "_allowedPatternChanged" - }, - _previousValidInput: { - type: String, - value: '' - }, - _patternAlreadyChecked: { - type: Boolean, - value: false - } - }, - listeners: { - input: '_onInput', - keypress: '_onKeypress' - }, - registered: function() { - if (!this._canDispatchEventOnDisabled()) { - this._origDispatchEvent = this.dispatchEvent; - this.dispatchEvent = this._dispatchEventFirefoxIE; - } - }, - created: function() { - Polymer.IronA11yAnnouncer.requestAvailability(); - }, - _canDispatchEventOnDisabled: function() { - var input = document.createElement('input'); - var canDispatch = false; - input.disabled = true; - input.addEventListener('feature-check-dispatch-event', function() { - canDispatch = true; - }); - try { - input.dispatchEvent(new Event('feature-check-dispatch-event')); - } catch (e) {} - return canDispatch; - }, - _dispatchEventFirefoxIE: function() { - var disabled = this.disabled; - this.disabled = false; - this._origDispatchEvent.apply(this, arguments); - this.disabled = disabled; - }, - get _patternRegExp() { - var pattern; - if (this.allowedPattern) { - pattern = new RegExp(this.allowedPattern); - } else { - switch (this.type) { - case 'number': - pattern = /[0-9.,e-]/; - break; - } - } - return pattern; - }, - ready: function() { - this.bindValue = this.value; - }, - _bindValueChanged: function() { - if (this.value !== this.bindValue) { - this.value = !(this.bindValue || this.bindValue === 0 || this.bindValue === false) ? '' : this.bindValue; - } - this.fire('bind-value-changed', { - value: this.bindValue - }); - }, - _allowedPatternChanged: function() { - this.preventInvalidInput = this.allowedPattern ? true : false; - }, - _onInput: function() { - if (this.preventInvalidInput && !this._patternAlreadyChecked) { - var valid = this._checkPatternValidity(); - if (!valid) { - this._announceInvalidCharacter('Invalid string of characters not entered.'); - this.value = this._previousValidInput; - } - } - this.bindValue = this.value; - this._previousValidInput = this.value; - this._patternAlreadyChecked = false; - }, - _isPrintable: function(event) { - var anyNonPrintable = event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 13 || event.keyCode == 27; - var mozNonPrintable = event.keyCode == 19 || event.keyCode == 20 || event.keyCode == 45 || event.keyCode == 46 || event.keyCode == 144 || event.keyCode == 145 || event.keyCode > 32 && event.keyCode < 41 || event.keyCode > 111 && event.keyCode < 124; - return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable); - }, - _onKeypress: function(event) { - if (!this.preventInvalidInput && this.type !== 'number') { - return; - } - var regexp = this._patternRegExp; - if (!regexp) { - return; - } - if (event.metaKey || event.ctrlKey || event.altKey) return; - this._patternAlreadyChecked = true; - var thisChar = String.fromCharCode(event.charCode); - if (this._isPrintable(event) && !regexp.test(thisChar)) { - event.preventDefault(); - this._announceInvalidCharacter('Invalid character ' + thisChar + ' not entered.'); - } - }, - _checkPatternValidity: function() { - var regexp = this._patternRegExp; - if (!regexp) { - return true; - } - for (var i = 0; i < this.value.length; i++) { - if (!regexp.test(this.value[i])) { - return false; - } - } - return true; - }, - validate: function() { - var valid = this.checkValidity(); - if (valid) { - if (this.required && this.value === '') { - valid = false; - } else if (this.hasValidator()) { - valid = Polymer.IronValidatableBehavior.validate.call(this, this.value); - } - } - this.invalid = !valid; - this.fire('iron-input-validate'); - return valid; - }, - _announceInvalidCharacter: function(message) { - this.fire('iron-announce', { - text: message - }); - } -}); - -Polymer({ - is: 'paper-input-container', - properties: { - noLabelFloat: { - type: Boolean, - value: false - }, - alwaysFloatLabel: { - type: Boolean, - value: false - }, - attrForValue: { - type: String, - value: 'bind-value' - }, - autoValidate: { - type: Boolean, - value: false - }, - invalid: { - observer: '_invalidChanged', - type: Boolean, - value: false - }, - focused: { - readOnly: true, - type: Boolean, - value: false, - notify: true - }, - _addons: { - type: Array - }, - _inputHasContent: { - type: Boolean, - value: false - }, - _inputSelector: { - type: String, - value: 'input,textarea,.paper-input-input' - }, - _boundOnFocus: { - type: Function, - value: function() { - return this._onFocus.bind(this); - } - }, - _boundOnBlur: { - type: Function, - value: function() { - return this._onBlur.bind(this); - } - }, - _boundOnInput: { - type: Function, - value: function() { - return this._onInput.bind(this); - } - }, - _boundValueChanged: { - type: Function, - value: function() { - return this._onValueChanged.bind(this); - } - } - }, - listeners: { - 'addon-attached': '_onAddonAttached', - 'iron-input-validate': '_onIronInputValidate' - }, - get _valueChangedEvent() { - return this.attrForValue + '-changed'; - }, - get _propertyForValue() { - return Polymer.CaseMap.dashToCamelCase(this.attrForValue); - }, - get _inputElement() { - return Polymer.dom(this).querySelector(this._inputSelector); - }, - get _inputElementValue() { - return this._inputElement[this._propertyForValue] || this._inputElement.value; - }, - ready: function() { - if (!this._addons) { - this._addons = []; - } - this.addEventListener('focus', this._boundOnFocus, true); - this.addEventListener('blur', this._boundOnBlur, true); - }, - attached: function() { - if (this.attrForValue) { - this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged); - } else { - this.addEventListener('input', this._onInput); - } - if (this._inputElementValue != '') { - this._handleValueAndAutoValidate(this._inputElement); - } else { - this._handleValue(this._inputElement); - } - }, - _onAddonAttached: function(event) { - if (!this._addons) { - this._addons = []; - } - var target = event.target; - if (this._addons.indexOf(target) === -1) { - this._addons.push(target); - if (this.isAttached) { - this._handleValue(this._inputElement); - } - } - }, - _onFocus: function() { - this._setFocused(true); - }, - _onBlur: function() { - this._setFocused(false); - this._handleValueAndAutoValidate(this._inputElement); - }, - _onInput: function(event) { - this._handleValueAndAutoValidate(event.target); - }, - _onValueChanged: function(event) { - this._handleValueAndAutoValidate(event.target); - }, - _handleValue: function(inputElement) { - var value = this._inputElementValue; - if (value || value === 0 || inputElement.type === 'number' && !inputElement.checkValidity()) { - this._inputHasContent = true; - } else { - this._inputHasContent = false; - } - this.updateAddons({ - inputElement: inputElement, - value: value, - invalid: this.invalid - }); - }, - _handleValueAndAutoValidate: function(inputElement) { - if (this.autoValidate) { - var valid; - if (inputElement.validate) { - valid = inputElement.validate(this._inputElementValue); - } else { - valid = inputElement.checkValidity(); - } - this.invalid = !valid; - } - this._handleValue(inputElement); - }, - _onIronInputValidate: function(event) { - this.invalid = this._inputElement.invalid; - }, - _invalidChanged: function() { - if (this._addons) { - this.updateAddons({ - invalid: this.invalid - }); - } - }, - updateAddons: function(state) { - for (var addon, index = 0; addon = this._addons[index]; index++) { - addon.update(state); - } - }, - _computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) { - var cls = 'input-content'; - if (!noLabelFloat) { - var label = this.querySelector('label'); - if (alwaysFloatLabel || _inputHasContent) { - cls += ' label-is-floating'; - this.$.labelAndInputContainer.style.position = 'static'; - if (invalid) { - cls += ' is-invalid'; - } else if (focused) { - cls += " label-is-highlighted"; - } - } else { - if (label) { - this.$.labelAndInputContainer.style.position = 'relative'; - } - } - } else { - if (_inputHasContent) { - cls += ' label-is-hidden'; - } - } - return cls; - }, - _computeUnderlineClass: function(focused, invalid) { - var cls = 'underline'; - if (invalid) { - cls += ' is-invalid'; - } else if (focused) { - cls += ' is-highlighted'; - } - return cls; - }, - _computeAddOnContentClass: function(focused, invalid) { - var cls = 'add-on-content'; - if (invalid) { - cls += ' is-invalid'; - } else if (focused) { - cls += ' is-highlighted'; - } - return cls; - } -}); - -Polymer.PaperSpinnerBehavior = { - listeners: { - animationend: '__reset', - webkitAnimationEnd: '__reset' - }, - properties: { - active: { - type: Boolean, - value: false, - reflectToAttribute: true, - observer: '__activeChanged' - }, - alt: { - type: String, - value: 'loading', - observer: '__altChanged' - }, - __coolingDown: { - type: Boolean, - value: false - } - }, - __computeContainerClasses: function(active, coolingDown) { - return [ active || coolingDown ? 'active' : '', coolingDown ? 'cooldown' : '' ].join(' '); - }, - __activeChanged: function(active, old) { - this.__setAriaHidden(!active); - this.__coolingDown = !active && old; - }, - __altChanged: function(alt) { - if (alt === this.getPropertyInfo('alt').value) { - this.alt = this.getAttribute('aria-label') || alt; - } else { - this.__setAriaHidden(alt === ''); - this.setAttribute('aria-label', alt); - } - }, - __setAriaHidden: function(hidden) { - var attr = 'aria-hidden'; - if (hidden) { - this.setAttribute(attr, 'true'); - } else { - this.removeAttribute(attr); - } - }, - __reset: function() { - this.active = false; - this.__coolingDown = false; - } -}; - -Polymer({ - is: 'paper-spinner-lite', - behaviors: [ Polymer.PaperSpinnerBehavior ] -}); - +cr.define("downloads",function(){var Item=Polymer({is:"downloads-item",properties:{data:{type:Object},completelyOnDisk_:{computed:"computeCompletelyOnDisk_("+"data.state, data.file_externally_removed)",type:Boolean,value:true},controlledBy_:{computed:"computeControlledBy_(data.by_ext_id, data.by_ext_name)",type:String,value:""},isActive_:{computed:"computeIsActive_("+"data.state, data.file_externally_removed)",type:Boolean,value:true},isDangerous_:{computed:"computeIsDangerous_(data.state)",type:Boolean,value:false},isMalware_:{computed:"computeIsMalware_(isDangerous_, data.danger_type)",type:Boolean,value:false},isInProgress_:{computed:"computeIsInProgress_(data.state)",type:Boolean,value:false},pauseOrResumeText_:{computed:"computePauseOrResumeText_(isInProgress_, data.resume)",type:String},showCancel_:{computed:"computeShowCancel_(data.state)",type:Boolean,value:false},showProgress_:{computed:"computeShowProgress_(showCancel_, data.percent)",type:Boolean,value:false}},observers:["observeControlledBy_(controlledBy_)","observeIsDangerous_(isDangerous_, data)"],ready:function(){this.content=this.$.content},computeClass_:function(){var classes=[];if(this.isActive_)classes.push("is-active");if(this.isDangerous_)classes.push("dangerous");if(this.showProgress_)classes.push("show-progress");return classes.join(" ")},computeCompletelyOnDisk_:function(){return this.data.state==downloads.States.COMPLETE&&!this.data.file_externally_removed},computeControlledBy_:function(){if(!this.data.by_ext_id||!this.data.by_ext_name)return"";var url="chrome://extensions#"+this.data.by_ext_id;var name=this.data.by_ext_name;return loadTimeData.getStringF("controlledByUrl",url,name)},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"downloads:remove-circle";default:return"cr:warning"}},computeDate_:function(){assert(typeof this.data.hideDate=="boolean");if(this.data.hideDate)return"";return assert(this.data.since_string||this.data.date_string)},computeDescription_:function(){var data=this.data;switch(data.state){case downloads.States.DANGEROUS:var fileName=data.file_name;switch(data.danger_type){case downloads.DangerType.DANGEROUS_FILE:return loadTimeData.getString("dangerFileDesc");case downloads.DangerType.DANGEROUS_URL:case downloads.DangerType.DANGEROUS_CONTENT:case downloads.DangerType.DANGEROUS_HOST:return loadTimeData.getString("dangerDownloadDesc");case downloads.DangerType.UNCOMMON_CONTENT:return loadTimeData.getString("dangerUncommonDesc");case downloads.DangerType.POTENTIALLY_UNWANTED:return loadTimeData.getString("dangerSettingsDesc")}break;case downloads.States.IN_PROGRESS:case downloads.States.PAUSED:return data.progress_status_text}return""},computeIsActive_:function(){return this.data.state!=downloads.States.CANCELLED&&this.data.state!=downloads.States.INTERRUPTED&&!this.data.file_externally_removed},computeIsDangerous_:function(){return this.data.state==downloads.States.DANGEROUS},computeIsInProgress_:function(){return this.data.state==downloads.States.IN_PROGRESS},computeIsMalware_:function(){return this.isDangerous_&&(this.data.danger_type==downloads.DangerType.DANGEROUS_CONTENT||this.data.danger_type==downloads.DangerType.DANGEROUS_HOST||this.data.danger_type==downloads.DangerType.DANGEROUS_URL||this.data.danger_type==downloads.DangerType.POTENTIALLY_UNWANTED)},computePauseOrResumeText_:function(){if(this.isInProgress_)return loadTimeData.getString("controlPause");if(this.data.resume)return loadTimeData.getString("controlResume");return""},computeRemoveStyle_:function(){var canDelete=loadTimeData.getBoolean("allowDeletingHistory");var hideRemove=this.isDangerous_||this.showCancel_||!canDelete;return hideRemove?"visibility: hidden":""},computeShowCancel_:function(){return this.data.state==downloads.States.IN_PROGRESS||this.data.state==downloads.States.PAUSED},computeShowProgress_:function(){return this.showCancel_&&this.data.percent>=-1},computeTag_:function(){switch(this.data.state){case downloads.States.CANCELLED:return loadTimeData.getString("statusCancelled");case downloads.States.INTERRUPTED:return this.data.last_reason_text;case downloads.States.COMPLETE:return this.data.file_externally_removed?loadTimeData.getString("statusRemoved"):""}return""},isIndeterminate_:function(){return this.data.percent==-1},observeControlledBy_:function(){this.$["controlled-by"].innerHTML=this.controlledBy_},observeIsDangerous_:function(){if(!this.data)return;if(this.isDangerous_){this.$.url.removeAttribute("href")}else{this.$.url.href=assert(this.data.url);var filePath=encodeURIComponent(this.data.file_path);var scaleFactor="?scale="+window.devicePixelRatio+"x";this.$["file-icon"].src="chrome://fileicon/"+filePath+scaleFactor}},onCancelTap_:function(){downloads.ActionService.getInstance().cancel(this.data.id)},onDiscardDangerousTap_:function(){downloads.ActionService.getInstance().discardDangerous(this.data.id)},onDragStart_:function(e){e.preventDefault();downloads.ActionService.getInstance().drag(this.data.id)},onFileLinkTap_:function(e){e.preventDefault();downloads.ActionService.getInstance().openFile(this.data.id)},onPauseOrResumeTap_:function(){if(this.isInProgress_)downloads.ActionService.getInstance().pause(this.data.id);else downloads.ActionService.getInstance().resume(this.data.id)},onRemoveTap_:function(){downloads.ActionService.getInstance().remove(this.data.id)},onRetryTap_:function(){downloads.ActionService.getInstance().download(this.data.url)},onSaveDangerousTap_:function(){downloads.ActionService.getInstance().saveDangerous(this.data.id)},onShowTap_:function(){downloads.ActionService.getInstance().show(this.data.id)}});return{Item:Item}});Polymer.PaperItemBehaviorImpl={hostAttributes:{role:"option",tabindex:"0"}};Polymer.PaperItemBehavior=[Polymer.IronButtonState,Polymer.IronControlState,Polymer.PaperItemBehaviorImpl];Polymer({is:"paper-item",behaviors:[Polymer.PaperItemBehavior]});Polymer.IronSelection=function(selectCallback){this.selection=[];this.selectCallback=selectCallback};Polymer.IronSelection.prototype={get:function(){return this.multi?this.selection.slice():this.selection[0]},clear:function(excludes){this.selection.slice().forEach(function(item){if(!excludes||excludes.indexOf(item)<0){this.setItemSelected(item,false)}},this)},isSelected:function(item){return this.selection.indexOf(item)>=0},setItemSelected:function(item,isSelected){if(item!=null){if(isSelected!==this.isSelected(item)){if(isSelected){this.selection.push(item)}else{var i=this.selection.indexOf(item);if(i>=0){this.selection.splice(i,1)}}if(this.selectCallback){this.selectCallback(item,isSelected)}}}},select:function(item){if(this.multi){this.toggle(item)}else if(this.get()!==item){this.setItemSelected(this.get(),false);this.setItemSelected(item,true)}},toggle:function(item){this.setItemSelected(item,!this.isSelected(item))}};Polymer.IronSelectableBehavior={properties:{attrForSelected:{type:String,value:null},selected:{type:String,notify:true},selectedItem:{type:Object,readOnly:true,notify:true},activateEvent:{type:String,value:"tap",observer:"_activateEventChanged"},selectable:String,selectedClass:{type:String,value:"iron-selected"},selectedAttribute:{type:String,value:null},fallbackSelection:{type:String,value:null},items:{type:Array,readOnly:true,notify:true,value:function(){return[]}},_excludedLocalNames:{type:Object,value:function(){return{template:1}}}},observers:["_updateAttrForSelected(attrForSelected)","_updateSelected(selected)","_checkFallback(fallbackSelection)"],created:function(){this._bindFilterItem=this._filterItem.bind(this);this._selection=new Polymer.IronSelection(this._applySelection.bind(this))},attached:function(){this._observer=this._observeItems(this);this._updateItems();if(!this._shouldUpdateSelection){this._updateSelected()}this._addListener(this.activateEvent)},detached:function(){if(this._observer){Polymer.dom(this).unobserveNodes(this._observer)}this._removeListener(this.activateEvent)},indexOf:function(item){return this.items.indexOf(item)},select:function(value){this.selected=value},selectPrevious:function(){var length=this.items.length;var index=(Number(this._valueToIndex(this.selected))-1+length)%length;this.selected=this._indexToValue(index)},selectNext:function(){var index=(Number(this._valueToIndex(this.selected))+1)%this.items.length;this.selected=this._indexToValue(index)},selectIndex:function(index){this.select(this._indexToValue(index))},forceSynchronousItemUpdate:function(){this._updateItems()},get _shouldUpdateSelection(){return this.selected!=null},_checkFallback:function(){if(this._shouldUpdateSelection){this._updateSelected()}},_addListener:function(eventName){this.listen(this,eventName,"_activateHandler")},_removeListener:function(eventName){this.unlisten(this,eventName,"_activateHandler")},_activateEventChanged:function(eventName,old){this._removeListener(old);this._addListener(eventName)},_updateItems:function(){var nodes=Polymer.dom(this).queryDistributedElements(this.selectable||"*");nodes=Array.prototype.filter.call(nodes,this._bindFilterItem);this._setItems(nodes)},_updateAttrForSelected:function(){if(this._shouldUpdateSelection){this.selected=this._indexToValue(this.indexOf(this.selectedItem))}},_updateSelected:function(){this._selectSelected(this.selected)},_selectSelected:function(selected){this._selection.select(this._valueToItem(this.selected));if(this.fallbackSelection&&this.items.length&&this._selection.get()===undefined){this.selected=this.fallbackSelection}},_filterItem:function(node){return!this._excludedLocalNames[node.localName]},_valueToItem:function(value){return value==null?null:this.items[this._valueToIndex(value)]},_valueToIndex:function(value){if(this.attrForSelected){for(var i=0,item;item=this.items[i];i++){if(this._valueForItem(item)==value){return i}}}else{return Number(value)}},_indexToValue:function(index){if(this.attrForSelected){var item=this.items[index];if(item){return this._valueForItem(item)}}else{return index}},_valueForItem:function(item){var propValue=item[Polymer.CaseMap.dashToCamelCase(this.attrForSelected)];return propValue!=undefined?propValue:item.getAttribute(this.attrForSelected)},_applySelection:function(item,isSelected){if(this.selectedClass){this.toggleClass(this.selectedClass,isSelected,item)}if(this.selectedAttribute){this.toggleAttribute(this.selectedAttribute,isSelected,item)}this._selectionChange();this.fire("iron-"+(isSelected?"select":"deselect"),{item:item})},_selectionChange:function(){this._setSelectedItem(this._selection.get())},_observeItems:function(node){return Polymer.dom(node).observeNodes(function(mutation){this._updateItems();if(this._shouldUpdateSelection){this._updateSelected()}this.fire("iron-items-changed",mutation,{bubbles:false,cancelable:false})})},_activateHandler:function(e){var t=e.target;var items=this.items;while(t&&t!=this){var i=items.indexOf(t);if(i>=0){var value=this._indexToValue(i);this._itemActivate(value,t);return}t=t.parentNode}},_itemActivate:function(value,item){if(!this.fire("iron-activate",{selected:value,item:item},{cancelable:true}).defaultPrevented){this.select(value)}}};Polymer.IronMultiSelectableBehaviorImpl={properties:{multi:{type:Boolean,value:false,observer:"multiChanged"},selectedValues:{type:Array,notify:true},selectedItems:{type:Array,readOnly:true,notify:true}},observers:["_updateSelected(selectedValues.splices)"],select:function(value){if(this.multi){if(this.selectedValues){this._toggleSelected(value)}else{this.selectedValues=[value]}}else{this.selected=value}},multiChanged:function(multi){this._selection.multi=multi},get _shouldUpdateSelection(){return this.selected!=null||this.selectedValues!=null&&this.selectedValues.length},_updateAttrForSelected:function(){if(!this.multi){Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this)}else if(this._shouldUpdateSelection){this.selectedValues=this.selectedItems.map(function(selectedItem){return this._indexToValue(this.indexOf(selectedItem))},this).filter(function(unfilteredValue){return unfilteredValue!=null},this)}},_updateSelected:function(){if(this.multi){this._selectMulti(this.selectedValues)}else{this._selectSelected(this.selected)}},_selectMulti:function(values){if(values){var selectedItems=this._valuesToItems(values);this._selection.clear(selectedItems);for(var i=0;i<selectedItems.length;i++){this._selection.setItemSelected(selectedItems[i],true)}if(this.fallbackSelection&&this.items.length&&!this._selection.get().length){var fallback=this._valueToItem(this.fallbackSelection);if(fallback){this.selectedValues=[this.fallbackSelection]}}}else{this._selection.clear()}},_selectionChange:function(){var s=this._selection.get();if(this.multi){this._setSelectedItems(s)}else{this._setSelectedItems([s]);this._setSelectedItem(s)}},_toggleSelected:function(value){var i=this.selectedValues.indexOf(value);var unselected=i<0;if(unselected){this.push("selectedValues",value)}else{this.splice("selectedValues",i,1)}},_valuesToItems:function(values){return values==null?null:values.map(function(value){return this._valueToItem(value)},this)}};Polymer.IronMultiSelectableBehavior=[Polymer.IronSelectableBehavior,Polymer.IronMultiSelectableBehaviorImpl];Polymer.IronMenuBehaviorImpl={properties:{focusedItem:{observer:"_focusedItemChanged",readOnly:true,type:Object},attrForItemTitle:{type:String}},hostAttributes:{role:"menu",tabindex:"0"},observers:["_updateMultiselectable(multi)"],listeners:{focus:"_onFocus",keydown:"_onKeydown","iron-items-changed":"_onIronItemsChanged"},keyBindings:{up:"_onUpKey",down:"_onDownKey",esc:"_onEscKey","shift+tab:keydown":"_onShiftTabDown"},attached:function(){this._resetTabindices()},select:function(value){if(this._defaultFocusAsync){this.cancelAsync(this._defaultFocusAsync);this._defaultFocusAsync=null}var item=this._valueToItem(value);if(item&&item.hasAttribute("disabled"))return;this._setFocusedItem(item);Polymer.IronMultiSelectableBehaviorImpl.select.apply(this,arguments)},_resetTabindices:function(){var selectedItem=this.multi?this.selectedItems&&this.selectedItems[0]:this.selectedItem;this.items.forEach(function(item){item.setAttribute("tabindex",item===selectedItem?"0":"-1")},this)},_updateMultiselectable:function(multi){if(multi){this.setAttribute("aria-multiselectable","true")}else{this.removeAttribute("aria-multiselectable")}},_focusWithKeyboardEvent:function(event){for(var i=0,item;item=this.items[i];i++){var attr=this.attrForItemTitle||"textContent";var title=item[attr]||item.getAttribute(attr);if(!item.hasAttribute("disabled")&&title&&title.trim().charAt(0).toLowerCase()===String.fromCharCode(event.keyCode).toLowerCase()){this._setFocusedItem(item);break}}},_focusPrevious:function(){var length=this.items.length;var curFocusIndex=Number(this.indexOf(this.focusedItem));for(var i=1;i<length+1;i++){var item=this.items[(curFocusIndex-i+length)%length];if(!item.hasAttribute("disabled")){var owner=Polymer.dom(item).getOwnerRoot()||document;this._setFocusedItem(item);if(Polymer.dom(owner).activeElement==item){return}}}},_focusNext:function(){var length=this.items.length;var curFocusIndex=Number(this.indexOf(this.focusedItem));for(var i=1;i<length+1;i++){var item=this.items[(curFocusIndex+i)%length];if(!item.hasAttribute("disabled")){var owner=Polymer.dom(item).getOwnerRoot()||document;this._setFocusedItem(item);if(Polymer.dom(owner).activeElement==item){return}}}},_applySelection:function(item,isSelected){if(isSelected){item.setAttribute("aria-selected","true")}else{item.removeAttribute("aria-selected")}Polymer.IronSelectableBehavior._applySelection.apply(this,arguments)},_focusedItemChanged:function(focusedItem,old){old&&old.setAttribute("tabindex","-1");if(focusedItem){focusedItem.setAttribute("tabindex","0");focusedItem.focus()}},_onIronItemsChanged:function(event){if(event.detail.addedNodes.length){this._resetTabindices()}},_onShiftTabDown:function(event){var oldTabIndex=this.getAttribute("tabindex");Polymer.IronMenuBehaviorImpl._shiftTabPressed=true;this._setFocusedItem(null);this.setAttribute("tabindex","-1");this.async(function(){this.setAttribute("tabindex",oldTabIndex);Polymer.IronMenuBehaviorImpl._shiftTabPressed=false},1)},_onFocus:function(event){if(Polymer.IronMenuBehaviorImpl._shiftTabPressed){return}var rootTarget=Polymer.dom(event).rootTarget;if(rootTarget!==this&&typeof rootTarget.tabIndex!=="undefined"&&!this.isLightDescendant(rootTarget)){return}this._defaultFocusAsync=this.async(function(){var selectedItem=this.multi?this.selectedItems&&this.selectedItems[0]:this.selectedItem;this._setFocusedItem(null);if(selectedItem){this._setFocusedItem(selectedItem)}else if(this.items[0]){this._focusNext()}})},_onUpKey:function(event){this._focusPrevious();event.detail.keyboardEvent.preventDefault()},_onDownKey:function(event){this._focusNext();event.detail.keyboardEvent.preventDefault()},_onEscKey:function(event){this.focusedItem.blur()},_onKeydown:function(event){if(!this.keyboardEventMatchesKeys(event,"up down esc")){this._focusWithKeyboardEvent(event)}event.stopPropagation()},_activateHandler:function(event){Polymer.IronSelectableBehavior._activateHandler.call(this,event);event.stopPropagation()}};Polymer.IronMenuBehaviorImpl._shiftTabPressed=false;Polymer.IronMenuBehavior=[Polymer.IronMultiSelectableBehavior,Polymer.IronA11yKeysBehavior,Polymer.IronMenuBehaviorImpl];(function(){Polymer({is:"paper-menu",behaviors:[Polymer.IronMenuBehavior]})})();Polymer.IronFitBehavior={properties:{sizingTarget:{type:Object,value:function(){return this}},fitInto:{type:Object,value:window},noOverlap:{type:Boolean},positionTarget:{type:Element},horizontalAlign:{type:String},verticalAlign:{type:String},dynamicAlign:{type:Boolean},horizontalOffset:{type:Number,value:0,notify:true},verticalOffset:{type:Number,value:0,notify:true},autoFitOnAttach:{type:Boolean,value:false},_fitInfo:{type:Object}},get _fitWidth(){var fitWidth;if(this.fitInto===window){fitWidth=this.fitInto.innerWidth}else{fitWidth=this.fitInto.getBoundingClientRect().width}return fitWidth},get _fitHeight(){var fitHeight;if(this.fitInto===window){fitHeight=this.fitInto.innerHeight}else{fitHeight=this.fitInto.getBoundingClientRect().height}return fitHeight},get _fitLeft(){var fitLeft;if(this.fitInto===window){fitLeft=0}else{fitLeft=this.fitInto.getBoundingClientRect().left}return fitLeft},get _fitTop(){var fitTop;if(this.fitInto===window){fitTop=0}else{fitTop=this.fitInto.getBoundingClientRect().top}return fitTop},get _defaultPositionTarget(){var parent=Polymer.dom(this).parentNode;if(parent&&parent.nodeType===Node.DOCUMENT_FRAGMENT_NODE){parent=parent.host}return parent},get _localeHorizontalAlign(){if(this._isRTL){if(this.horizontalAlign==="right"){return"left"}if(this.horizontalAlign==="left"){return"right"}}return this.horizontalAlign},attached:function(){this._isRTL=window.getComputedStyle(this).direction=="rtl";this.positionTarget=this.positionTarget||this._defaultPositionTarget;if(this.autoFitOnAttach){if(window.getComputedStyle(this).display==="none"){setTimeout(function(){this.fit()}.bind(this))}else{this.fit()}}},fit:function(){this.position();this.constrain();this.center()},_discoverInfo:function(){if(this._fitInfo){return}var target=window.getComputedStyle(this);var sizer=window.getComputedStyle(this.sizingTarget);this._fitInfo={inlineStyle:{top:this.style.top||"",left:this.style.left||"",position:this.style.position||""},sizerInlineStyle:{maxWidth:this.sizingTarget.style.maxWidth||"",maxHeight:this.sizingTarget.style.maxHeight||"",boxSizing:this.sizingTarget.style.boxSizing||""},positionedBy:{vertically:target.top!=="auto"?"top":target.bottom!=="auto"?"bottom":null,horizontally:target.left!=="auto"?"left":target.right!=="auto"?"right":null},sizedBy:{height:sizer.maxHeight!=="none",width:sizer.maxWidth!=="none",minWidth:parseInt(sizer.minWidth,10)||0,minHeight:parseInt(sizer.minHeight,10)||0},margin:{top:parseInt(target.marginTop,10)||0,right:parseInt(target.marginRight,10)||0,bottom:parseInt(target.marginBottom,10)||0,left:parseInt(target.marginLeft,10)||0}};if(this.verticalOffset){this._fitInfo.margin.top=this._fitInfo.margin.bottom=this.verticalOffset;this._fitInfo.inlineStyle.marginTop=this.style.marginTop||"";this._fitInfo.inlineStyle.marginBottom=this.style.marginBottom||"";this.style.marginTop=this.style.marginBottom=this.verticalOffset+"px"}if(this.horizontalOffset){this._fitInfo.margin.left=this._fitInfo.margin.right=this.horizontalOffset;this._fitInfo.inlineStyle.marginLeft=this.style.marginLeft||"";this._fitInfo.inlineStyle.marginRight=this.style.marginRight||"";this.style.marginLeft=this.style.marginRight=this.horizontalOffset+"px"}},resetFit:function(){var info=this._fitInfo||{};for(var property in info.sizerInlineStyle){this.sizingTarget.style[property]=info.sizerInlineStyle[property]}for(var property in info.inlineStyle){this.style[property]=info.inlineStyle[property]}this._fitInfo=null},refit:function(){var scrollLeft=this.sizingTarget.scrollLeft;var scrollTop=this.sizingTarget.scrollTop;this.resetFit();this.fit();this.sizingTarget.scrollLeft=scrollLeft;this.sizingTarget.scrollTop=scrollTop},position:function(){if(!this.horizontalAlign&&!this.verticalAlign){return}this._discoverInfo();this.style.position="fixed";this.sizingTarget.style.boxSizing="border-box";this.style.left="0px";this.style.top="0px";var rect=this.getBoundingClientRect();var positionRect=this.__getNormalizedRect(this.positionTarget);var fitRect=this.__getNormalizedRect(this.fitInto);var margin=this._fitInfo.margin;var size={width:rect.width+margin.left+margin.right,height:rect.height+margin.top+margin.bottom};var position=this.__getPosition(this._localeHorizontalAlign,this.verticalAlign,size,positionRect,fitRect);var left=position.left+margin.left;var top=position.top+margin.top;var right=Math.min(fitRect.right-margin.right,left+rect.width);var bottom=Math.min(fitRect.bottom-margin.bottom,top+rect.height);var minWidth=this._fitInfo.sizedBy.minWidth;var minHeight=this._fitInfo.sizedBy.minHeight;if(left<margin.left){left=margin.left;if(right-left<minWidth){left=right-minWidth}}if(top<margin.top){top=margin.top;if(bottom-top<minHeight){top=bottom-minHeight}}this.sizingTarget.style.maxWidth=right-left+"px";this.sizingTarget.style.maxHeight=bottom-top+"px";this.style.left=left-rect.left+"px";this.style.top=top-rect.top+"px"},constrain:function(){if(this.horizontalAlign||this.verticalAlign){return}this._discoverInfo();var info=this._fitInfo;if(!info.positionedBy.vertically){this.style.position="fixed";this.style.top="0px"}if(!info.positionedBy.horizontally){this.style.position="fixed";this.style.left="0px"}this.sizingTarget.style.boxSizing="border-box";var rect=this.getBoundingClientRect();if(!info.sizedBy.height){this.__sizeDimension(rect,info.positionedBy.vertically,"top","bottom","Height")}if(!info.sizedBy.width){this.__sizeDimension(rect,info.positionedBy.horizontally,"left","right","Width")}},_sizeDimension:function(rect,positionedBy,start,end,extent){this.__sizeDimension(rect,positionedBy,start,end,extent)},__sizeDimension:function(rect,positionedBy,start,end,extent){var info=this._fitInfo;var fitRect=this.__getNormalizedRect(this.fitInto);var max=extent==="Width"?fitRect.width:fitRect.height;var flip=positionedBy===end;var offset=flip?max-rect[end]:rect[start];var margin=info.margin[flip?start:end];var offsetExtent="offset"+extent;var sizingOffset=this[offsetExtent]-this.sizingTarget[offsetExtent];this.sizingTarget.style["max"+extent]=max-margin-offset-sizingOffset+"px"},center:function(){if(this.horizontalAlign||this.verticalAlign){return}this._discoverInfo();var positionedBy=this._fitInfo.positionedBy;if(positionedBy.vertically&&positionedBy.horizontally){return}this.style.position="fixed";if(!positionedBy.vertically){this.style.top="0px"}if(!positionedBy.horizontally){this.style.left="0px"}var rect=this.getBoundingClientRect();var fitRect=this.__getNormalizedRect(this.fitInto);if(!positionedBy.vertically){var top=fitRect.top-rect.top+(fitRect.height-rect.height)/2;this.style.top=top+"px"}if(!positionedBy.horizontally){var left=fitRect.left-rect.left+(fitRect.width-rect.width)/2;this.style.left=left+"px"}},__getNormalizedRect:function(target){if(target===document.documentElement||target===window){return{top:0,left:0,width:window.innerWidth,height:window.innerHeight,right:window.innerWidth,bottom:window.innerHeight}}return target.getBoundingClientRect()},__getCroppedArea:function(position,size,fitRect){var verticalCrop=Math.min(0,position.top)+Math.min(0,fitRect.bottom-(position.top+size.height));var horizontalCrop=Math.min(0,position.left)+Math.min(0,fitRect.right-(position.left+size.width));return Math.abs(verticalCrop)*size.width+Math.abs(horizontalCrop)*size.height},__getPosition:function(hAlign,vAlign,size,positionRect,fitRect){var positions=[{verticalAlign:"top",horizontalAlign:"left",top:positionRect.top,left:positionRect.left},{verticalAlign:"top",horizontalAlign:"right",top:positionRect.top,left:positionRect.right-size.width},{verticalAlign:"bottom",horizontalAlign:"left",top:positionRect.bottom-size.height,left:positionRect.left},{verticalAlign:"bottom",horizontalAlign:"right",top:positionRect.bottom-size.height,left:positionRect.right-size.width}];if(this.noOverlap){for(var i=0,l=positions.length;i<l;i++){var copy={};for(var key in positions[i]){copy[key]=positions[i][key]}positions.push(copy)}positions[0].top=positions[1].top+=positionRect.height;positions[2].top=positions[3].top-=positionRect.height;positions[4].left=positions[6].left+=positionRect.width;positions[5].left=positions[7].left-=positionRect.width}vAlign=vAlign==="auto"?null:vAlign;hAlign=hAlign==="auto"?null:hAlign;var position;for(var i=0;i<positions.length;i++){var pos=positions[i];if(!this.dynamicAlign&&!this.noOverlap&&pos.verticalAlign===vAlign&&pos.horizontalAlign===hAlign){position=pos;break}var alignOk=(!vAlign||pos.verticalAlign===vAlign)&&(!hAlign||pos.horizontalAlign===hAlign);if(!this.dynamicAlign&&!alignOk){continue}position=position||pos;pos.croppedArea=this.__getCroppedArea(pos,size,fitRect);var diff=pos.croppedArea-position.croppedArea;if(diff<0||diff===0&&alignOk){position=pos}if(position.croppedArea===0&&alignOk){break}}return position}};(function(){"use strict";Polymer({is:"iron-overlay-backdrop",properties:{opened:{reflectToAttribute:true,type:Boolean,value:false,observer:"_openedChanged"}},listeners:{transitionend:"_onTransitionend"},created:function(){this.__openedRaf=null},attached:function(){this.opened&&this._openedChanged(this.opened)},prepare:function(){if(this.opened&&!this.parentNode){Polymer.dom(document.body).appendChild(this)}},open:function(){this.opened=true},close:function(){this.opened=false},complete:function(){if(!this.opened&&this.parentNode===document.body){Polymer.dom(this.parentNode).removeChild(this)}},_onTransitionend:function(event){if(event&&event.target===this){this.complete()}},_openedChanged:function(opened){if(opened){this.prepare()}else{var cs=window.getComputedStyle(this);if(cs.transitionDuration==="0s"||cs.opacity==0){this.complete()}}if(!this.isAttached){return}if(this.__openedRaf){window.cancelAnimationFrame(this.__openedRaf);this.__openedRaf=null}this.scrollTop=this.scrollTop;this.__openedRaf=window.requestAnimationFrame(function(){this.__openedRaf=null;this.toggleClass("opened",this.opened)}.bind(this))}})})();Polymer.IronOverlayManagerClass=function(){this._overlays=[];this._minimumZ=101;this._backdropElement=null;Polymer.Gestures.add(document,"tap",this._onCaptureClick.bind(this));document.addEventListener("focus",this._onCaptureFocus.bind(this),true);document.addEventListener("keydown",this._onCaptureKeyDown.bind(this),true)};Polymer.IronOverlayManagerClass.prototype={constructor:Polymer.IronOverlayManagerClass,get backdropElement(){if(!this._backdropElement){this._backdropElement=document.createElement("iron-overlay-backdrop")}return this._backdropElement},get deepActiveElement(){var active=document.activeElement||document.body;while(active.root&&Polymer.dom(active.root).activeElement){active=Polymer.dom(active.root).activeElement}return active},_bringOverlayAtIndexToFront:function(i){var overlay=this._overlays[i];if(!overlay){return}var lastI=this._overlays.length-1;var currentOverlay=this._overlays[lastI];if(currentOverlay&&this._shouldBeBehindOverlay(overlay,currentOverlay)){lastI--}if(i>=lastI){return}var minimumZ=Math.max(this.currentOverlayZ(),this._minimumZ);if(this._getZ(overlay)<=minimumZ){this._applyOverlayZ(overlay,minimumZ)}while(i<lastI){this._overlays[i]=this._overlays[i+1];i++}this._overlays[lastI]=overlay},addOrRemoveOverlay:function(overlay){if(overlay.opened){this.addOverlay(overlay)}else{this.removeOverlay(overlay)}},addOverlay:function(overlay){var i=this._overlays.indexOf(overlay);if(i>=0){this._bringOverlayAtIndexToFront(i);this.trackBackdrop();return}var insertionIndex=this._overlays.length;var currentOverlay=this._overlays[insertionIndex-1];var minimumZ=Math.max(this._getZ(currentOverlay),this._minimumZ);var newZ=this._getZ(overlay);if(currentOverlay&&this._shouldBeBehindOverlay(overlay,currentOverlay)){this._applyOverlayZ(currentOverlay,minimumZ);insertionIndex--;var previousOverlay=this._overlays[insertionIndex-1];minimumZ=Math.max(this._getZ(previousOverlay),this._minimumZ)}if(newZ<=minimumZ){this._applyOverlayZ(overlay,minimumZ)}this._overlays.splice(insertionIndex,0,overlay);this.trackBackdrop()},removeOverlay:function(overlay){var i=this._overlays.indexOf(overlay);if(i===-1){return}this._overlays.splice(i,1);this.trackBackdrop()},currentOverlay:function(){var i=this._overlays.length-1;return this._overlays[i]},currentOverlayZ:function(){return this._getZ(this.currentOverlay())},ensureMinimumZ:function(minimumZ){this._minimumZ=Math.max(this._minimumZ,minimumZ)},focusOverlay:function(){var current=this.currentOverlay();if(current){current._applyFocus()}},trackBackdrop:function(){var overlay=this._overlayWithBackdrop();if(!overlay&&!this._backdropElement){return}this.backdropElement.style.zIndex=this._getZ(overlay)-1;this.backdropElement.opened=!!overlay},getBackdrops:function(){var backdrops=[];for(var i=0;i<this._overlays.length;i++){if(this._overlays[i].withBackdrop){backdrops.push(this._overlays[i])}}return backdrops},backdropZ:function(){return this._getZ(this._overlayWithBackdrop())-1},_overlayWithBackdrop:function(){for(var i=0;i<this._overlays.length;i++){if(this._overlays[i].withBackdrop){return this._overlays[i]}}},_getZ:function(overlay){var z=this._minimumZ;if(overlay){var z1=Number(overlay.style.zIndex||window.getComputedStyle(overlay).zIndex);if(z1===z1){z=z1}}return z},_setZ:function(element,z){element.style.zIndex=z},_applyOverlayZ:function(overlay,aboveZ){this._setZ(overlay,aboveZ+2)},_overlayInPath:function(path){path=path||[];for(var i=0;i<path.length;i++){if(path[i]._manager===this){return path[i]}}},_onCaptureClick:function(event){var overlay=this.currentOverlay();if(overlay&&this._overlayInPath(Polymer.dom(event).path)!==overlay){overlay._onCaptureClick(event)}},_onCaptureFocus:function(event){var overlay=this.currentOverlay();if(overlay){overlay._onCaptureFocus(event)}},_onCaptureKeyDown:function(event){var overlay=this.currentOverlay();if(overlay){if(Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event,"esc")){overlay._onCaptureEsc(event)}else if(Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event,"tab")){overlay._onCaptureTab(event)}}},_shouldBeBehindOverlay:function(overlay1,overlay2){return!overlay1.alwaysOnTop&&overlay2.alwaysOnTop}};Polymer.IronOverlayManager=new Polymer.IronOverlayManagerClass;(function(){"use strict";Polymer.IronOverlayBehaviorImpl={properties:{opened:{observer:"_openedChanged",type:Boolean,value:false,notify:true},canceled:{observer:"_canceledChanged",readOnly:true, +type:Boolean,value:false},withBackdrop:{observer:"_withBackdropChanged",type:Boolean},noAutoFocus:{type:Boolean,value:false},noCancelOnEscKey:{type:Boolean,value:false},noCancelOnOutsideClick:{type:Boolean,value:false},closingReason:{type:Object},restoreFocusOnClose:{type:Boolean,value:false},alwaysOnTop:{type:Boolean},_manager:{type:Object,value:Polymer.IronOverlayManager},_focusedChild:{type:Object}},listeners:{"iron-resize":"_onIronResize"},get backdropElement(){return this._manager.backdropElement},get _focusNode(){return this._focusedChild||Polymer.dom(this).querySelector("[autofocus]")||this},get _focusableNodes(){var FOCUSABLE_WITH_DISABLED=["a[href]","area[href]","iframe","[tabindex]","[contentEditable=true]"];var FOCUSABLE_WITHOUT_DISABLED=["input","select","textarea","button"];var selector=FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),')+':not([tabindex="-1"]),'+FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),')+':not([disabled]):not([tabindex="-1"])';var focusables=Polymer.dom(this).querySelectorAll(selector);if(this.tabIndex>=0){focusables.splice(0,0,this)}return focusables.sort(function(a,b){if(a.tabIndex===b.tabIndex){return 0}if(a.tabIndex===0||a.tabIndex>b.tabIndex){return 1}return-1})},ready:function(){this.__isAnimating=false;this.__shouldRemoveTabIndex=false;this.__firstFocusableNode=this.__lastFocusableNode=null;this.__raf=null;this.__restoreFocusNode=null;this._ensureSetup()},attached:function(){if(this.opened){this._openedChanged(this.opened)}this._observer=Polymer.dom(this).observeNodes(this._onNodesChange)},detached:function(){Polymer.dom(this).unobserveNodes(this._observer);this._observer=null;if(this.__raf){window.cancelAnimationFrame(this.__raf);this.__raf=null}this._manager.removeOverlay(this)},toggle:function(){this._setCanceled(false);this.opened=!this.opened},open:function(){this._setCanceled(false);this.opened=true},close:function(){this._setCanceled(false);this.opened=false},cancel:function(event){var cancelEvent=this.fire("iron-overlay-canceled",event,{cancelable:true});if(cancelEvent.defaultPrevented){return}this._setCanceled(true);this.opened=false},_ensureSetup:function(){if(this._overlaySetup){return}this._overlaySetup=true;this.style.outline="none";this.style.display="none"},_openedChanged:function(opened){if(opened){this.removeAttribute("aria-hidden")}else{this.setAttribute("aria-hidden","true")}if(!this.isAttached){return}this.__isAnimating=true;this.__onNextAnimationFrame(this.__openedChanged)},_canceledChanged:function(){this.closingReason=this.closingReason||{};this.closingReason.canceled=this.canceled},_withBackdropChanged:function(){if(this.withBackdrop&&!this.hasAttribute("tabindex")){this.setAttribute("tabindex","-1");this.__shouldRemoveTabIndex=true}else if(this.__shouldRemoveTabIndex){this.removeAttribute("tabindex");this.__shouldRemoveTabIndex=false}if(this.opened&&this.isAttached){this._manager.trackBackdrop()}},_prepareRenderOpened:function(){this.__restoreFocusNode=this._manager.deepActiveElement;this._preparePositioning();this.refit();this._finishPositioning();if(this.noAutoFocus&&document.activeElement===this._focusNode){this._focusNode.blur();this.__restoreFocusNode.focus()}},_renderOpened:function(){this._finishRenderOpened()},_renderClosed:function(){this._finishRenderClosed()},_finishRenderOpened:function(){this.notifyResize();this.__isAnimating=false;var focusableNodes=this._focusableNodes;this.__firstFocusableNode=focusableNodes[0];this.__lastFocusableNode=focusableNodes[focusableNodes.length-1];this.fire("iron-overlay-opened")},_finishRenderClosed:function(){this.style.display="none";this.style.zIndex="";this.notifyResize();this.__isAnimating=false;this.fire("iron-overlay-closed",this.closingReason)},_preparePositioning:function(){this.style.transition=this.style.webkitTransition="none";this.style.transform=this.style.webkitTransform="none";this.style.display=""},_finishPositioning:function(){this.style.display="none";this.scrollTop=this.scrollTop;this.style.transition=this.style.webkitTransition="";this.style.transform=this.style.webkitTransform="";this.style.display="";this.scrollTop=this.scrollTop},_applyFocus:function(){if(this.opened){if(!this.noAutoFocus){this._focusNode.focus()}}else{this._focusNode.blur();this._focusedChild=null;if(this.restoreFocusOnClose&&this.__restoreFocusNode){this.__restoreFocusNode.focus()}this.__restoreFocusNode=null;var currentOverlay=this._manager.currentOverlay();if(currentOverlay&&this!==currentOverlay){currentOverlay._applyFocus()}}},_onCaptureClick:function(event){if(!this.noCancelOnOutsideClick){this.cancel(event)}},_onCaptureFocus:function(event){if(!this.withBackdrop){return}var path=Polymer.dom(event).path;if(path.indexOf(this)===-1){event.stopPropagation();this._applyFocus()}else{this._focusedChild=path[0]}},_onCaptureEsc:function(event){if(!this.noCancelOnEscKey){this.cancel(event)}},_onCaptureTab:function(event){if(!this.withBackdrop){return}var shift=event.shiftKey;var nodeToCheck=shift?this.__firstFocusableNode:this.__lastFocusableNode;var nodeToSet=shift?this.__lastFocusableNode:this.__firstFocusableNode;var shouldWrap=false;if(nodeToCheck===nodeToSet){shouldWrap=true}else{var focusedNode=this._manager.deepActiveElement;shouldWrap=focusedNode===nodeToCheck||focusedNode===this}if(shouldWrap){event.preventDefault();this._focusedChild=nodeToSet;this._applyFocus()}},_onIronResize:function(){if(this.opened&&!this.__isAnimating){this.__onNextAnimationFrame(this.refit)}},_onNodesChange:function(){if(this.opened&&!this.__isAnimating){this.notifyResize()}},__openedChanged:function(){if(this.opened){this._prepareRenderOpened();this._manager.addOverlay(this);this._applyFocus();this._renderOpened()}else{this._manager.removeOverlay(this);this._applyFocus();this._renderClosed()}},__onNextAnimationFrame:function(callback){if(this.__raf){window.cancelAnimationFrame(this.__raf)}var self=this;this.__raf=window.requestAnimationFrame(function nextAnimationFrame(){self.__raf=null;callback.call(self)})}};Polymer.IronOverlayBehavior=[Polymer.IronFitBehavior,Polymer.IronResizableBehavior,Polymer.IronOverlayBehaviorImpl]})();Polymer.NeonAnimatableBehavior={properties:{animationConfig:{type:Object},entryAnimation:{observer:"_entryAnimationChanged",type:String},exitAnimation:{observer:"_exitAnimationChanged",type:String}},_entryAnimationChanged:function(){this.animationConfig=this.animationConfig||{};this.animationConfig["entry"]=[{name:this.entryAnimation,node:this}]},_exitAnimationChanged:function(){this.animationConfig=this.animationConfig||{};this.animationConfig["exit"]=[{name:this.exitAnimation,node:this}]},_copyProperties:function(config1,config2){for(var property in config2){config1[property]=config2[property]}},_cloneConfig:function(config){var clone={isClone:true};this._copyProperties(clone,config);return clone},_getAnimationConfigRecursive:function(type,map,allConfigs){if(!this.animationConfig){return}if(this.animationConfig.value&&typeof this.animationConfig.value==="function"){this._warn(this._logf("playAnimation","Please put 'animationConfig' inside of your components 'properties' object instead of outside of it."));return}var thisConfig;if(type){thisConfig=this.animationConfig[type]}else{thisConfig=this.animationConfig}if(!Array.isArray(thisConfig)){thisConfig=[thisConfig]}if(thisConfig){for(var config,index=0;config=thisConfig[index];index++){if(config.animatable){config.animatable._getAnimationConfigRecursive(config.type||type,map,allConfigs)}else{if(config.id){var cachedConfig=map[config.id];if(cachedConfig){if(!cachedConfig.isClone){map[config.id]=this._cloneConfig(cachedConfig);cachedConfig=map[config.id]}this._copyProperties(cachedConfig,config)}else{map[config.id]=config}}else{allConfigs.push(config)}}}}},getAnimationConfig:function(type){var map={};var allConfigs=[];this._getAnimationConfigRecursive(type,map,allConfigs);for(var key in map){allConfigs.push(map[key])}return allConfigs}};Polymer.NeonAnimationRunnerBehaviorImpl={_configureAnimations:function(configs){var results=[];if(configs.length>0){for(var config,index=0;config=configs[index];index++){var neonAnimation=document.createElement(config.name);if(neonAnimation.isNeonAnimation){var result=null;try{result=neonAnimation.configure(config);if(typeof result.cancel!="function"){result=document.timeline.play(result)}}catch(e){result=null;console.warn("Couldnt play","(",config.name,").",e)}if(result){results.push({neonAnimation:neonAnimation,config:config,animation:result})}}else{console.warn(this.is+":",config.name,"not found!")}}}return results},_shouldComplete:function(activeEntries){var finished=true;for(var i=0;i<activeEntries.length;i++){if(activeEntries[i].animation.playState!="finished"){finished=false;break}}return finished},_complete:function(activeEntries){for(var i=0;i<activeEntries.length;i++){activeEntries[i].neonAnimation.complete(activeEntries[i].config)}for(var i=0;i<activeEntries.length;i++){activeEntries[i].animation.cancel()}},playAnimation:function(type,cookie){var configs=this.getAnimationConfig(type);if(!configs){return}this._active=this._active||{};if(this._active[type]){this._complete(this._active[type]);delete this._active[type]}var activeEntries=this._configureAnimations(configs);if(activeEntries.length==0){this.fire("neon-animation-finish",cookie,{bubbles:false});return}this._active[type]=activeEntries;for(var i=0;i<activeEntries.length;i++){activeEntries[i].animation.onfinish=function(){if(this._shouldComplete(activeEntries)){this._complete(activeEntries);delete this._active[type];this.fire("neon-animation-finish",cookie,{bubbles:false})}}.bind(this)}},cancelAnimation:function(){for(var k in this._animations){this._animations[k].cancel()}this._animations={}}};Polymer.NeonAnimationRunnerBehavior=[Polymer.NeonAnimatableBehavior,Polymer.NeonAnimationRunnerBehaviorImpl];Polymer.NeonAnimationBehavior={properties:{animationTiming:{type:Object,value:function(){return{duration:500,easing:"cubic-bezier(0.4, 0, 0.2, 1)",fill:"both"}}}},isNeonAnimation:true,timingFromConfig:function(config){if(config.timing){for(var property in config.timing){this.animationTiming[property]=config.timing[property]}}return this.animationTiming},setPrefixedProperty:function(node,property,value){var map={transform:["webkitTransform"],transformOrigin:["mozTransformOrigin","webkitTransformOrigin"]};var prefixes=map[property];for(var prefix,index=0;prefix=prefixes[index];index++){node.style[prefix]=value}node.style[property]=value},complete:function(){}};Polymer({is:"opaque-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;this._effect=new KeyframeEffect(node,[{opacity:"1"},{opacity:"1"}],this.timingFromConfig(config));node.style.opacity="0";return this._effect},complete:function(config){config.node.style.opacity=""}});(function(){"use strict";var LAST_TOUCH_POSITION={pageX:0,pageY:0};var ROOT_TARGET=null;var SCROLLABLE_NODES=[];Polymer.IronDropdownScrollManager={get currentLockingElement(){return this._lockingElements[this._lockingElements.length-1]},elementIsScrollLocked:function(element){var currentLockingElement=this.currentLockingElement;if(currentLockingElement===undefined)return false;var scrollLocked;if(this._hasCachedLockedElement(element)){return true}if(this._hasCachedUnlockedElement(element)){return false}scrollLocked=!!currentLockingElement&¤tLockingElement!==element&&!this._composedTreeContains(currentLockingElement,element);if(scrollLocked){this._lockedElementCache.push(element)}else{this._unlockedElementCache.push(element)}return scrollLocked},pushScrollLock:function(element){if(this._lockingElements.indexOf(element)>=0){return}if(this._lockingElements.length===0){this._lockScrollInteractions()}this._lockingElements.push(element);this._lockedElementCache=[];this._unlockedElementCache=[]},removeScrollLock:function(element){var index=this._lockingElements.indexOf(element);if(index===-1){return}this._lockingElements.splice(index,1);this._lockedElementCache=[];this._unlockedElementCache=[];if(this._lockingElements.length===0){this._unlockScrollInteractions()}},_lockingElements:[],_lockedElementCache:null,_unlockedElementCache:null,_hasCachedLockedElement:function(element){return this._lockedElementCache.indexOf(element)>-1},_hasCachedUnlockedElement:function(element){return this._unlockedElementCache.indexOf(element)>-1},_composedTreeContains:function(element,child){var contentElements;var distributedNodes;var contentIndex;var nodeIndex;if(element.contains(child)){return true}contentElements=Polymer.dom(element).querySelectorAll("content");for(contentIndex=0;contentIndex<contentElements.length;++contentIndex){distributedNodes=Polymer.dom(contentElements[contentIndex]).getDistributedNodes();for(nodeIndex=0;nodeIndex<distributedNodes.length;++nodeIndex){if(this._composedTreeContains(distributedNodes[nodeIndex],child)){return true}}}return false},_scrollInteractionHandler:function(event){if(event.cancelable&&this._shouldPreventScrolling(event)){event.preventDefault()}if(event.targetTouches){var touch=event.targetTouches[0];LAST_TOUCH_POSITION.pageX=touch.pageX;LAST_TOUCH_POSITION.pageY=touch.pageY}},_lockScrollInteractions:function(){this._boundScrollHandler=this._boundScrollHandler||this._scrollInteractionHandler.bind(this);document.addEventListener("wheel",this._boundScrollHandler,true);document.addEventListener("mousewheel",this._boundScrollHandler,true);document.addEventListener("DOMMouseScroll",this._boundScrollHandler,true);document.addEventListener("touchstart",this._boundScrollHandler,true);document.addEventListener("touchmove",this._boundScrollHandler,true)},_unlockScrollInteractions:function(){document.removeEventListener("wheel",this._boundScrollHandler,true);document.removeEventListener("mousewheel",this._boundScrollHandler,true);document.removeEventListener("DOMMouseScroll",this._boundScrollHandler,true);document.removeEventListener("touchstart",this._boundScrollHandler,true);document.removeEventListener("touchmove",this._boundScrollHandler,true)},_shouldPreventScrolling:function(event){var target=Polymer.dom(event).rootTarget;if(event.type!=="touchmove"&&ROOT_TARGET!==target){ROOT_TARGET=target;SCROLLABLE_NODES=this._getScrollableNodes(Polymer.dom(event).path)}if(!SCROLLABLE_NODES.length){return true}if(event.type==="touchstart"){return false}var info=this._getScrollInfo(event);return!this._getScrollingNode(SCROLLABLE_NODES,info.deltaX,info.deltaY)},_getScrollableNodes:function(nodes){var scrollables=[];var lockingIndex=nodes.indexOf(this.currentLockingElement);for(var i=0;i<=lockingIndex;i++){var node=nodes[i];if(node.nodeType===11){continue}var style=node.style;if(style.overflow!=="scroll"&&style.overflow!=="auto"){style=window.getComputedStyle(node)}if(style.overflow==="scroll"||style.overflow==="auto"){scrollables.push(node)}}return scrollables},_getScrollingNode:function(nodes,deltaX,deltaY){if(!deltaX&&!deltaY){return}var verticalScroll=Math.abs(deltaY)>=Math.abs(deltaX);for(var i=0;i<nodes.length;i++){var node=nodes[i];var canScroll=false;if(verticalScroll){canScroll=deltaY<0?node.scrollTop>0:node.scrollTop<node.scrollHeight-node.clientHeight}else{canScroll=deltaX<0?node.scrollLeft>0:node.scrollLeft<node.scrollWidth-node.clientWidth}if(canScroll){return node}}},_getScrollInfo:function(event){var info={deltaX:event.deltaX,deltaY:event.deltaY};if("deltaX"in event){}else if("wheelDeltaX"in event){info.deltaX=-event.wheelDeltaX;info.deltaY=-event.wheelDeltaY}else if("axis"in event){info.deltaX=event.axis===1?event.detail:0;info.deltaY=event.axis===2?event.detail:0}else if(event.targetTouches){var touch=event.targetTouches[0];info.deltaX=LAST_TOUCH_POSITION.pageX-touch.pageX;info.deltaY=LAST_TOUCH_POSITION.pageY-touch.pageY}return info}}})();(function(){"use strict";Polymer({is:"iron-dropdown",behaviors:[Polymer.IronControlState,Polymer.IronA11yKeysBehavior,Polymer.IronOverlayBehavior,Polymer.NeonAnimationRunnerBehavior],properties:{horizontalAlign:{type:String,value:"left",reflectToAttribute:true},verticalAlign:{type:String,value:"top",reflectToAttribute:true},openAnimationConfig:{type:Object},closeAnimationConfig:{type:Object},focusTarget:{type:Object},noAnimations:{type:Boolean,value:false},allowOutsideScroll:{type:Boolean,value:false},_boundOnCaptureScroll:{type:Function,value:function(){return this._onCaptureScroll.bind(this)}}},listeners:{"neon-animation-finish":"_onNeonAnimationFinish"},observers:["_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)"],get containedElement(){return Polymer.dom(this.$.content).getDistributedNodes()[0]},get _focusTarget(){return this.focusTarget||this.containedElement},ready:function(){this._scrollTop=0;this._scrollLeft=0;this._refitOnScrollRAF=null},attached:function(){if(!this.sizingTarget||this.sizingTarget===this){this.sizingTarget=this.containedElement}},detached:function(){this.cancelAnimation();document.removeEventListener("scroll",this._boundOnCaptureScroll);Polymer.IronDropdownScrollManager.removeScrollLock(this)},_openedChanged:function(){if(this.opened&&this.disabled){this.cancel()}else{this.cancelAnimation();this._updateAnimationConfig();this._saveScrollPosition();if(this.opened){document.addEventListener("scroll",this._boundOnCaptureScroll);!this.allowOutsideScroll&&Polymer.IronDropdownScrollManager.pushScrollLock(this)}else{document.removeEventListener("scroll",this._boundOnCaptureScroll);Polymer.IronDropdownScrollManager.removeScrollLock(this)}Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this,arguments)}},_renderOpened:function(){if(!this.noAnimations&&this.animationConfig.open){this.$.contentWrapper.classList.add("animating");this.playAnimation("open")}else{Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this,arguments)}},_renderClosed:function(){if(!this.noAnimations&&this.animationConfig.close){this.$.contentWrapper.classList.add("animating");this.playAnimation("close")}else{Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this,arguments)}},_onNeonAnimationFinish:function(){this.$.contentWrapper.classList.remove("animating");if(this.opened){this._finishRenderOpened()}else{this._finishRenderClosed()}},_onCaptureScroll:function(){if(!this.allowOutsideScroll){this._restoreScrollPosition()}else{this._refitOnScrollRAF&&window.cancelAnimationFrame(this._refitOnScrollRAF);this._refitOnScrollRAF=window.requestAnimationFrame(this.refit.bind(this))}},_saveScrollPosition:function(){if(document.scrollingElement){this._scrollTop=document.scrollingElement.scrollTop;this._scrollLeft=document.scrollingElement.scrollLeft}else{this._scrollTop=Math.max(document.documentElement.scrollTop,document.body.scrollTop);this._scrollLeft=Math.max(document.documentElement.scrollLeft,document.body.scrollLeft)}},_restoreScrollPosition:function(){if(document.scrollingElement){document.scrollingElement.scrollTop=this._scrollTop;document.scrollingElement.scrollLeft=this._scrollLeft}else{document.documentElement.scrollTop=this._scrollTop;document.documentElement.scrollLeft=this._scrollLeft;document.body.scrollTop=this._scrollTop;document.body.scrollLeft=this._scrollLeft}},_updateAnimationConfig:function(){var animations=(this.openAnimationConfig||[]).concat(this.closeAnimationConfig||[]);for(var i=0;i<animations.length;i++){animations[i].node=this.containedElement}this.animationConfig={open:this.openAnimationConfig,close:this.closeAnimationConfig}},_updateOverlayPosition:function(){if(this.isAttached){this.notifyResize()}},_applyFocus:function(){var focusTarget=this.focusTarget||this.containedElement;if(focusTarget&&this.opened&&!this.noAutoFocus){focusTarget.focus()}else{Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this,arguments)}}})})();Polymer({is:"fade-in-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;this._effect=new KeyframeEffect(node,[{opacity:"0"},{opacity:"1"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"fade-out-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;this._effect=new KeyframeEffect(node,[{opacity:"1"},{opacity:"0"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"paper-menu-grow-height-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;var rect=node.getBoundingClientRect();var height=rect.height;this._effect=new KeyframeEffect(node,[{height:height/2+"px"},{height:height+"px"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"paper-menu-grow-width-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;var rect=node.getBoundingClientRect();var width=rect.width;this._effect=new KeyframeEffect(node,[{width:width/2+"px"},{width:width+"px"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"paper-menu-shrink-width-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;var rect=node.getBoundingClientRect();var width=rect.width;this._effect=new KeyframeEffect(node,[{width:width+"px"},{width:width-width/20+"px"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"paper-menu-shrink-height-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;var rect=node.getBoundingClientRect();var height=rect.height;var top=rect.top;this.setPrefixedProperty(node,"transformOrigin","0 0");this._effect=new KeyframeEffect(node,[{height:height+"px",transform:"translateY(0)"},{height:height/2+"px",transform:"translateY(-20px)"}],this.timingFromConfig(config));return this._effect}});(function(){"use strict";var config={ANIMATION_CUBIC_BEZIER:"cubic-bezier(.3,.95,.5,1)",MAX_ANIMATION_TIME_MS:400};var PaperMenuButton=Polymer({is:"paper-menu-button",behaviors:[Polymer.IronA11yKeysBehavior,Polymer.IronControlState],properties:{opened:{type:Boolean,value:false,notify:true,observer:"_openedChanged"},horizontalAlign:{type:String,value:"left",reflectToAttribute:true},verticalAlign:{type:String,value:"top",reflectToAttribute:true},dynamicAlign:{type:Boolean},horizontalOffset:{type:Number,value:0,notify:true},verticalOffset:{type:Number,value:0,notify:true},noOverlap:{type:Boolean},noAnimations:{type:Boolean,value:false},ignoreSelect:{type:Boolean,value:false},closeOnActivate:{type:Boolean,value:false},openAnimationConfig:{type:Object,value:function(){return[{name:"fade-in-animation",timing:{delay:100,duration:200}},{name:"paper-menu-grow-width-animation",timing:{delay:100,duration:150,easing:config.ANIMATION_CUBIC_BEZIER}},{name:"paper-menu-grow-height-animation",timing:{delay:100,duration:275,easing:config.ANIMATION_CUBIC_BEZIER}}]}},closeAnimationConfig:{type:Object,value:function(){return[{name:"fade-out-animation",timing:{duration:150}},{name:"paper-menu-shrink-width-animation",timing:{delay:100,duration:50,easing:config.ANIMATION_CUBIC_BEZIER}},{name:"paper-menu-shrink-height-animation",timing:{duration:200,easing:"ease-in"}}]}},allowOutsideScroll:{type:Boolean,value:false},restoreFocusOnClose:{type:Boolean,value:true},_dropdownContent:{type:Object}},hostAttributes:{role:"group","aria-haspopup":"true"},listeners:{"iron-activate":"_onIronActivate","iron-select":"_onIronSelect"},get contentElement(){return Polymer.dom(this.$.content).getDistributedNodes()[0]},toggle:function(){if(this.opened){this.close()}else{this.open()}},open:function(){if(this.disabled){return}this.$.dropdown.open()},close:function(){this.$.dropdown.close()},_onIronSelect:function(event){if(!this.ignoreSelect){this.close()}},_onIronActivate:function(event){if(this.closeOnActivate){this.close()}},_openedChanged:function(opened,oldOpened){if(opened){this._dropdownContent=this.contentElement;this.fire("paper-dropdown-open")}else if(oldOpened!=null){this.fire("paper-dropdown-close")}},_disabledChanged:function(disabled){Polymer.IronControlState._disabledChanged.apply(this,arguments);if(disabled&&this.opened){this.close()}},__onIronOverlayCanceled:function(event){var uiEvent=event.detail;var target=Polymer.dom(uiEvent).rootTarget;var trigger=this.$.trigger;var path=Polymer.dom(uiEvent).path;if(path.indexOf(trigger)>-1){event.preventDefault()}}});Object.keys(config).forEach(function(key){PaperMenuButton[key]=config[key]});Polymer.PaperMenuButton=PaperMenuButton})();Polymer.PaperInkyFocusBehaviorImpl={observers:["_focusedChanged(receivedFocusFromKeyboard)"],_focusedChanged:function(receivedFocusFromKeyboard){if(receivedFocusFromKeyboard){this.ensureRipple()}if(this.hasRipple()){this._ripple.holdDown=receivedFocusFromKeyboard}},_createRipple:function(){var ripple=Polymer.PaperRippleBehavior._createRipple();ripple.id="ink";ripple.setAttribute("center","");ripple.classList.add("circle");return ripple}};Polymer.PaperInkyFocusBehavior=[Polymer.IronButtonState,Polymer.IronControlState,Polymer.PaperRippleBehavior,Polymer.PaperInkyFocusBehaviorImpl];Polymer({is:"paper-icon-button",hostAttributes:{role:"button",tabindex:"0"},behaviors:[Polymer.PaperInkyFocusBehavior],properties:{src:{type:String},icon:{type:String},alt:{type:String,observer:"_altChanged"}},_altChanged:function(newValue,oldValue){var label=this.getAttribute("aria-label");if(!label||oldValue==label){this.setAttribute("aria-label",newValue)}}});Polymer({is:"iron-media-query",properties:{queryMatches:{type:Boolean,value:false,readOnly:true,notify:true},query:{type:String,observer:"queryChanged"},full:{type:Boolean,value:false},_boundMQHandler:{value:function(){return this.queryHandler.bind(this)}},_mq:{value:null}},attached:function(){this.style.display="none";this.queryChanged()},detached:function(){this._remove()},_add:function(){if(this._mq){this._mq.addListener(this._boundMQHandler)}},_remove:function(){if(this._mq){this._mq.removeListener(this._boundMQHandler)}this._mq=null},queryChanged:function(){this._remove();var query=this.query;if(!query){return}if(!this.full&&query[0]!=="("){query="("+query+")"}this._mq=window.matchMedia(query);this._add();this.queryHandler(this._mq)},queryHandler:function(mq){this._setQueryMatches(mq.matches)}});(function(){"use strict";Polymer.IronA11yAnnouncer=Polymer({is:"iron-a11y-announcer",properties:{mode:{type:String,value:"polite"},_text:{type:String,value:""}},created:function(){if(!Polymer.IronA11yAnnouncer.instance){Polymer.IronA11yAnnouncer.instance=this}document.body.addEventListener("iron-announce",this._onIronAnnounce.bind(this))},announce:function(text){this._text="";this.async(function(){this._text=text},100)},_onIronAnnounce:function(event){if(event.detail&&event.detail.text){this.announce(event.detail.text)}}});Polymer.IronA11yAnnouncer.instance=null;Polymer.IronA11yAnnouncer.requestAvailability=function(){if(!Polymer.IronA11yAnnouncer.instance){Polymer.IronA11yAnnouncer.instance=document.createElement("iron-a11y-announcer")}document.body.appendChild(Polymer.IronA11yAnnouncer.instance)}})();Polymer.IronValidatableBehaviorMeta=null;Polymer.IronValidatableBehavior={properties:{validator:{type:String},invalid:{notify:true,reflectToAttribute:true,type:Boolean,value:false},_validatorMeta:{type:Object},validatorType:{type:String,value:"validator"},_validator:{type:Object,computed:"__computeValidator(validator)"}},observers:["_invalidChanged(invalid)"],registered:function(){Polymer.IronValidatableBehaviorMeta=new Polymer.IronMeta({type:"validator"})},_invalidChanged:function(){if(this.invalid){this.setAttribute("aria-invalid","true")}else{this.removeAttribute("aria-invalid")}},hasValidator:function(){return this._validator!=null},validate:function(value){this.invalid=!this._getValidity(value);return!this.invalid},_getValidity:function(value){if(this.hasValidator()){return this._validator.validate(value)}return true},__computeValidator:function(){return Polymer.IronValidatableBehaviorMeta&&Polymer.IronValidatableBehaviorMeta.byKey(this.validator)}};Polymer({is:"iron-input","extends":"input",behaviors:[Polymer.IronValidatableBehavior],properties:{bindValue:{observer:"_bindValueChanged",type:String},preventInvalidInput:{type:Boolean},allowedPattern:{type:String,observer:"_allowedPatternChanged"},_previousValidInput:{type:String,value:""},_patternAlreadyChecked:{type:Boolean,value:false}},listeners:{input:"_onInput",keypress:"_onKeypress"},registered:function(){if(!this._canDispatchEventOnDisabled()){this._origDispatchEvent=this.dispatchEvent;this.dispatchEvent=this._dispatchEventFirefoxIE}},created:function(){Polymer.IronA11yAnnouncer.requestAvailability()},_canDispatchEventOnDisabled:function(){var input=document.createElement("input");var canDispatch=false;input.disabled=true;input.addEventListener("feature-check-dispatch-event",function(){canDispatch=true});try{input.dispatchEvent(new Event("feature-check-dispatch-event"))}catch(e){}return canDispatch},_dispatchEventFirefoxIE:function(){var disabled=this.disabled;this.disabled=false;this._origDispatchEvent.apply(this,arguments);this.disabled=disabled},get _patternRegExp(){var pattern;if(this.allowedPattern){pattern=new RegExp(this.allowedPattern)}else{switch(this.type){case"number":pattern=/[0-9.,e-]/;break}}return pattern},ready:function(){this.bindValue=this.value},_bindValueChanged:function(){if(this.value!==this.bindValue){this.value=!(this.bindValue||this.bindValue===0||this.bindValue===false)?"":this.bindValue}this.fire("bind-value-changed",{value:this.bindValue})},_allowedPatternChanged:function(){this.preventInvalidInput=this.allowedPattern?true:false},_onInput:function(){if(this.preventInvalidInput&&!this._patternAlreadyChecked){var valid=this._checkPatternValidity();if(!valid){this._announceInvalidCharacter("Invalid string of characters not entered.");this.value=this._previousValidInput}}this.bindValue=this.value;this._previousValidInput=this.value;this._patternAlreadyChecked=false},_isPrintable:function(event){var anyNonPrintable=event.keyCode==8||event.keyCode==9||event.keyCode==13||event.keyCode==27;var mozNonPrintable=event.keyCode==19||event.keyCode==20||event.keyCode==45||event.keyCode==46||event.keyCode==144||event.keyCode==145||event.keyCode>32&&event.keyCode<41||event.keyCode>111&&event.keyCode<124;return!anyNonPrintable&&!(event.charCode==0&&mozNonPrintable)},_onKeypress:function(event){if(!this.preventInvalidInput&&this.type!=="number"){return}var regexp=this._patternRegExp;if(!regexp){return}if(event.metaKey||event.ctrlKey||event.altKey)return;this._patternAlreadyChecked=true;var thisChar=String.fromCharCode(event.charCode);if(this._isPrintable(event)&&!regexp.test(thisChar)){event.preventDefault();this._announceInvalidCharacter("Invalid character "+thisChar+" not entered.")}},_checkPatternValidity:function(){var regexp=this._patternRegExp;if(!regexp){return true}for(var i=0;i<this.value.length;i++){if(!regexp.test(this.value[i])){return false}}return true},validate:function(){var valid=this.checkValidity();if(valid){if(this.required&&this.value===""){valid=false}else if(this.hasValidator()){valid=Polymer.IronValidatableBehavior.validate.call(this,this.value)}}this.invalid=!valid;this.fire("iron-input-validate");return valid},_announceInvalidCharacter:function(message){this.fire("iron-announce",{text:message})}});Polymer({is:"paper-input-container",properties:{noLabelFloat:{type:Boolean,value:false},alwaysFloatLabel:{type:Boolean,value:false},attrForValue:{type:String,value:"bind-value"},autoValidate:{type:Boolean,value:false},invalid:{observer:"_invalidChanged",type:Boolean,value:false},focused:{readOnly:true,type:Boolean,value:false,notify:true},_addons:{type:Array},_inputHasContent:{type:Boolean,value:false},_inputSelector:{type:String,value:"input,textarea,.paper-input-input"},_boundOnFocus:{type:Function,value:function(){return this._onFocus.bind(this)}},_boundOnBlur:{type:Function,value:function(){return this._onBlur.bind(this)}},_boundOnInput:{type:Function,value:function(){return this._onInput.bind(this)}},_boundValueChanged:{ +type:Function,value:function(){return this._onValueChanged.bind(this)}}},listeners:{"addon-attached":"_onAddonAttached","iron-input-validate":"_onIronInputValidate"},get _valueChangedEvent(){return this.attrForValue+"-changed"},get _propertyForValue(){return Polymer.CaseMap.dashToCamelCase(this.attrForValue)},get _inputElement(){return Polymer.dom(this).querySelector(this._inputSelector)},get _inputElementValue(){return this._inputElement[this._propertyForValue]||this._inputElement.value},ready:function(){if(!this._addons){this._addons=[]}this.addEventListener("focus",this._boundOnFocus,true);this.addEventListener("blur",this._boundOnBlur,true)},attached:function(){if(this.attrForValue){this._inputElement.addEventListener(this._valueChangedEvent,this._boundValueChanged)}else{this.addEventListener("input",this._onInput)}if(this._inputElementValue!=""){this._handleValueAndAutoValidate(this._inputElement)}else{this._handleValue(this._inputElement)}},_onAddonAttached:function(event){if(!this._addons){this._addons=[]}var target=event.target;if(this._addons.indexOf(target)===-1){this._addons.push(target);if(this.isAttached){this._handleValue(this._inputElement)}}},_onFocus:function(){this._setFocused(true)},_onBlur:function(){this._setFocused(false);this._handleValueAndAutoValidate(this._inputElement)},_onInput:function(event){this._handleValueAndAutoValidate(event.target)},_onValueChanged:function(event){this._handleValueAndAutoValidate(event.target)},_handleValue:function(inputElement){var value=this._inputElementValue;if(value||value===0||inputElement.type==="number"&&!inputElement.checkValidity()){this._inputHasContent=true}else{this._inputHasContent=false}this.updateAddons({inputElement:inputElement,value:value,invalid:this.invalid})},_handleValueAndAutoValidate:function(inputElement){if(this.autoValidate){var valid;if(inputElement.validate){valid=inputElement.validate(this._inputElementValue)}else{valid=inputElement.checkValidity()}this.invalid=!valid}this._handleValue(inputElement)},_onIronInputValidate:function(event){this.invalid=this._inputElement.invalid},_invalidChanged:function(){if(this._addons){this.updateAddons({invalid:this.invalid})}},updateAddons:function(state){for(var addon,index=0;addon=this._addons[index];index++){addon.update(state)}},_computeInputContentClass:function(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent){var cls="input-content";if(!noLabelFloat){var label=this.querySelector("label");if(alwaysFloatLabel||_inputHasContent){cls+=" label-is-floating";this.$.labelAndInputContainer.style.position="static";if(invalid){cls+=" is-invalid"}else if(focused){cls+=" label-is-highlighted"}}else{if(label){this.$.labelAndInputContainer.style.position="relative"}}}else{if(_inputHasContent){cls+=" label-is-hidden"}}return cls},_computeUnderlineClass:function(focused,invalid){var cls="underline";if(invalid){cls+=" is-invalid"}else if(focused){cls+=" is-highlighted"}return cls},_computeAddOnContentClass:function(focused,invalid){var cls="add-on-content";if(invalid){cls+=" is-invalid"}else if(focused){cls+=" is-highlighted"}return cls}});Polymer.PaperSpinnerBehavior={listeners:{animationend:"__reset",webkitAnimationEnd:"__reset"},properties:{active:{type:Boolean,value:false,reflectToAttribute:true,observer:"__activeChanged"},alt:{type:String,value:"loading",observer:"__altChanged"},__coolingDown:{type:Boolean,value:false}},__computeContainerClasses:function(active,coolingDown){return[active||coolingDown?"active":"",coolingDown?"cooldown":""].join(" ")},__activeChanged:function(active,old){this.__setAriaHidden(!active);this.__coolingDown=!active&&old},__altChanged:function(alt){if(alt===this.getPropertyInfo("alt").value){this.alt=this.getAttribute("aria-label")||alt}else{this.__setAriaHidden(alt==="");this.setAttribute("aria-label",alt)}},__setAriaHidden:function(hidden){var attr="aria-hidden";if(hidden){this.setAttribute(attr,"true")}else{this.removeAttribute(attr)}},__reset:function(){this.active=false;this.__coolingDown=false}};Polymer({is:"paper-spinner-lite",behaviors:[Polymer.PaperSpinnerBehavior]}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var CrSearchFieldBehavior = { - properties: { - label: { - type: String, - value: '' - }, - clearLabel: { - type: String, - value: '' - }, - showingSearch: { - type: Boolean, - value: false, - notify: true, - observer: 'showingSearchChanged_', - reflectToAttribute: true - }, - lastValue_: { - type: String, - value: '' - } - }, - getSearchInput: function() {}, - getValue: function() { - return this.getSearchInput().value; - }, - setValue: function(value) { - this.getSearchInput().bindValue = value; - this.onValueChanged_(value); - }, - showAndFocus: function() { - this.showingSearch = true; - this.focus_(); - }, - focus_: function() { - this.getSearchInput().focus(); - }, - onSearchTermSearch: function() { - this.onValueChanged_(this.getValue()); - }, - onValueChanged_: function(newValue) { - if (newValue == this.lastValue_) return; - this.fire('search-changed', newValue); - this.lastValue_ = newValue; - }, - onSearchTermKeydown: function(e) { - if (e.key == 'Escape') this.showingSearch = false; - }, - showingSearchChanged_: function() { - if (this.showingSearch) { - this.focus_(); - return; - } - this.setValue(''); - this.getSearchInput().blur(); - } -}; - +var CrSearchFieldBehavior={properties:{label:{type:String,value:""},clearLabel:{type:String,value:""},showingSearch:{type:Boolean,value:false,notify:true,observer:"showingSearchChanged_",reflectToAttribute:true},lastValue_:{type:String,value:""}},getSearchInput:function(){},getValue:function(){return this.getSearchInput().value},setValue:function(value){this.getSearchInput().bindValue=value;this.onValueChanged_(value)},showAndFocus:function(){this.showingSearch=true;this.focus_()},focus_:function(){this.getSearchInput().focus()},onSearchTermSearch:function(){this.onValueChanged_(this.getValue())},onValueChanged_:function(newValue){if(newValue==this.lastValue_)return;this.fire("search-changed",newValue);this.lastValue_=newValue},onSearchTermKeydown:function(e){if(e.key=="Escape")this.showingSearch=false},showingSearchChanged_:function(){if(this.showingSearch){this.focus_();return}this.setValue("");this.getSearchInput().blur()}}; // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'cr-toolbar-search-field', - behaviors: [ CrSearchFieldBehavior ], - properties: { - narrow: { - type: Boolean, - reflectToAttribute: true - }, - label: String, - clearLabel: String, - spinnerActive: { - type: Boolean, - reflectToAttribute: true - }, - hasSearchText_: Boolean, - isSpinnerShown_: { - type: Boolean, - computed: 'computeIsSpinnerShown_(spinnerActive, showingSearch)' - } - }, - listeners: { - tap: 'showSearch_', - 'searchInput.bind-value-changed': 'onBindValueChanged_' - }, - getSearchInput: function() { - return this.$.searchInput; - }, - isSearchFocused: function() { - return this.$.searchTerm.focused; - }, - computeIconTabIndex_: function(narrow) { - return narrow ? 0 : -1; - }, - computeIsSpinnerShown_: function() { - return this.spinnerActive && this.showingSearch; - }, - onInputBlur_: function() { - if (!this.hasSearchText_) this.showingSearch = false; - }, - onBindValueChanged_: function() { - var newValue = this.$.searchInput.bindValue; - this.hasSearchText_ = newValue != ''; - if (newValue != '') this.showingSearch = true; - }, - showSearch_: function(e) { - if (e.target != this.$.clearSearch) this.showingSearch = true; - }, - hideSearch_: function(e) { - this.showingSearch = false; - e.stopPropagation(); - } -}); - +Polymer({is:"cr-toolbar-search-field",behaviors:[CrSearchFieldBehavior],properties:{narrow:{type:Boolean,reflectToAttribute:true},label:String,clearLabel:String,spinnerActive:{type:Boolean,reflectToAttribute:true},hasSearchText_:Boolean,isSpinnerShown_:{type:Boolean,computed:"computeIsSpinnerShown_(spinnerActive, showingSearch)"}},listeners:{tap:"showSearch_","searchInput.bind-value-changed":"onBindValueChanged_"},getSearchInput:function(){return this.$.searchInput},isSearchFocused:function(){return this.$.searchTerm.focused},computeIconTabIndex_:function(narrow){return narrow?0:-1},computeIsSpinnerShown_:function(){return this.spinnerActive&&this.showingSearch},onInputBlur_:function(){if(!this.hasSearchText_)this.showingSearch=false},onBindValueChanged_:function(){var newValue=this.$.searchInput.bindValue;this.hasSearchText_=newValue!="";if(newValue!="")this.showingSearch=true},showSearch_:function(e){if(e.target!=this.$.clearSearch)this.showingSearch=true},hideSearch_:function(e){this.showingSearch=false;e.stopPropagation()}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'cr-toolbar', - properties: { - pageName: String, - searchPrompt: String, - clearLabel: String, - menuLabel: String, - menuPromo: String, - spinnerActive: Boolean, - showMenu: { - type: Boolean, - value: false - }, - showMenuPromo: { - type: Boolean, - value: false - }, - closeMenuPromo: String, - narrow_: { - type: Boolean, - reflectToAttribute: true - }, - showingSearch_: { - type: Boolean, - reflectToAttribute: true - } - }, - observers: [ 'possiblyShowMenuPromo_(showMenu, showMenuPromo, showingSearch_)' ], - getSearchField: function() { - return this.$.search; - }, - onClosePromoTap_: function() { - this.showMenuPromo = false; - }, - onMenuTap_: function() { - this.fire('cr-menu-tap'); - this.onClosePromoTap_(); - }, - possiblyShowMenuPromo_: function() { - Polymer.RenderStatus.afterNextRender(this, function() { - if (this.showMenu && this.showMenuPromo && !this.showingSearch_) { - this.$$('#menuPromo').animate({ - opacity: [ 0, .9 ] - }, { - duration: 500, - fill: 'forwards' - }); - this.fire('cr-menu-promo-shown'); - } - }.bind(this)); - }, - titleIfNotShowMenuPromo_: function(title, showMenuPromo) { - return showMenuPromo ? '' : title; - } -}); - +Polymer({is:"cr-toolbar",properties:{pageName:String,searchPrompt:String,clearLabel:String,menuLabel:String,menuPromo:String,spinnerActive:Boolean,showMenu:{type:Boolean,value:false},showMenuPromo:{type:Boolean,value:false},closeMenuPromo:String,narrow_:{type:Boolean,reflectToAttribute:true},showingSearch_:{type:Boolean,reflectToAttribute:true}},observers:["possiblyShowMenuPromo_(showMenu, showMenuPromo, showingSearch_)"],getSearchField:function(){return this.$.search},onClosePromoTap_:function(){this.showMenuPromo=false},onMenuTap_:function(){this.fire("cr-menu-tap");this.onClosePromoTap_()},possiblyShowMenuPromo_:function(){Polymer.RenderStatus.afterNextRender(this,function(){if(this.showMenu&&this.showMenuPromo&&!this.showingSearch_){this.$$("#menuPromo").animate({opacity:[0,.9]},{duration:500,fill:"forwards"});this.fire("cr-menu-promo-shown")}}.bind(this))},titleIfNotShowMenuPromo_:function(title,showMenuPromo){return showMenuPromo?"":title}}); // 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. -cr.define('downloads', function() { - var Toolbar = Polymer({ - is: 'downloads-toolbar', - properties: { - downloadsShowing: { - reflectToAttribute: true, - type: Boolean, - value: false, - observer: 'downloadsShowingChanged_' - }, - spinnerActive: { - type: Boolean, - notify: true - } - }, - listeners: { - 'paper-dropdown-close': 'onPaperDropdownClose_', - 'paper-dropdown-open': 'onPaperDropdownOpen_' - }, - canUndo: function() { - return !this.$.toolbar.getSearchField().isSearchFocused(); - }, - canClearAll: function() { - return !this.$.toolbar.getSearchField().getValue() && this.downloadsShowing; - }, - onFindCommand: function() { - this.$.toolbar.getSearchField().showAndFocus(); - }, - closeMoreActions_: function() { - this.$.more.close(); - }, - downloadsShowingChanged_: function() { - this.updateClearAll_(); - }, - onClearAllTap_: function() { - assert(this.canClearAll()); - downloads.ActionService.getInstance().clearAll(); - }, - onPaperDropdownClose_: function() { - window.removeEventListener('resize', assert(this.boundClose_)); - }, - onItemBlur_: function(e) { - var menu = this.$$('paper-menu'); - if (menu.items.indexOf(e.relatedTarget) >= 0) return; - this.$.more.restoreFocusOnClose = false; - this.closeMoreActions_(); - this.$.more.restoreFocusOnClose = true; - }, - onPaperDropdownOpen_: function() { - this.boundClose_ = this.boundClose_ || this.closeMoreActions_.bind(this); - window.addEventListener('resize', this.boundClose_); - }, - onSearchChanged_: function(event) { - var actionService = downloads.ActionService.getInstance(); - if (actionService.search(event.detail)) this.spinnerActive = actionService.isSearching(); - this.updateClearAll_(); - }, - onOpenDownloadsFolderTap_: function() { - downloads.ActionService.getInstance().openDownloadsFolder(); - }, - updateClearAll_: function() { - this.$$('paper-menu .clear-all').hidden = !this.canClearAll(); - } - }); - return { - Toolbar: Toolbar - }; -}); - +cr.define("downloads",function(){var Toolbar=Polymer({is:"downloads-toolbar",properties:{downloadsShowing:{reflectToAttribute:true,type:Boolean,value:false,observer:"downloadsShowingChanged_"},spinnerActive:{type:Boolean,notify:true}},listeners:{"paper-dropdown-close":"onPaperDropdownClose_","paper-dropdown-open":"onPaperDropdownOpen_"},canUndo:function(){return!this.$.toolbar.getSearchField().isSearchFocused()},canClearAll:function(){return!this.$.toolbar.getSearchField().getValue()&&this.downloadsShowing},onFindCommand:function(){this.$.toolbar.getSearchField().showAndFocus()},closeMoreActions_:function(){this.$.more.close()},downloadsShowingChanged_:function(){this.updateClearAll_()},onClearAllTap_:function(){assert(this.canClearAll());downloads.ActionService.getInstance().clearAll()},onPaperDropdownClose_:function(){window.removeEventListener("resize",assert(this.boundClose_))},onItemBlur_:function(e){var menu=this.$$("paper-menu");if(menu.items.indexOf(e.relatedTarget)>=0)return;this.$.more.restoreFocusOnClose=false;this.closeMoreActions_();this.$.more.restoreFocusOnClose=true},onPaperDropdownOpen_:function(){this.boundClose_=this.boundClose_||this.closeMoreActions_.bind(this);window.addEventListener("resize",this.boundClose_)},onSearchChanged_:function(event){var actionService=downloads.ActionService.getInstance();if(actionService.search(event.detail))this.spinnerActive=actionService.isSearching();this.updateClearAll_()},onOpenDownloadsFolderTap_:function(){downloads.ActionService.getInstance().openDownloadsFolder()},updateClearAll_:function(){this.$$("paper-menu .clear-all").hidden=!this.canClearAll()}});return{Toolbar:Toolbar}}); // 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. -cr.define('downloads', function() { - var Manager = Polymer({ - is: 'downloads-manager', - properties: { - hasDownloads_: { - observer: 'hasDownloadsChanged_', - type: Boolean - }, - hasShadow_: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - items_: { - type: Array, - value: function() { - return []; - } - }, - spinnerActive_: { - type: Boolean, - notify: true - } - }, - hostAttributes: { - loading: true - }, - listeners: { - 'downloads-list.scroll': 'onListScroll_' - }, - observers: [ 'itemsChanged_(items_.*)' ], - clearAll_: function() { - this.set('items_', []); - }, - hasDownloadsChanged_: function() { - if (loadTimeData.getBoolean('allowDeletingHistory')) this.$.toolbar.downloadsShowing = this.hasDownloads_; - if (this.hasDownloads_) { - this.$['downloads-list'].fire('iron-resize'); - } else { - var isSearching = downloads.ActionService.getInstance().isSearching(); - var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; - this.$['no-downloads'].querySelector('span').textContent = loadTimeData.getString(messageToShow); - } - }, - insertItems_: function(index, list) { - this.splice.apply(this, [ 'items_', index, 0 ].concat(list)); - this.updateHideDates_(index, index + list.length); - this.removeAttribute('loading'); - this.spinnerActive_ = false; - }, - itemsChanged_: function() { - this.hasDownloads_ = this.items_.length > 0; - }, - onCanExecute_: function(e) { - e = e; - switch (e.command.id) { - case 'undo-command': - e.canExecute = this.$.toolbar.canUndo(); - break; - - case 'clear-all-command': - e.canExecute = this.$.toolbar.canClearAll(); - break; - - case 'find-command': - e.canExecute = true; - break; - } - }, - onCommand_: function(e) { - if (e.command.id == 'clear-all-command') downloads.ActionService.getInstance().clearAll(); else if (e.command.id == 'undo-command') downloads.ActionService.getInstance().undo(); else if (e.command.id == 'find-command') this.$.toolbar.onFindCommand(); - }, - onListScroll_: function() { - var list = this.$['downloads-list']; - if (list.scrollHeight - list.scrollTop - list.offsetHeight <= 100) { - downloads.ActionService.getInstance().loadMore(); - } - this.hasShadow_ = list.scrollTop > 0; - }, - onLoad_: function() { - cr.ui.decorate('command', cr.ui.Command); - document.addEventListener('canExecute', this.onCanExecute_.bind(this)); - document.addEventListener('command', this.onCommand_.bind(this)); - downloads.ActionService.getInstance().loadMore(); - }, - removeItem_: function(index) { - this.splice('items_', index, 1); - this.updateHideDates_(index, index); - this.onListScroll_(); - }, - updateHideDates_: function(start, end) { - for (var i = start; i <= end; ++i) { - var current = this.items_[i]; - if (!current) continue; - var prev = this.items_[i - 1]; - current.hideDate = !!prev && prev.date_string == current.date_string; - } - }, - updateItem_: function(index, data) { - this.set('items_.' + index, data); - this.updateHideDates_(index, index); - var list = this.$['downloads-list']; - list.updateSizeForItem(index); - } - }); - Manager.clearAll = function() { - Manager.get().clearAll_(); - }; - Manager.get = function() { - return queryRequiredElement('downloads-manager'); - }; - Manager.insertItems = function(index, list) { - Manager.get().insertItems_(index, list); - }; - Manager.onLoad = function() { - Manager.get().onLoad_(); - }; - Manager.removeItem = function(index) { - Manager.get().removeItem_(index); - }; - Manager.updateItem = function(index, data) { - Manager.get().updateItem_(index, data); - }; - return { - Manager: Manager - }; -}); - +cr.define("downloads",function(){var Manager=Polymer({is:"downloads-manager",properties:{hasDownloads_:{observer:"hasDownloadsChanged_",type:Boolean},hasShadow_:{type:Boolean,value:false,reflectToAttribute:true},items_:{type:Array,value:function(){return[]}},spinnerActive_:{type:Boolean,notify:true}},hostAttributes:{loading:true},listeners:{"downloads-list.scroll":"onListScroll_"},observers:["itemsChanged_(items_.*)"],clearAll_:function(){this.set("items_",[])},hasDownloadsChanged_:function(){if(loadTimeData.getBoolean("allowDeletingHistory"))this.$.toolbar.downloadsShowing=this.hasDownloads_;if(this.hasDownloads_){this.$["downloads-list"].fire("iron-resize")}else{var isSearching=downloads.ActionService.getInstance().isSearching();var messageToShow=isSearching?"noSearchResults":"noDownloads";this.$["no-downloads"].querySelector("span").textContent=loadTimeData.getString(messageToShow)}},insertItems_:function(index,list){this.splice.apply(this,["items_",index,0].concat(list));this.updateHideDates_(index,index+list.length);this.removeAttribute("loading");this.spinnerActive_=false},itemsChanged_:function(){this.hasDownloads_=this.items_.length>0},onCanExecute_:function(e){e=e;switch(e.command.id){case"undo-command":e.canExecute=this.$.toolbar.canUndo();break;case"clear-all-command":e.canExecute=this.$.toolbar.canClearAll();break;case"find-command":e.canExecute=true;break}},onCommand_:function(e){if(e.command.id=="clear-all-command")downloads.ActionService.getInstance().clearAll();else if(e.command.id=="undo-command")downloads.ActionService.getInstance().undo();else if(e.command.id=="find-command")this.$.toolbar.onFindCommand()},onListScroll_:function(){var list=this.$["downloads-list"];if(list.scrollHeight-list.scrollTop-list.offsetHeight<=100){downloads.ActionService.getInstance().loadMore()}this.hasShadow_=list.scrollTop>0},onLoad_:function(){cr.ui.decorate("command",cr.ui.Command);document.addEventListener("canExecute",this.onCanExecute_.bind(this));document.addEventListener("command",this.onCommand_.bind(this));downloads.ActionService.getInstance().loadMore()},removeItem_:function(index){this.splice("items_",index,1);this.updateHideDates_(index,index);this.onListScroll_()},updateHideDates_:function(start,end){for(var i=start;i<=end;++i){var current=this.items_[i];if(!current)continue;var prev=this.items_[i-1];current.hideDate=!!prev&&prev.date_string==current.date_string}},updateItem_:function(index,data){this.set("items_."+index,data);this.updateHideDates_(index,index);var list=this.$["downloads-list"];list.updateSizeForItem(index)}});Manager.clearAll=function(){Manager.get().clearAll_()};Manager.get=function(){return queryRequiredElement("downloads-manager")};Manager.insertItems=function(index,list){Manager.get().insertItems_(index,list)};Manager.onLoad=function(){Manager.get().onLoad_()};Manager.removeItem=function(index){Manager.get().removeItem_(index)};Manager.updateItem=function(index,data){Manager.get().updateItem_(index,data)};return{Manager:Manager}}); // 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. -window.addEventListener('load', function() { - downloads.Manager.onLoad(); - document.fonts.load('bold 12px Roboto'); -}); +window.addEventListener("load",function(){downloads.Manager.onLoad();document.fonts.load("bold 12px Roboto")}); \ No newline at end of file
diff --git a/chrome/browser/resources/md_downloads/vulcanized.html b/chrome/browser/resources/md_downloads/vulcanized.html index 2a45dc6..b810aaa 100644 --- a/chrome/browser/resources/md_downloads/vulcanized.html +++ b/chrome/browser/resources/md_downloads/vulcanized.html
@@ -1082,7 +1082,8 @@ #content:not(.is-active) { background: rgba(255, 255, 255, .6); - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .03), 0 1px 4px 0 rgba(0, 0, 0, .048), + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .03), + 0 1px 4px 0 rgba(0, 0, 0, .048), 0 3px 1px -2px rgba(0, 0, 0, .12); }
diff --git a/chrome/browser/resources/md_history/app.crisper.js b/chrome/browser/resources/md_history/app.crisper.js index 7f6560e2..cce7210 100644 --- a/chrome/browser/resources/md_history/app.crisper.js +++ b/chrome/browser/resources/md_history/app.crisper.js
@@ -1,5527 +1,86 @@ // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -function PromiseResolver() { - this.resolve_; - this.reject_; - this.promise_ = new Promise(function(resolve, reject) { - this.resolve_ = resolve; - this.reject_ = reject; - }.bind(this)); -} - -PromiseResolver.prototype = { - get promise() { - return this.promise_; - }, - set promise(p) { - assertNotReached(); - }, - get resolve() { - return this.resolve_; - }, - set resolve(r) { - assertNotReached(); - }, - get reject() { - return this.reject_; - }, - set reject(s) { - assertNotReached(); - } -}; - +function PromiseResolver(){this.resolve_;this.reject_;this.promise_=new Promise(function(resolve,reject){this.resolve_=resolve;this.reject_=reject}.bind(this))}PromiseResolver.prototype={get promise(){return this.promise_},set promise(p){assertNotReached()},get resolve(){return this.resolve_},set resolve(r){assertNotReached()},get reject(){return this.reject_},set reject(s){assertNotReached()}}; // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var global = this; - -var WebUIListener; - -var cr = cr || function() { - 'use strict'; - function exportPath(name, opt_object, opt_objectToExportTo) { - var parts = name.split('.'); - var cur = opt_objectToExportTo || global; - for (var part; parts.length && (part = parts.shift()); ) { - if (!parts.length && opt_object !== undefined) { - cur[part] = opt_object; - } else if (part in cur) { - cur = cur[part]; - } else { - cur = cur[part] = {}; - } - } - return cur; - } - function dispatchPropertyChange(target, propertyName, newValue, oldValue) { - var e = new Event(propertyName + 'Change'); - e.propertyName = propertyName; - e.newValue = newValue; - e.oldValue = oldValue; - target.dispatchEvent(e); - } - function getAttributeName(jsName) { - return jsName.replace(/([A-Z])/g, '-$1').toLowerCase(); - } - var PropertyKind = { - JS: 'js', - ATTR: 'attr', - BOOL_ATTR: 'boolAttr' - }; - function getGetter(name, kind) { - switch (kind) { - case PropertyKind.JS: - var privateName = name + '_'; - return function() { - return this[privateName]; - }; - - case PropertyKind.ATTR: - var attributeName = getAttributeName(name); - return function() { - return this.getAttribute(attributeName); - }; - - case PropertyKind.BOOL_ATTR: - var attributeName = getAttributeName(name); - return function() { - return this.hasAttribute(attributeName); - }; - } - throw 'not reached'; - } - function getSetter(name, kind, opt_setHook) { - switch (kind) { - case PropertyKind.JS: - var privateName = name + '_'; - return function(value) { - var oldValue = this[name]; - if (value !== oldValue) { - this[privateName] = value; - if (opt_setHook) opt_setHook.call(this, value, oldValue); - dispatchPropertyChange(this, name, value, oldValue); - } - }; - - case PropertyKind.ATTR: - var attributeName = getAttributeName(name); - return function(value) { - var oldValue = this[name]; - if (value !== oldValue) { - if (value == undefined) this.removeAttribute(attributeName); else this.setAttribute(attributeName, value); - if (opt_setHook) opt_setHook.call(this, value, oldValue); - dispatchPropertyChange(this, name, value, oldValue); - } - }; - - case PropertyKind.BOOL_ATTR: - var attributeName = getAttributeName(name); - return function(value) { - var oldValue = this[name]; - if (value !== oldValue) { - if (value) this.setAttribute(attributeName, name); else this.removeAttribute(attributeName); - if (opt_setHook) opt_setHook.call(this, value, oldValue); - dispatchPropertyChange(this, name, value, oldValue); - } - }; - } - throw 'not reached'; - } - function defineProperty(obj, name, opt_kind, opt_setHook) { - if (typeof obj == 'function') obj = obj.prototype; - var kind = opt_kind || PropertyKind.JS; - if (!obj.__lookupGetter__(name)) obj.__defineGetter__(name, getGetter(name, kind)); - if (!obj.__lookupSetter__(name)) obj.__defineSetter__(name, getSetter(name, kind, opt_setHook)); - } - var uidCounter = 1; - function createUid() { - return uidCounter++; - } - function getUid(item) { - if (item.hasOwnProperty('uid')) return item.uid; - return item.uid = createUid(); - } - function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) { - var e = new Event(type, { - bubbles: opt_bubbles, - cancelable: opt_cancelable === undefined || opt_cancelable - }); - return target.dispatchEvent(e); - } - function define(name, fun) { - var obj = exportPath(name); - var exports = fun(); - for (var propertyName in exports) { - var propertyDescriptor = Object.getOwnPropertyDescriptor(exports, propertyName); - if (propertyDescriptor) Object.defineProperty(obj, propertyName, propertyDescriptor); - } - } - function addSingletonGetter(ctor) { - ctor.getInstance = function() { - return ctor.instance_ || (ctor.instance_ = new ctor()); - }; - } - function makePublic(ctor, methods, opt_target) { - methods.forEach(function(method) { - ctor[method] = function() { - var target = opt_target ? document.getElementById(opt_target) : ctor.getInstance(); - return target[method + '_'].apply(target, arguments); - }; - }); - } - var chromeSendResolverMap = {}; - function webUIResponse(id, isSuccess, response) { - var resolver = chromeSendResolverMap[id]; - delete chromeSendResolverMap[id]; - if (isSuccess) resolver.resolve(response); else resolver.reject(response); - } - function sendWithPromise(methodName, var_args) { - var args = Array.prototype.slice.call(arguments, 1); - var promiseResolver = new PromiseResolver(); - var id = methodName + '_' + createUid(); - chromeSendResolverMap[id] = promiseResolver; - chrome.send(methodName, [ id ].concat(args)); - return promiseResolver.promise; - } - var webUIListenerMap = {}; - function webUIListenerCallback(event, var_args) { - var eventListenersMap = webUIListenerMap[event]; - if (!eventListenersMap) { - return; - } - var args = Array.prototype.slice.call(arguments, 1); - for (var listenerId in eventListenersMap) { - eventListenersMap[listenerId].apply(null, args); - } - } - function addWebUIListener(eventName, callback) { - webUIListenerMap[eventName] = webUIListenerMap[eventName] || {}; - var uid = createUid(); - webUIListenerMap[eventName][uid] = callback; - return { - eventName: eventName, - uid: uid - }; - } - function removeWebUIListener(listener) { - var listenerExists = webUIListenerMap[listener.eventName] && webUIListenerMap[listener.eventName][listener.uid]; - if (listenerExists) { - delete webUIListenerMap[listener.eventName][listener.uid]; - return true; - } - return false; - } - return { - addSingletonGetter: addSingletonGetter, - createUid: createUid, - define: define, - defineProperty: defineProperty, - dispatchPropertyChange: dispatchPropertyChange, - dispatchSimpleEvent: dispatchSimpleEvent, - exportPath: exportPath, - getUid: getUid, - makePublic: makePublic, - PropertyKind: PropertyKind, - addWebUIListener: addWebUIListener, - removeWebUIListener: removeWebUIListener, - sendWithPromise: sendWithPromise, - webUIListenerCallback: webUIListenerCallback, - webUIResponse: webUIResponse, - get doc() { - return document; - }, - get isMac() { - return /Mac/.test(navigator.platform); - }, - get isWindows() { - return /Win/.test(navigator.platform); - }, - get isChromeOS() { - return /CrOS/.test(navigator.userAgent); - }, - get isLinux() { - return /Linux/.test(navigator.userAgent); - }, - get isAndroid() { - return /Android/.test(navigator.userAgent); - }, - get isIOS() { - return /iPad|iPhone|iPod/.test(navigator.platform); - } - }; -}(); - +var global=this;var WebUIListener;var cr=cr||function(){"use strict";function exportPath(name,opt_object,opt_objectToExportTo){var parts=name.split(".");var cur=opt_objectToExportTo||global;for(var part;parts.length&&(part=parts.shift());){if(!parts.length&&opt_object!==undefined){cur[part]=opt_object}else if(part in cur){cur=cur[part]}else{cur=cur[part]={}}}return cur}function dispatchPropertyChange(target,propertyName,newValue,oldValue){var e=new Event(propertyName+"Change");e.propertyName=propertyName;e.newValue=newValue;e.oldValue=oldValue;target.dispatchEvent(e)}function getAttributeName(jsName){return jsName.replace(/([A-Z])/g,"-$1").toLowerCase()}var PropertyKind={JS:"js",ATTR:"attr",BOOL_ATTR:"boolAttr"};function getGetter(name,kind){switch(kind){case PropertyKind.JS:var privateName=name+"_";return function(){return this[privateName]};case PropertyKind.ATTR:var attributeName=getAttributeName(name);return function(){return this.getAttribute(attributeName)};case PropertyKind.BOOL_ATTR:var attributeName=getAttributeName(name);return function(){return this.hasAttribute(attributeName)}}throw"not reached"}function getSetter(name,kind,opt_setHook){switch(kind){case PropertyKind.JS:var privateName=name+"_";return function(value){var oldValue=this[name];if(value!==oldValue){this[privateName]=value;if(opt_setHook)opt_setHook.call(this,value,oldValue);dispatchPropertyChange(this,name,value,oldValue)}};case PropertyKind.ATTR:var attributeName=getAttributeName(name);return function(value){var oldValue=this[name];if(value!==oldValue){if(value==undefined)this.removeAttribute(attributeName);else this.setAttribute(attributeName,value);if(opt_setHook)opt_setHook.call(this,value,oldValue);dispatchPropertyChange(this,name,value,oldValue)}};case PropertyKind.BOOL_ATTR:var attributeName=getAttributeName(name);return function(value){var oldValue=this[name];if(value!==oldValue){if(value)this.setAttribute(attributeName,name);else this.removeAttribute(attributeName);if(opt_setHook)opt_setHook.call(this,value,oldValue);dispatchPropertyChange(this,name,value,oldValue)}}}throw"not reached"}function defineProperty(obj,name,opt_kind,opt_setHook){if(typeof obj=="function")obj=obj.prototype;var kind=opt_kind||PropertyKind.JS;if(!obj.__lookupGetter__(name))obj.__defineGetter__(name,getGetter(name,kind));if(!obj.__lookupSetter__(name))obj.__defineSetter__(name,getSetter(name,kind,opt_setHook))}var uidCounter=1;function createUid(){return uidCounter++}function getUid(item){if(item.hasOwnProperty("uid"))return item.uid;return item.uid=createUid()}function dispatchSimpleEvent(target,type,opt_bubbles,opt_cancelable){var e=new Event(type,{bubbles:opt_bubbles,cancelable:opt_cancelable===undefined||opt_cancelable});return target.dispatchEvent(e)}function define(name,fun){var obj=exportPath(name);var exports=fun();for(var propertyName in exports){var propertyDescriptor=Object.getOwnPropertyDescriptor(exports,propertyName);if(propertyDescriptor)Object.defineProperty(obj,propertyName,propertyDescriptor)}}function addSingletonGetter(ctor){ctor.getInstance=function(){return ctor.instance_||(ctor.instance_=new ctor)}}function makePublic(ctor,methods,opt_target){methods.forEach(function(method){ctor[method]=function(){var target=opt_target?document.getElementById(opt_target):ctor.getInstance();return target[method+"_"].apply(target,arguments)}})}var chromeSendResolverMap={};function webUIResponse(id,isSuccess,response){var resolver=chromeSendResolverMap[id];delete chromeSendResolverMap[id];if(isSuccess)resolver.resolve(response);else resolver.reject(response)}function sendWithPromise(methodName,var_args){var args=Array.prototype.slice.call(arguments,1);var promiseResolver=new PromiseResolver;var id=methodName+"_"+createUid();chromeSendResolverMap[id]=promiseResolver;chrome.send(methodName,[id].concat(args));return promiseResolver.promise}var webUIListenerMap={};function webUIListenerCallback(event,var_args){var eventListenersMap=webUIListenerMap[event];if(!eventListenersMap){return}var args=Array.prototype.slice.call(arguments,1);for(var listenerId in eventListenersMap){eventListenersMap[listenerId].apply(null,args)}}function addWebUIListener(eventName,callback){webUIListenerMap[eventName]=webUIListenerMap[eventName]||{};var uid=createUid();webUIListenerMap[eventName][uid]=callback;return{eventName:eventName,uid:uid}}function removeWebUIListener(listener){var listenerExists=webUIListenerMap[listener.eventName]&&webUIListenerMap[listener.eventName][listener.uid];if(listenerExists){delete webUIListenerMap[listener.eventName][listener.uid];return true}return false}return{addSingletonGetter:addSingletonGetter,createUid:createUid,define:define,defineProperty:defineProperty,dispatchPropertyChange:dispatchPropertyChange,dispatchSimpleEvent:dispatchSimpleEvent,exportPath:exportPath,getUid:getUid,makePublic:makePublic,PropertyKind:PropertyKind,addWebUIListener:addWebUIListener,removeWebUIListener:removeWebUIListener,sendWithPromise:sendWithPromise,webUIListenerCallback:webUIListenerCallback,webUIResponse:webUIResponse,get doc(){return document},get isMac(){return/Mac/.test(navigator.platform)},get isWindows(){return/Win/.test(navigator.platform)},get isChromeOS(){return/CrOS/.test(navigator.userAgent)},get isLinux(){return/Linux/.test(navigator.userAgent)},get isAndroid(){return/Android/.test(navigator.userAgent)},get isIOS(){return/iPad|iPhone|iPod/.test(navigator.platform)}}}(); // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -cr.define('cr.ui', function() { - function decorate(source, constr) { - var elements; - if (typeof source == 'string') elements = cr.doc.querySelectorAll(source); else elements = [ source ]; - for (var i = 0, el; el = elements[i]; i++) { - if (!(el instanceof constr)) constr.decorate(el); - } - } - function createElementHelper(tagName, opt_bag) { - var doc; - if (opt_bag && opt_bag.ownerDocument) doc = opt_bag.ownerDocument; else doc = cr.doc; - return doc.createElement(tagName); - } - function define(tagNameOrFunction) { - var createFunction, tagName; - if (typeof tagNameOrFunction == 'function') { - createFunction = tagNameOrFunction; - tagName = ''; - } else { - createFunction = createElementHelper; - tagName = tagNameOrFunction; - } - function f(opt_propertyBag) { - var el = createFunction(tagName, opt_propertyBag); - f.decorate(el); - for (var propertyName in opt_propertyBag) { - el[propertyName] = opt_propertyBag[propertyName]; - } - return el; - } - f.decorate = function(el) { - el.__proto__ = f.prototype; - el.decorate(); - }; - return f; - } - function limitInputWidth(el, parentEl, min, opt_scale) { - el.style.width = '10px'; - var doc = el.ownerDocument; - var win = doc.defaultView; - var computedStyle = win.getComputedStyle(el); - var parentComputedStyle = win.getComputedStyle(parentEl); - var rtl = computedStyle.direction == 'rtl'; - var inputRect = el.getBoundingClientRect(); - var parentRect = parentEl.getBoundingClientRect(); - var startPos = rtl ? parentRect.right - inputRect.right : inputRect.left - parentRect.left; - var inner = parseInt(computedStyle.borderLeftWidth, 10) + parseInt(computedStyle.paddingLeft, 10) + parseInt(computedStyle.paddingRight, 10) + parseInt(computedStyle.borderRightWidth, 10); - var parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) : parseInt(parentComputedStyle.paddingRight, 10); - var max = parentEl.clientWidth - startPos - inner - parentPadding; - if (opt_scale) max *= opt_scale; - function limit() { - if (el.scrollWidth > max) { - el.style.width = max + 'px'; - } else { - el.style.width = 0; - var sw = el.scrollWidth; - if (sw < min) { - el.style.width = min + 'px'; - } else { - el.style.width = sw + 'px'; - } - } - } - el.addEventListener('input', limit); - limit(); - } - function toCssPx(pixels) { - if (!window.isFinite(pixels)) console.error('Pixel value is not a number: ' + pixels); - return Math.round(pixels) + 'px'; - } - function swallowDoubleClick(e) { - var doc = e.target.ownerDocument; - var counter = Math.min(1, e.detail); - function swallow(e) { - e.stopPropagation(); - e.preventDefault(); - } - function onclick(e) { - if (e.detail > counter) { - counter = e.detail; - swallow(e); - } else { - doc.removeEventListener('dblclick', swallow, true); - doc.removeEventListener('click', onclick, true); - } - } - setTimeout(function() { - doc.addEventListener('click', onclick, true); - doc.addEventListener('dblclick', swallow, true); - }, 0); - } - return { - decorate: decorate, - define: define, - limitInputWidth: limitInputWidth, - toCssPx: toCssPx, - swallowDoubleClick: swallowDoubleClick - }; -}); - +cr.define("cr.ui",function(){function decorate(source,constr){var elements;if(typeof source=="string")elements=cr.doc.querySelectorAll(source);else elements=[source];for(var i=0,el;el=elements[i];i++){if(!(el instanceof constr))constr.decorate(el)}}function createElementHelper(tagName,opt_bag){var doc;if(opt_bag&&opt_bag.ownerDocument)doc=opt_bag.ownerDocument;else doc=cr.doc;return doc.createElement(tagName)}function define(tagNameOrFunction){var createFunction,tagName;if(typeof tagNameOrFunction=="function"){createFunction=tagNameOrFunction;tagName=""}else{createFunction=createElementHelper;tagName=tagNameOrFunction}function f(opt_propertyBag){var el=createFunction(tagName,opt_propertyBag);f.decorate(el);for(var propertyName in opt_propertyBag){el[propertyName]=opt_propertyBag[propertyName]}return el}f.decorate=function(el){el.__proto__=f.prototype;el.decorate()};return f}function limitInputWidth(el,parentEl,min,opt_scale){el.style.width="10px";var doc=el.ownerDocument;var win=doc.defaultView;var computedStyle=win.getComputedStyle(el);var parentComputedStyle=win.getComputedStyle(parentEl);var rtl=computedStyle.direction=="rtl";var inputRect=el.getBoundingClientRect();var parentRect=parentEl.getBoundingClientRect();var startPos=rtl?parentRect.right-inputRect.right:inputRect.left-parentRect.left;var inner=parseInt(computedStyle.borderLeftWidth,10)+parseInt(computedStyle.paddingLeft,10)+parseInt(computedStyle.paddingRight,10)+parseInt(computedStyle.borderRightWidth,10);var parentPadding=rtl?parseInt(parentComputedStyle.paddingLeft,10):parseInt(parentComputedStyle.paddingRight,10);var max=parentEl.clientWidth-startPos-inner-parentPadding;if(opt_scale)max*=opt_scale;function limit(){if(el.scrollWidth>max){el.style.width=max+"px"}else{el.style.width=0;var sw=el.scrollWidth;if(sw<min){el.style.width=min+"px"}else{el.style.width=sw+"px"}}}el.addEventListener("input",limit);limit()}function toCssPx(pixels){if(!window.isFinite(pixels))console.error("Pixel value is not a number: "+pixels);return Math.round(pixels)+"px"}function swallowDoubleClick(e){var doc=e.target.ownerDocument;var counter=Math.min(1,e.detail);function swallow(e){e.stopPropagation();e.preventDefault()}function onclick(e){if(e.detail>counter){counter=e.detail;swallow(e)}else{doc.removeEventListener("dblclick",swallow,true);doc.removeEventListener("click",onclick,true)}}setTimeout(function(){doc.addEventListener("click",onclick,true);doc.addEventListener("dblclick",swallow,true)},0)}return{decorate:decorate,define:define,limitInputWidth:limitInputWidth,toCssPx:toCssPx,swallowDoubleClick:swallowDoubleClick}}); // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -cr.define('cr.ui', function() { - function KeyboardShortcut(shortcut) { - var mods = {}; - var ident = ''; - shortcut.split('|').forEach(function(part) { - var partLc = part.toLowerCase(); - switch (partLc) { - case 'alt': - case 'ctrl': - case 'meta': - case 'shift': - mods[partLc + 'Key'] = true; - break; +cr.define("cr.ui",function(){function KeyboardShortcut(shortcut){var mods={};var ident="";shortcut.split("|").forEach(function(part){var partLc=part.toLowerCase();switch(partLc){case"alt":case"ctrl":case"meta":case"shift":mods[partLc+"Key"]=true;break;default:if(ident)throw Error("Invalid shortcut");ident=part}});this.ident_=ident;this.mods_=mods}KeyboardShortcut.prototype={matchesEvent:function(e){if(e.key==this.ident_){var mods=this.mods_;return["altKey","ctrlKey","metaKey","shiftKey"].every(function(k){return e[k]==!!mods[k]})}return false}};var Command=cr.ui.define("command");Command.prototype={__proto__:HTMLElement.prototype,decorate:function(){CommandManager.init(assert(this.ownerDocument));if(this.hasAttribute("shortcut"))this.shortcut=this.getAttribute("shortcut")},execute:function(opt_element){if(this.disabled)return;var doc=this.ownerDocument;if(doc.activeElement){var e=new Event("command",{bubbles:true});e.command=this;(opt_element||doc.activeElement).dispatchEvent(e)}},canExecuteChange:function(opt_node){dispatchCanExecuteEvent(this,opt_node||this.ownerDocument.activeElement)},shortcut_:"",get shortcut(){return this.shortcut_},set shortcut(shortcut){var oldShortcut=this.shortcut_;if(shortcut!==oldShortcut){this.keyboardShortcuts_=shortcut.split(/\s+/).map(function(shortcut){return new KeyboardShortcut(shortcut)});this.shortcut_=shortcut;cr.dispatchPropertyChange(this,"shortcut",this.shortcut_,oldShortcut)}},matchesEvent:function(e){if(!this.keyboardShortcuts_)return false;return this.keyboardShortcuts_.some(function(keyboardShortcut){return keyboardShortcut.matchesEvent(e)})}};cr.defineProperty(Command,"label",cr.PropertyKind.ATTR);cr.defineProperty(Command,"disabled",cr.PropertyKind.BOOL_ATTR);cr.defineProperty(Command,"hidden",cr.PropertyKind.BOOL_ATTR);cr.defineProperty(Command,"checked",cr.PropertyKind.BOOL_ATTR);cr.defineProperty(Command,"hideShortcutText",cr.PropertyKind.BOOL_ATTR);function dispatchCanExecuteEvent(command,target){var e=new CanExecuteEvent(command);target.dispatchEvent(e);command.disabled=!e.canExecute}var commandManagers={};function CommandManager(doc){doc.addEventListener("focus",this.handleFocus_.bind(this),true);doc.addEventListener("keydown",this.handleKeyDown_.bind(this),false)}CommandManager.init=function(doc){var uid=cr.getUid(doc);if(!(uid in commandManagers)){commandManagers[uid]=new CommandManager(doc)}};CommandManager.prototype={handleFocus_:function(e){var target=e.target;if(target.menu||target.command)return;var commands=Array.prototype.slice.call(target.ownerDocument.querySelectorAll("command"));commands.forEach(function(command){dispatchCanExecuteEvent(command,target)})},handleKeyDown_:function(e){var target=e.target;var commands=Array.prototype.slice.call(target.ownerDocument.querySelectorAll("command"));for(var i=0,command;command=commands[i];i++){if(command.matchesEvent(e)){command.canExecuteChange();if(!command.disabled){e.preventDefault();e.stopPropagation();command.execute();return}}}}};function CanExecuteEvent(command){var e=new Event("canExecute",{bubbles:true,cancelable:true});e.__proto__=CanExecuteEvent.prototype;e.command=command;return e}CanExecuteEvent.prototype={__proto__:Event.prototype,command:null,canExecute_:false,get canExecute(){return this.canExecute_},set canExecute(canExecute){this.canExecute_=!!canExecute;this.stopPropagation();this.preventDefault()}};return{Command:Command,CanExecuteEvent:CanExecuteEvent}});Polymer({is:"iron-media-query",properties:{queryMatches:{type:Boolean,value:false,readOnly:true,notify:true},query:{type:String,observer:"queryChanged"},full:{type:Boolean,value:false},_boundMQHandler:{value:function(){return this.queryHandler.bind(this)}},_mq:{value:null}},attached:function(){this.style.display="none";this.queryChanged()},detached:function(){this._remove()},_add:function(){if(this._mq){this._mq.addListener(this._boundMQHandler)}},_remove:function(){if(this._mq){this._mq.removeListener(this._boundMQHandler)}this._mq=null},queryChanged:function(){this._remove();var query=this.query;if(!query){return}if(!this.full&&query[0]!=="("){query="("+query+")"}this._mq=window.matchMedia(query);this._add();this.queryHandler(this._mq)},queryHandler:function(mq){this._setQueryMatches(mq.matches)}});Polymer.IronResizableBehavior={properties:{_parentResizable:{type:Object,observer:"_parentResizableChanged"},_notifyingDescendant:{type:Boolean,value:false}},listeners:{"iron-request-resize-notifications":"_onIronRequestResizeNotifications"},created:function(){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},notifyResize:function(){if(!this.isAttached){return}this._interestedResizables.forEach(function(resizable){if(this.resizerShouldNotify(resizable)){this._notifyDescendant(resizable)}},this);this._fireResize()},assignParentResizable:function(parentResizable){this._parentResizable=parentResizable},stopResizeNotificationsFor:function(target){var index=this._interestedResizables.indexOf(target);if(index>-1){this._interestedResizables.splice(index,1);this.unlisten(target,"iron-resize","_onDescendantIronResize")}},resizerShouldNotify:function(element){return true},_onDescendantIronResize:function(event){if(this._notifyingDescendant){event.stopPropagation();return}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){if(!this.isAttached){return}this._notifyingDescendant=true;descendant.notifyResize();this._notifyingDescendant=false}};Polymer.IronSelection=function(selectCallback){this.selection=[];this.selectCallback=selectCallback};Polymer.IronSelection.prototype={get:function(){return this.multi?this.selection.slice():this.selection[0]},clear:function(excludes){this.selection.slice().forEach(function(item){if(!excludes||excludes.indexOf(item)<0){this.setItemSelected(item,false)}},this)},isSelected:function(item){return this.selection.indexOf(item)>=0},setItemSelected:function(item,isSelected){if(item!=null){if(isSelected!==this.isSelected(item)){if(isSelected){this.selection.push(item)}else{var i=this.selection.indexOf(item);if(i>=0){this.selection.splice(i,1)}}if(this.selectCallback){this.selectCallback(item,isSelected)}}}},select:function(item){if(this.multi){this.toggle(item)}else if(this.get()!==item){this.setItemSelected(this.get(),false);this.setItemSelected(item,true)}},toggle:function(item){this.setItemSelected(item,!this.isSelected(item))}};Polymer.IronSelectableBehavior={properties:{attrForSelected:{type:String,value:null},selected:{type:String,notify:true},selectedItem:{type:Object,readOnly:true,notify:true},activateEvent:{type:String,value:"tap",observer:"_activateEventChanged"},selectable:String,selectedClass:{type:String,value:"iron-selected"},selectedAttribute:{type:String,value:null},fallbackSelection:{type:String,value:null},items:{type:Array,readOnly:true,notify:true,value:function(){return[]}},_excludedLocalNames:{type:Object,value:function(){return{template:1}}}},observers:["_updateAttrForSelected(attrForSelected)","_updateSelected(selected)","_checkFallback(fallbackSelection)"],created:function(){this._bindFilterItem=this._filterItem.bind(this);this._selection=new Polymer.IronSelection(this._applySelection.bind(this))},attached:function(){this._observer=this._observeItems(this);this._updateItems();if(!this._shouldUpdateSelection){this._updateSelected()}this._addListener(this.activateEvent)},detached:function(){if(this._observer){Polymer.dom(this).unobserveNodes(this._observer)}this._removeListener(this.activateEvent)},indexOf:function(item){return this.items.indexOf(item)},select:function(value){this.selected=value},selectPrevious:function(){var length=this.items.length;var index=(Number(this._valueToIndex(this.selected))-1+length)%length;this.selected=this._indexToValue(index)},selectNext:function(){var index=(Number(this._valueToIndex(this.selected))+1)%this.items.length;this.selected=this._indexToValue(index)},selectIndex:function(index){this.select(this._indexToValue(index))},forceSynchronousItemUpdate:function(){this._updateItems()},get _shouldUpdateSelection(){return this.selected!=null},_checkFallback:function(){if(this._shouldUpdateSelection){this._updateSelected()}},_addListener:function(eventName){this.listen(this,eventName,"_activateHandler")},_removeListener:function(eventName){this.unlisten(this,eventName,"_activateHandler")},_activateEventChanged:function(eventName,old){this._removeListener(old);this._addListener(eventName)},_updateItems:function(){var nodes=Polymer.dom(this).queryDistributedElements(this.selectable||"*");nodes=Array.prototype.filter.call(nodes,this._bindFilterItem);this._setItems(nodes)},_updateAttrForSelected:function(){if(this._shouldUpdateSelection){this.selected=this._indexToValue(this.indexOf(this.selectedItem))}},_updateSelected:function(){this._selectSelected(this.selected)},_selectSelected:function(selected){this._selection.select(this._valueToItem(this.selected));if(this.fallbackSelection&&this.items.length&&this._selection.get()===undefined){this.selected=this.fallbackSelection}},_filterItem:function(node){return!this._excludedLocalNames[node.localName]},_valueToItem:function(value){return value==null?null:this.items[this._valueToIndex(value)]},_valueToIndex:function(value){if(this.attrForSelected){for(var i=0,item;item=this.items[i];i++){if(this._valueForItem(item)==value){return i}}}else{return Number(value)}},_indexToValue:function(index){if(this.attrForSelected){var item=this.items[index];if(item){return this._valueForItem(item)}}else{return index}},_valueForItem:function(item){var propValue=item[Polymer.CaseMap.dashToCamelCase(this.attrForSelected)];return propValue!=undefined?propValue:item.getAttribute(this.attrForSelected)},_applySelection:function(item,isSelected){if(this.selectedClass){this.toggleClass(this.selectedClass,isSelected,item)}if(this.selectedAttribute){this.toggleAttribute(this.selectedAttribute,isSelected,item)}this._selectionChange();this.fire("iron-"+(isSelected?"select":"deselect"),{item:item})},_selectionChange:function(){this._setSelectedItem(this._selection.get())},_observeItems:function(node){return Polymer.dom(node).observeNodes(function(mutation){this._updateItems();if(this._shouldUpdateSelection){this._updateSelected()}this.fire("iron-items-changed",mutation,{bubbles:false,cancelable:false})})},_activateHandler:function(e){var t=e.target;var items=this.items;while(t&&t!=this){var i=items.indexOf(t);if(i>=0){var value=this._indexToValue(i);this._itemActivate(value,t);return}t=t.parentNode}},_itemActivate:function(value,item){if(!this.fire("iron-activate",{selected:value,item:item},{cancelable:true}).defaultPrevented){this.select(value)}}};Polymer({is:"iron-pages",behaviors:[Polymer.IronResizableBehavior,Polymer.IronSelectableBehavior],properties:{activateEvent:{type:String,value:null}},observers:["_selectedPageChanged(selected)"],_selectedPageChanged:function(selected,old){this.async(this.notifyResize)}});Polymer.IronScrollTargetBehavior={properties:{scrollTarget:{type:HTMLElement,value:function(){return this._defaultScrollTarget}}},observers:["_scrollTargetChanged(scrollTarget, isAttached)"],_scrollTargetChanged:function(scrollTarget,isAttached){var eventTarget;if(this._oldScrollTarget){eventTarget=this._oldScrollTarget===this._doc?window:this._oldScrollTarget;eventTarget.removeEventListener("scroll",this._boundScrollHandler);this._oldScrollTarget=null}if(!isAttached){return}if(scrollTarget==="document"){this.scrollTarget=this._doc}else if(typeof scrollTarget==="string"){this.scrollTarget=this.domHost?this.domHost.$[scrollTarget]:Polymer.dom(this.ownerDocument).querySelector("#"+scrollTarget)}else if(this._isValidScrollTarget()){eventTarget=scrollTarget===this._doc?window:scrollTarget;this._boundScrollHandler=this._boundScrollHandler||this._scrollHandler.bind(this);this._oldScrollTarget=scrollTarget;eventTarget.addEventListener("scroll",this._boundScrollHandler)}},_scrollHandler:function scrollHandler(){},get _defaultScrollTarget(){return this._doc},get _doc(){return this.ownerDocument.documentElement},get _scrollTop(){if(this._isValidScrollTarget()){return this.scrollTarget===this._doc?window.pageYOffset:this.scrollTarget.scrollTop}return 0},get _scrollLeft(){if(this._isValidScrollTarget()){return this.scrollTarget===this._doc?window.pageXOffset:this.scrollTarget.scrollLeft}return 0},set _scrollTop(top){if(this.scrollTarget===this._doc){window.scrollTo(window.pageXOffset,top)}else if(this._isValidScrollTarget()){this.scrollTarget.scrollTop=top}},set _scrollLeft(left){if(this.scrollTarget===this._doc){window.scrollTo(left,window.pageYOffset)}else if(this._isValidScrollTarget()){this.scrollTarget.scrollLeft=left}},scroll:function(left,top){if(this.scrollTarget===this._doc){window.scrollTo(left,top)}else if(this._isValidScrollTarget()){this.scrollTarget.scrollLeft=left;this.scrollTarget.scrollTop=top}},get _scrollTargetWidth(){if(this._isValidScrollTarget()){return this.scrollTarget===this._doc?window.innerWidth:this.scrollTarget.offsetWidth}return 0},get _scrollTargetHeight(){if(this._isValidScrollTarget()){return this.scrollTarget===this._doc?window.innerHeight:this.scrollTarget.offsetHeight}return 0},_isValidScrollTarget:function(){return this.scrollTarget instanceof HTMLElement}};(function(){var metaDatas={};var metaArrays={};var singleton=null;Polymer.IronMeta=Polymer({is:"iron-meta",properties:{type:{type:String,value:"default",observer:"_typeChanged"},key:{type:String,observer:"_keyChanged"},value:{type:Object,notify:true,observer:"_valueChanged"},self:{type:Boolean,observer:"_selfChanged"},list:{type:Array,notify:true}},hostAttributes:{hidden:true},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(){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)},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)}}}});Polymer.IronMeta.getIronMeta=function getIronMeta(){if(singleton===null){singleton=new Polymer.IronMeta}return singleton};Polymer.IronMetaQuery=Polymer({is:"iron-meta-query",properties:{type:{type:String,value:"default",observer:"_typeChanged"},key:{type:String,observer:"_keyChanged"},value:{type:Object,notify:true,readOnly:true},list:{type:Array,notify:true}},factoryImpl:function(config){if(config){for(var n in config){switch(n){case"type":case"key":this[n]=config[n];break}}}},created:function(){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)}},byKey:function(key){return this._metaData&&this._metaData[key]}})})();Polymer({is:"iron-icon",properties:{icon:{type:String,observer:"_iconChanged"},theme:{type:String,observer:"_updateIcon"},src:{type:String,observer:"_srcChanged"},_meta:{value:Polymer.Base.create("iron-meta",{type:"iconset"}),observer:"_updateIcon"}},_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},_updateIcon:function(){if(this._usesIconset()){if(this._img&&this._img.parentNode){Polymer.dom(this.root).removeChild(this._img)}if(this._iconName===""){if(this._iconset){this._iconset.removeIcon(this)}}else if(this._iconsetName&&this._meta){this._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._iconset){this._iconset.removeIcon(this)}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)}}});(function(){"use strict";var KEY_IDENTIFIER={"U+0008":"backspace","U+0009":"tab","U+001B":"esc","U+0020":"space","U+007F":"del"};var KEY_CODE={8:"backspace",9:"tab",13:"enter",27:"esc",33:"pageup",34:"pagedown",35:"end",36:"home",32:"space",37:"left",38:"up",39:"right",40:"down",46:"del",106:"*"};var MODIFIER_KEYS={shift:"shiftKey",ctrl:"ctrlKey",alt:"altKey",meta:"metaKey"};var KEY_CHAR=/[a-z0-9*]/;var IDENT_CHAR=/U\+/;var ARROW_KEY=/^arrow/;var SPACE_KEY=/^space(bar)?/;var ESC_KEY=/^escape$/;function transformKey(key,noSpecialChars){var validKey="";if(key){var lKey=key.toLowerCase();if(lKey===" "||SPACE_KEY.test(lKey)){validKey="space"}else if(ESC_KEY.test(lKey)){validKey="esc"}else if(lKey.length==1){if(!noSpecialChars||KEY_CHAR.test(lKey)){validKey=lKey}}else if(ARROW_KEY.test(lKey)){validKey=lKey.replace("arrow","")}else if(lKey=="multiply"){validKey="*"}else{validKey=lKey}}return validKey}function transformKeyIdentifier(keyIdent){var validKey="";if(keyIdent){if(keyIdent in KEY_IDENTIFIER){validKey=KEY_IDENTIFIER[keyIdent]}else if(IDENT_CHAR.test(keyIdent)){keyIdent=parseInt(keyIdent.replace("U+","0x"),16);validKey=String.fromCharCode(keyIdent).toLowerCase()}else{validKey=keyIdent.toLowerCase()}}return validKey}function transformKeyCode(keyCode){var validKey="";if(Number(keyCode)){if(keyCode>=65&&keyCode<=90){validKey=String.fromCharCode(32+keyCode)}else if(keyCode>=112&&keyCode<=123){validKey="f"+(keyCode-112)}else if(keyCode>=48&&keyCode<=57){validKey=String(keyCode-48)}else if(keyCode>=96&&keyCode<=105){validKey=String(keyCode-96)}else{validKey=KEY_CODE[keyCode]}}return validKey}function normalizedKeyForEvent(keyEvent,noSpecialChars){if(keyEvent.key){return transformKey(keyEvent.key,noSpecialChars)}if(keyEvent.detail&&keyEvent.detail.key){return transformKey(keyEvent.detail.key,noSpecialChars)}return transformKeyIdentifier(keyEvent.keyIdentifier)||transformKeyCode(keyEvent.keyCode)||""}function keyComboMatchesEvent(keyCombo,event){var keyEvent=normalizedKeyForEvent(event,keyCombo.hasModifiers);return keyEvent===keyCombo.key&&(!keyCombo.hasModifiers||!!event.shiftKey===!!keyCombo.shiftKey&&!!event.ctrlKey===!!keyCombo.ctrlKey&&!!event.altKey===!!keyCombo.altKey&&!!event.metaKey===!!keyCombo.metaKey)}function parseKeyComboString(keyComboString){if(keyComboString.length===1){return{combo:keyComboString,key:keyComboString,event:"keydown"}}return keyComboString.split("+").reduce(function(parsedKeyCombo,keyComboPart){var eventParts=keyComboPart.split(":");var keyName=eventParts[0];var event=eventParts[1];if(keyName in MODIFIER_KEYS){parsedKeyCombo[MODIFIER_KEYS[keyName]]=true;parsedKeyCombo.hasModifiers=true}else{parsedKeyCombo.key=keyName;parsedKeyCombo.event=event||"keydown"}return parsedKeyCombo},{combo:keyComboString.split(":").shift()})}function parseEventString(eventString){return eventString.trim().split(" ").map(function(keyComboString){return parseKeyComboString(keyComboString)})}Polymer.IronA11yKeysBehavior={properties:{keyEventTarget:{type:Object,value:function(){return this}},stopKeyboardEventPropagation:{type:Boolean,value:false},_boundKeyHandlers:{type:Array,value:function(){return[]}},_imperativeKeyBindings:{type:Object,value:function(){return{}}}},observers:["_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)"],keyBindings:{},registered:function(){this._prepKeyBindings()},attached:function(){this._listenKeyEventListeners()},detached:function(){this._unlistenKeyEventListeners()},addOwnKeyBinding:function(eventString,handlerName){this._imperativeKeyBindings[eventString]=handlerName;this._prepKeyBindings();this._resetKeyEventListeners()},removeOwnKeyBindings:function(){this._imperativeKeyBindings={};this._prepKeyBindings();this._resetKeyEventListeners()},keyboardEventMatchesKeys:function(event,eventString){var keyCombos=parseEventString(eventString);for(var i=0;i<keyCombos.length;++i){if(keyComboMatchesEvent(keyCombos[i],event)){return true}}return false},_collectKeyBindings:function(){var keyBindings=this.behaviors.map(function(behavior){return behavior.keyBindings});if(keyBindings.indexOf(this.keyBindings)===-1){keyBindings.push(this.keyBindings)}return keyBindings},_prepKeyBindings:function(){this._keyBindings={};this._collectKeyBindings().forEach(function(keyBindings){for(var eventString in keyBindings){this._addKeyBinding(eventString,keyBindings[eventString])}},this);for(var eventString in this._imperativeKeyBindings){this._addKeyBinding(eventString,this._imperativeKeyBindings[eventString])}for(var eventName in this._keyBindings){this._keyBindings[eventName].sort(function(kb1,kb2){var b1=kb1[0].hasModifiers;var b2=kb2[0].hasModifiers;return b1===b2?0:b1?-1:1})}},_addKeyBinding:function(eventString,handlerName){parseEventString(eventString).forEach(function(keyCombo){this._keyBindings[keyCombo.event]=this._keyBindings[keyCombo.event]||[];this._keyBindings[keyCombo.event].push([keyCombo,handlerName])},this)},_resetKeyEventListeners:function(){this._unlistenKeyEventListeners();if(this.isAttached){this._listenKeyEventListeners()}},_listenKeyEventListeners:function(){if(!this.keyEventTarget){return}Object.keys(this._keyBindings).forEach(function(eventName){var keyBindings=this._keyBindings[eventName];var boundKeyHandler=this._onKeyBindingEvent.bind(this,keyBindings);this._boundKeyHandlers.push([this.keyEventTarget,eventName,boundKeyHandler]);this.keyEventTarget.addEventListener(eventName,boundKeyHandler)},this)},_unlistenKeyEventListeners:function(){var keyHandlerTuple;var keyEventTarget;var eventName;var boundKeyHandler;while(this._boundKeyHandlers.length){keyHandlerTuple=this._boundKeyHandlers.pop();keyEventTarget=keyHandlerTuple[0];eventName=keyHandlerTuple[1];boundKeyHandler=keyHandlerTuple[2];keyEventTarget.removeEventListener(eventName,boundKeyHandler)}},_onKeyBindingEvent:function(keyBindings,event){if(this.stopKeyboardEventPropagation){event.stopPropagation()}if(event.defaultPrevented){return}for(var i=0;i<keyBindings.length;i++){var keyCombo=keyBindings[i][0];var handlerName=keyBindings[i][1];if(keyComboMatchesEvent(keyCombo,event)){this._triggerKeyHandler(keyCombo,handlerName,event);if(event.defaultPrevented){return}}}},_triggerKeyHandler:function(keyCombo,handlerName,keyboardEvent){var detail=Object.create(keyCombo);detail.keyboardEvent=keyboardEvent;var event=new CustomEvent(keyCombo.event,{detail:detail,cancelable:true});this[handlerName].call(this,event);if(event.defaultPrevented){keyboardEvent.preventDefault()}}}})();Polymer.IronControlState={properties:{focused:{type:Boolean,value:false,notify:true,readOnly:true,reflectToAttribute:true},disabled:{type:Boolean,value:false,notify:true,observer:"_disabledChanged",reflectToAttribute:true},_oldTabIndex:{type:Number},_boundFocusBlurHandler:{type:Function,value:function(){return this._focusBlurHandler.bind(this)}}},observers:["_changedControlState(focused, disabled)"],ready:function(){this.addEventListener("focus",this._boundFocusBlurHandler,true);this.addEventListener("blur",this._boundFocusBlurHandler,true)},_focusBlurHandler:function(event){if(event.target===this){this._setFocused(event.type==="focus")}else if(!this.shadowRoot){var target=Polymer.dom(event).localTarget;if(!this.isLightDescendant(target)){this.fire(event.type,{sourceEvent:event},{node:this,bubbles:event.bubbles,cancelable:event.cancelable})}}},_disabledChanged:function(disabled,old){this.setAttribute("aria-disabled",disabled?"true":"false");this.style.pointerEvents=disabled?"none":"";if(disabled){this._oldTabIndex=this.tabIndex;this._setFocused(false);this.tabIndex=-1;this.blur()}else if(this._oldTabIndex!==undefined){this.tabIndex=this._oldTabIndex}},_changedControlState:function(){if(this._controlStateChanged){this._controlStateChanged()}}};Polymer.IronButtonStateImpl={properties:{pressed:{type:Boolean,readOnly:true,value:false,reflectToAttribute:true,observer:"_pressedChanged"},toggles:{type:Boolean,value:false,reflectToAttribute:true},active:{type:Boolean,value:false,notify:true,reflectToAttribute:true},pointerDown:{type:Boolean,readOnly:true,value:false},receivedFocusFromKeyboard:{type:Boolean,readOnly:true},ariaActiveAttribute:{type:String,value:"aria-pressed",observer:"_ariaActiveAttributeChanged"}},listeners:{down:"_downHandler",up:"_upHandler",tap:"_tapHandler"},observers:["_detectKeyboardFocus(focused)","_activeChanged(active, ariaActiveAttribute)"],keyBindings:{"enter:keydown":"_asyncClick","space:keydown":"_spaceKeyDownHandler","space:keyup":"_spaceKeyUpHandler"},_mouseEventRe:/^mouse/,_tapHandler:function(){if(this.toggles){this._userActivate(!this.active)}else{this.active=false}},_detectKeyboardFocus:function(focused){this._setReceivedFocusFromKeyboard(!this.pointerDown&&focused)},_userActivate:function(active){if(this.active!==active){this.active=active;this.fire("change")}},_downHandler:function(event){this._setPointerDown(true);this._setPressed(true);this._setReceivedFocusFromKeyboard(false)},_upHandler:function(){this._setPointerDown(false);this._setPressed(false)},_spaceKeyDownHandler:function(event){var keyboardEvent=event.detail.keyboardEvent;var target=Polymer.dom(keyboardEvent).localTarget;if(this.isLightDescendant(target))return;keyboardEvent.preventDefault();keyboardEvent.stopImmediatePropagation();this._setPressed(true)},_spaceKeyUpHandler:function(event){var keyboardEvent=event.detail.keyboardEvent;var target=Polymer.dom(keyboardEvent).localTarget;if(this.isLightDescendant(target))return;if(this.pressed){this._asyncClick()}this._setPressed(false)},_asyncClick:function(){this.async(function(){this.click()},1)},_pressedChanged:function(pressed){this._changedButtonState()},_ariaActiveAttributeChanged:function(value,oldValue){if(oldValue&&oldValue!=value&&this.hasAttribute(oldValue)){this.removeAttribute(oldValue)}},_activeChanged:function(active,ariaActiveAttribute){if(this.toggles){this.setAttribute(this.ariaActiveAttribute,active?"true":"false")}else{this.removeAttribute(this.ariaActiveAttribute)}this._changedButtonState()},_controlStateChanged:function(){if(this.disabled){this._setPressed(false)}else{this._changedButtonState()}},_changedButtonState:function(){if(this._buttonStateChanged){this._buttonStateChanged()}}};Polymer.IronButtonState=[Polymer.IronA11yKeysBehavior,Polymer.IronButtonStateImpl];(function(){var Utility={distance:function(x1,y1,x2,y2){var xDelta=x1-x2;var yDelta=y1-y2;return Math.sqrt(xDelta*xDelta+yDelta*yDelta)},now:window.performance&&window.performance.now?window.performance.now.bind(window.performance):Date.now};function ElementMetrics(element){this.element=element;this.width=this.boundingRect.width;this.height=this.boundingRect.height;this.size=Math.max(this.width,this.height)}ElementMetrics.prototype={get boundingRect(){return this.element.getBoundingClientRect()},furthestCornerDistanceFrom:function(x,y){var topLeft=Utility.distance(x,y,0,0);var topRight=Utility.distance(x,y,this.width,0);var bottomLeft=Utility.distance(x,y,0,this.height);var bottomRight=Utility.distance(x,y,this.width,this.height);return Math.max(topLeft,topRight,bottomLeft,bottomRight)}};function Ripple(element){this.element=element;this.color=window.getComputedStyle(element).color;this.wave=document.createElement("div");this.waveContainer=document.createElement("div");this.wave.style.backgroundColor=this.color;this.wave.classList.add("wave");this.waveContainer.classList.add("wave-container");Polymer.dom(this.waveContainer).appendChild(this.wave);this.resetInteractionState()}Ripple.MAX_RADIUS=300;Ripple.prototype={get recenters(){return this.element.recenters},get center(){return this.element.center},get mouseDownElapsed(){var elapsed;if(!this.mouseDownStart){return 0}elapsed=Utility.now()-this.mouseDownStart;if(this.mouseUpStart){elapsed-=this.mouseUpElapsed}return elapsed},get mouseUpElapsed(){return this.mouseUpStart?Utility.now()-this.mouseUpStart:0},get mouseDownElapsedSeconds(){return this.mouseDownElapsed/1e3},get mouseUpElapsedSeconds(){return this.mouseUpElapsed/1e3},get mouseInteractionSeconds(){return this.mouseDownElapsedSeconds+this.mouseUpElapsedSeconds},get initialOpacity(){return this.element.initialOpacity},get opacityDecayVelocity(){return this.element.opacityDecayVelocity},get radius(){var width2=this.containerMetrics.width*this.containerMetrics.width;var height2=this.containerMetrics.height*this.containerMetrics.height;var waveRadius=Math.min(Math.sqrt(width2+height2),Ripple.MAX_RADIUS)*1.1+5;var duration=1.1-.2*(waveRadius/Ripple.MAX_RADIUS);var timeNow=this.mouseInteractionSeconds/duration;var size=waveRadius*(1-Math.pow(80,-timeNow));return Math.abs(size)},get opacity(){if(!this.mouseUpStart){return this.initialOpacity}return Math.max(0,this.initialOpacity-this.mouseUpElapsedSeconds*this.opacityDecayVelocity)},get outerOpacity(){var outerOpacity=this.mouseUpElapsedSeconds*.3;var waveOpacity=this.opacity;return Math.max(0,Math.min(outerOpacity,waveOpacity))},get isOpacityFullyDecayed(){return this.opacity<.01&&this.radius>=Math.min(this.maxRadius,Ripple.MAX_RADIUS)},get isRestingAtMaxRadius(){return this.opacity>=this.initialOpacity&&this.radius>=Math.min(this.maxRadius,Ripple.MAX_RADIUS)},get isAnimationComplete(){return this.mouseUpStart?this.isOpacityFullyDecayed:this.isRestingAtMaxRadius},get translationFraction(){return Math.min(1,this.radius/this.containerMetrics.size*2/Math.sqrt(2))},get xNow(){if(this.xEnd){return this.xStart+this.translationFraction*(this.xEnd-this.xStart)}return this.xStart},get yNow(){if(this.yEnd){return this.yStart+this.translationFraction*(this.yEnd-this.yStart)}return this.yStart},get isMouseDown(){return this.mouseDownStart&&!this.mouseUpStart},resetInteractionState:function(){this.maxRadius=0;this.mouseDownStart=0;this.mouseUpStart=0;this.xStart=0;this.yStart=0;this.xEnd=0;this.yEnd=0;this.slideDistance=0;this.containerMetrics=new ElementMetrics(this.element)},draw:function(){var scale;var translateString;var dx;var dy;this.wave.style.opacity=this.opacity;scale=this.radius/(this.containerMetrics.size/2);dx=this.xNow-this.containerMetrics.width/2; - default: - if (ident) throw Error('Invalid shortcut'); - ident = part; - } - }); - this.ident_ = ident; - this.mods_ = mods; - } - KeyboardShortcut.prototype = { - matchesEvent: function(e) { - if (e.key == this.ident_) { - var mods = this.mods_; - return [ 'altKey', 'ctrlKey', 'metaKey', 'shiftKey' ].every(function(k) { - return e[k] == !!mods[k]; - }); - } - return false; - } - }; - var Command = cr.ui.define('command'); - Command.prototype = { - __proto__: HTMLElement.prototype, - decorate: function() { - CommandManager.init(assert(this.ownerDocument)); - if (this.hasAttribute('shortcut')) this.shortcut = this.getAttribute('shortcut'); - }, - execute: function(opt_element) { - if (this.disabled) return; - var doc = this.ownerDocument; - if (doc.activeElement) { - var e = new Event('command', { - bubbles: true - }); - e.command = this; - (opt_element || doc.activeElement).dispatchEvent(e); - } - }, - canExecuteChange: function(opt_node) { - dispatchCanExecuteEvent(this, opt_node || this.ownerDocument.activeElement); - }, - shortcut_: '', - get shortcut() { - return this.shortcut_; - }, - set shortcut(shortcut) { - var oldShortcut = this.shortcut_; - if (shortcut !== oldShortcut) { - this.keyboardShortcuts_ = shortcut.split(/\s+/).map(function(shortcut) { - return new KeyboardShortcut(shortcut); - }); - this.shortcut_ = shortcut; - cr.dispatchPropertyChange(this, 'shortcut', this.shortcut_, oldShortcut); - } - }, - matchesEvent: function(e) { - if (!this.keyboardShortcuts_) return false; - return this.keyboardShortcuts_.some(function(keyboardShortcut) { - return keyboardShortcut.matchesEvent(e); - }); - } - }; - cr.defineProperty(Command, 'label', cr.PropertyKind.ATTR); - cr.defineProperty(Command, 'disabled', cr.PropertyKind.BOOL_ATTR); - cr.defineProperty(Command, 'hidden', cr.PropertyKind.BOOL_ATTR); - cr.defineProperty(Command, 'checked', cr.PropertyKind.BOOL_ATTR); - cr.defineProperty(Command, 'hideShortcutText', cr.PropertyKind.BOOL_ATTR); - function dispatchCanExecuteEvent(command, target) { - var e = new CanExecuteEvent(command); - target.dispatchEvent(e); - command.disabled = !e.canExecute; - } - var commandManagers = {}; - function CommandManager(doc) { - doc.addEventListener('focus', this.handleFocus_.bind(this), true); - doc.addEventListener('keydown', this.handleKeyDown_.bind(this), false); - } - CommandManager.init = function(doc) { - var uid = cr.getUid(doc); - if (!(uid in commandManagers)) { - commandManagers[uid] = new CommandManager(doc); - } - }; - CommandManager.prototype = { - handleFocus_: function(e) { - var target = e.target; - if (target.menu || target.command) return; - var commands = Array.prototype.slice.call(target.ownerDocument.querySelectorAll('command')); - commands.forEach(function(command) { - dispatchCanExecuteEvent(command, target); - }); - }, - handleKeyDown_: function(e) { - var target = e.target; - var commands = Array.prototype.slice.call(target.ownerDocument.querySelectorAll('command')); - for (var i = 0, command; command = commands[i]; i++) { - if (command.matchesEvent(e)) { - command.canExecuteChange(); - if (!command.disabled) { - e.preventDefault(); - e.stopPropagation(); - command.execute(); - return; - } - } - } - } - }; - function CanExecuteEvent(command) { - var e = new Event('canExecute', { - bubbles: true, - cancelable: true - }); - e.__proto__ = CanExecuteEvent.prototype; - e.command = command; - return e; - } - CanExecuteEvent.prototype = { - __proto__: Event.prototype, - command: null, - canExecute_: false, - get canExecute() { - return this.canExecute_; - }, - set canExecute(canExecute) { - this.canExecute_ = !!canExecute; - this.stopPropagation(); - this.preventDefault(); - } - }; - return { - Command: Command, - CanExecuteEvent: CanExecuteEvent - }; -}); - -Polymer({ - is: 'iron-media-query', - properties: { - queryMatches: { - type: Boolean, - value: false, - readOnly: true, - notify: true - }, - query: { - type: String, - observer: 'queryChanged' - }, - full: { - type: Boolean, - value: false - }, - _boundMQHandler: { - value: function() { - return this.queryHandler.bind(this); - } - }, - _mq: { - value: null - } - }, - attached: function() { - this.style.display = 'none'; - this.queryChanged(); - }, - detached: function() { - this._remove(); - }, - _add: function() { - if (this._mq) { - this._mq.addListener(this._boundMQHandler); - } - }, - _remove: function() { - if (this._mq) { - this._mq.removeListener(this._boundMQHandler); - } - this._mq = null; - }, - queryChanged: function() { - this._remove(); - var query = this.query; - if (!query) { - return; - } - if (!this.full && query[0] !== '(') { - query = '(' + query + ')'; - } - this._mq = window.matchMedia(query); - this._add(); - this.queryHandler(this._mq); - }, - queryHandler: function(mq) { - this._setQueryMatches(mq.matches); - } -}); - -Polymer.IronResizableBehavior = { - properties: { - _parentResizable: { - type: Object, - observer: '_parentResizableChanged' - }, - _notifyingDescendant: { - type: Boolean, - value: false - } - }, - listeners: { - 'iron-request-resize-notifications': '_onIronRequestResizeNotifications' - }, - created: function() { - 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; - }, - notifyResize: function() { - if (!this.isAttached) { - return; - } - this._interestedResizables.forEach(function(resizable) { - if (this.resizerShouldNotify(resizable)) { - this._notifyDescendant(resizable); - } - }, this); - this._fireResize(); - }, - assignParentResizable: function(parentResizable) { - this._parentResizable = parentResizable; - }, - stopResizeNotificationsFor: function(target) { - var index = this._interestedResizables.indexOf(target); - if (index > -1) { - this._interestedResizables.splice(index, 1); - this.unlisten(target, 'iron-resize', '_onDescendantIronResize'); - } - }, - resizerShouldNotify: function(element) { - return true; - }, - _onDescendantIronResize: function(event) { - if (this._notifyingDescendant) { - event.stopPropagation(); - return; - } - 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) { - if (!this.isAttached) { - return; - } - this._notifyingDescendant = true; - descendant.notifyResize(); - this._notifyingDescendant = false; - } -}; - -Polymer.IronSelection = function(selectCallback) { - this.selection = []; - this.selectCallback = selectCallback; -}; - -Polymer.IronSelection.prototype = { - get: function() { - return this.multi ? this.selection.slice() : this.selection[0]; - }, - clear: function(excludes) { - this.selection.slice().forEach(function(item) { - if (!excludes || excludes.indexOf(item) < 0) { - this.setItemSelected(item, false); - } - }, this); - }, - isSelected: function(item) { - return this.selection.indexOf(item) >= 0; - }, - setItemSelected: function(item, isSelected) { - if (item != null) { - if (isSelected !== this.isSelected(item)) { - if (isSelected) { - this.selection.push(item); - } else { - var i = this.selection.indexOf(item); - if (i >= 0) { - this.selection.splice(i, 1); - } - } - if (this.selectCallback) { - this.selectCallback(item, isSelected); - } - } - } - }, - select: function(item) { - if (this.multi) { - this.toggle(item); - } else if (this.get() !== item) { - this.setItemSelected(this.get(), false); - this.setItemSelected(item, true); - } - }, - toggle: function(item) { - this.setItemSelected(item, !this.isSelected(item)); - } -}; - -Polymer.IronSelectableBehavior = { - properties: { - attrForSelected: { - type: String, - value: null - }, - selected: { - type: String, - notify: true - }, - selectedItem: { - type: Object, - readOnly: true, - notify: true - }, - activateEvent: { - type: String, - value: 'tap', - observer: '_activateEventChanged' - }, - selectable: String, - selectedClass: { - type: String, - value: 'iron-selected' - }, - selectedAttribute: { - type: String, - value: null - }, - fallbackSelection: { - type: String, - value: null - }, - items: { - type: Array, - readOnly: true, - notify: true, - value: function() { - return []; - } - }, - _excludedLocalNames: { - type: Object, - value: function() { - return { - template: 1 - }; - } - } - }, - observers: [ '_updateAttrForSelected(attrForSelected)', '_updateSelected(selected)', '_checkFallback(fallbackSelection)' ], - created: function() { - this._bindFilterItem = this._filterItem.bind(this); - this._selection = new Polymer.IronSelection(this._applySelection.bind(this)); - }, - attached: function() { - this._observer = this._observeItems(this); - this._updateItems(); - if (!this._shouldUpdateSelection) { - this._updateSelected(); - } - this._addListener(this.activateEvent); - }, - detached: function() { - if (this._observer) { - Polymer.dom(this).unobserveNodes(this._observer); - } - this._removeListener(this.activateEvent); - }, - indexOf: function(item) { - return this.items.indexOf(item); - }, - select: function(value) { - this.selected = value; - }, - selectPrevious: function() { - var length = this.items.length; - var index = (Number(this._valueToIndex(this.selected)) - 1 + length) % length; - this.selected = this._indexToValue(index); - }, - selectNext: function() { - var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.length; - this.selected = this._indexToValue(index); - }, - selectIndex: function(index) { - this.select(this._indexToValue(index)); - }, - forceSynchronousItemUpdate: function() { - this._updateItems(); - }, - get _shouldUpdateSelection() { - return this.selected != null; - }, - _checkFallback: function() { - if (this._shouldUpdateSelection) { - this._updateSelected(); - } - }, - _addListener: function(eventName) { - this.listen(this, eventName, '_activateHandler'); - }, - _removeListener: function(eventName) { - this.unlisten(this, eventName, '_activateHandler'); - }, - _activateEventChanged: function(eventName, old) { - this._removeListener(old); - this._addListener(eventName); - }, - _updateItems: function() { - var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '*'); - nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); - this._setItems(nodes); - }, - _updateAttrForSelected: function() { - if (this._shouldUpdateSelection) { - this.selected = this._indexToValue(this.indexOf(this.selectedItem)); - } - }, - _updateSelected: function() { - this._selectSelected(this.selected); - }, - _selectSelected: function(selected) { - this._selection.select(this._valueToItem(this.selected)); - if (this.fallbackSelection && this.items.length && this._selection.get() === undefined) { - this.selected = this.fallbackSelection; - } - }, - _filterItem: function(node) { - return !this._excludedLocalNames[node.localName]; - }, - _valueToItem: function(value) { - return value == null ? null : this.items[this._valueToIndex(value)]; - }, - _valueToIndex: function(value) { - if (this.attrForSelected) { - for (var i = 0, item; item = this.items[i]; i++) { - if (this._valueForItem(item) == value) { - return i; - } - } - } else { - return Number(value); - } - }, - _indexToValue: function(index) { - if (this.attrForSelected) { - var item = this.items[index]; - if (item) { - return this._valueForItem(item); - } - } else { - return index; - } - }, - _valueForItem: function(item) { - var propValue = item[Polymer.CaseMap.dashToCamelCase(this.attrForSelected)]; - return propValue != undefined ? propValue : item.getAttribute(this.attrForSelected); - }, - _applySelection: function(item, isSelected) { - if (this.selectedClass) { - this.toggleClass(this.selectedClass, isSelected, item); - } - if (this.selectedAttribute) { - this.toggleAttribute(this.selectedAttribute, isSelected, item); - } - this._selectionChange(); - this.fire('iron-' + (isSelected ? 'select' : 'deselect'), { - item: item - }); - }, - _selectionChange: function() { - this._setSelectedItem(this._selection.get()); - }, - _observeItems: function(node) { - return Polymer.dom(node).observeNodes(function(mutation) { - this._updateItems(); - if (this._shouldUpdateSelection) { - this._updateSelected(); - } - this.fire('iron-items-changed', mutation, { - bubbles: false, - cancelable: false - }); - }); - }, - _activateHandler: function(e) { - var t = e.target; - var items = this.items; - while (t && t != this) { - var i = items.indexOf(t); - if (i >= 0) { - var value = this._indexToValue(i); - this._itemActivate(value, t); - return; - } - t = t.parentNode; - } - }, - _itemActivate: function(value, item) { - if (!this.fire('iron-activate', { - selected: value, - item: item - }, { - cancelable: true - }).defaultPrevented) { - this.select(value); - } - } -}; - -Polymer({ - is: 'iron-pages', - behaviors: [ Polymer.IronResizableBehavior, Polymer.IronSelectableBehavior ], - properties: { - activateEvent: { - type: String, - value: null - } - }, - observers: [ '_selectedPageChanged(selected)' ], - _selectedPageChanged: function(selected, old) { - this.async(this.notifyResize); - } -}); - -Polymer.IronScrollTargetBehavior = { - properties: { - scrollTarget: { - type: HTMLElement, - value: function() { - return this._defaultScrollTarget; - } - } - }, - observers: [ '_scrollTargetChanged(scrollTarget, isAttached)' ], - _scrollTargetChanged: function(scrollTarget, isAttached) { - var eventTarget; - if (this._oldScrollTarget) { - eventTarget = this._oldScrollTarget === this._doc ? window : this._oldScrollTarget; - eventTarget.removeEventListener('scroll', this._boundScrollHandler); - this._oldScrollTarget = null; - } - if (!isAttached) { - return; - } - if (scrollTarget === 'document') { - this.scrollTarget = this._doc; - } else if (typeof scrollTarget === 'string') { - this.scrollTarget = this.domHost ? this.domHost.$[scrollTarget] : Polymer.dom(this.ownerDocument).querySelector('#' + scrollTarget); - } else if (this._isValidScrollTarget()) { - eventTarget = scrollTarget === this._doc ? window : scrollTarget; - this._boundScrollHandler = this._boundScrollHandler || this._scrollHandler.bind(this); - this._oldScrollTarget = scrollTarget; - eventTarget.addEventListener('scroll', this._boundScrollHandler); - } - }, - _scrollHandler: function scrollHandler() {}, - get _defaultScrollTarget() { - return this._doc; - }, - get _doc() { - return this.ownerDocument.documentElement; - }, - get _scrollTop() { - if (this._isValidScrollTarget()) { - return this.scrollTarget === this._doc ? window.pageYOffset : this.scrollTarget.scrollTop; - } - return 0; - }, - get _scrollLeft() { - if (this._isValidScrollTarget()) { - return this.scrollTarget === this._doc ? window.pageXOffset : this.scrollTarget.scrollLeft; - } - return 0; - }, - set _scrollTop(top) { - if (this.scrollTarget === this._doc) { - window.scrollTo(window.pageXOffset, top); - } else if (this._isValidScrollTarget()) { - this.scrollTarget.scrollTop = top; - } - }, - set _scrollLeft(left) { - if (this.scrollTarget === this._doc) { - window.scrollTo(left, window.pageYOffset); - } else if (this._isValidScrollTarget()) { - this.scrollTarget.scrollLeft = left; - } - }, - scroll: function(left, top) { - if (this.scrollTarget === this._doc) { - window.scrollTo(left, top); - } else if (this._isValidScrollTarget()) { - this.scrollTarget.scrollLeft = left; - this.scrollTarget.scrollTop = top; - } - }, - get _scrollTargetWidth() { - if (this._isValidScrollTarget()) { - return this.scrollTarget === this._doc ? window.innerWidth : this.scrollTarget.offsetWidth; - } - return 0; - }, - get _scrollTargetHeight() { - if (this._isValidScrollTarget()) { - return this.scrollTarget === this._doc ? window.innerHeight : this.scrollTarget.offsetHeight; - } - return 0; - }, - _isValidScrollTarget: function() { - return this.scrollTarget instanceof HTMLElement; - } -}; - -(function() { - var metaDatas = {}; - var metaArrays = {}; - var singleton = null; - Polymer.IronMeta = Polymer({ - is: 'iron-meta', - properties: { - type: { - type: String, - value: 'default', - observer: '_typeChanged' - }, - key: { - type: String, - observer: '_keyChanged' - }, - value: { - type: Object, - notify: true, - observer: '_valueChanged' - }, - self: { - type: Boolean, - observer: '_selfChanged' - }, - list: { - type: Array, - notify: true - } - }, - hostAttributes: { - hidden: true - }, - 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() { - 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); - }, - 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); - } - } - } - }); - Polymer.IronMeta.getIronMeta = function getIronMeta() { - if (singleton === null) { - singleton = new Polymer.IronMeta(); - } - return singleton; - }; - Polymer.IronMetaQuery = Polymer({ - is: 'iron-meta-query', - properties: { - type: { - type: String, - value: 'default', - observer: '_typeChanged' - }, - key: { - type: String, - observer: '_keyChanged' - }, - value: { - type: Object, - notify: true, - readOnly: true - }, - list: { - type: Array, - notify: true - } - }, - factoryImpl: function(config) { - if (config) { - for (var n in config) { - switch (n) { - case 'type': - case 'key': - this[n] = config[n]; - break; - } - } - } - }, - created: function() { - 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); - } - }, - byKey: function(key) { - return this._metaData && this._metaData[key]; - } - }); -})(); - -Polymer({ - is: 'iron-icon', - properties: { - icon: { - type: String, - observer: '_iconChanged' - }, - theme: { - type: String, - observer: '_updateIcon' - }, - src: { - type: String, - observer: '_srcChanged' - }, - _meta: { - value: Polymer.Base.create('iron-meta', { - type: 'iconset' - }), - observer: '_updateIcon' - } - }, - _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; - }, - _updateIcon: function() { - if (this._usesIconset()) { - if (this._img && this._img.parentNode) { - Polymer.dom(this.root).removeChild(this._img); - } - if (this._iconName === "") { - if (this._iconset) { - this._iconset.removeIcon(this); - } - } else if (this._iconsetName && this._meta) { - this._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._iconset) { - this._iconset.removeIcon(this); - } - 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); - } - } -}); - -(function() { - 'use strict'; - var KEY_IDENTIFIER = { - 'U+0008': 'backspace', - 'U+0009': 'tab', - 'U+001B': 'esc', - 'U+0020': 'space', - 'U+007F': 'del' - }; - var KEY_CODE = { - 8: 'backspace', - 9: 'tab', - 13: 'enter', - 27: 'esc', - 33: 'pageup', - 34: 'pagedown', - 35: 'end', - 36: 'home', - 32: 'space', - 37: 'left', - 38: 'up', - 39: 'right', - 40: 'down', - 46: 'del', - 106: '*' - }; - var MODIFIER_KEYS = { - shift: 'shiftKey', - ctrl: 'ctrlKey', - alt: 'altKey', - meta: 'metaKey' - }; - var KEY_CHAR = /[a-z0-9*]/; - var IDENT_CHAR = /U\+/; - var ARROW_KEY = /^arrow/; - var SPACE_KEY = /^space(bar)?/; - var ESC_KEY = /^escape$/; - function transformKey(key, noSpecialChars) { - var validKey = ''; - if (key) { - var lKey = key.toLowerCase(); - if (lKey === ' ' || SPACE_KEY.test(lKey)) { - validKey = 'space'; - } else if (ESC_KEY.test(lKey)) { - validKey = 'esc'; - } else if (lKey.length == 1) { - if (!noSpecialChars || KEY_CHAR.test(lKey)) { - validKey = lKey; - } - } else if (ARROW_KEY.test(lKey)) { - validKey = lKey.replace('arrow', ''); - } else if (lKey == 'multiply') { - validKey = '*'; - } else { - validKey = lKey; - } - } - return validKey; - } - function transformKeyIdentifier(keyIdent) { - var validKey = ''; - if (keyIdent) { - if (keyIdent in KEY_IDENTIFIER) { - validKey = KEY_IDENTIFIER[keyIdent]; - } else if (IDENT_CHAR.test(keyIdent)) { - keyIdent = parseInt(keyIdent.replace('U+', '0x'), 16); - validKey = String.fromCharCode(keyIdent).toLowerCase(); - } else { - validKey = keyIdent.toLowerCase(); - } - } - return validKey; - } - function transformKeyCode(keyCode) { - var validKey = ''; - if (Number(keyCode)) { - if (keyCode >= 65 && keyCode <= 90) { - validKey = String.fromCharCode(32 + keyCode); - } else if (keyCode >= 112 && keyCode <= 123) { - validKey = 'f' + (keyCode - 112); - } else if (keyCode >= 48 && keyCode <= 57) { - validKey = String(keyCode - 48); - } else if (keyCode >= 96 && keyCode <= 105) { - validKey = String(keyCode - 96); - } else { - validKey = KEY_CODE[keyCode]; - } - } - return validKey; - } - function normalizedKeyForEvent(keyEvent, noSpecialChars) { - if (keyEvent.key) { - return transformKey(keyEvent.key, noSpecialChars); - } - if (keyEvent.detail && keyEvent.detail.key) { - return transformKey(keyEvent.detail.key, noSpecialChars); - } - return transformKeyIdentifier(keyEvent.keyIdentifier) || transformKeyCode(keyEvent.keyCode) || ''; - } - function keyComboMatchesEvent(keyCombo, event) { - var keyEvent = normalizedKeyForEvent(event, keyCombo.hasModifiers); - return keyEvent === keyCombo.key && (!keyCombo.hasModifiers || !!event.shiftKey === !!keyCombo.shiftKey && !!event.ctrlKey === !!keyCombo.ctrlKey && !!event.altKey === !!keyCombo.altKey && !!event.metaKey === !!keyCombo.metaKey); - } - function parseKeyComboString(keyComboString) { - if (keyComboString.length === 1) { - return { - combo: keyComboString, - key: keyComboString, - event: 'keydown' - }; - } - return keyComboString.split('+').reduce(function(parsedKeyCombo, keyComboPart) { - var eventParts = keyComboPart.split(':'); - var keyName = eventParts[0]; - var event = eventParts[1]; - if (keyName in MODIFIER_KEYS) { - parsedKeyCombo[MODIFIER_KEYS[keyName]] = true; - parsedKeyCombo.hasModifiers = true; - } else { - parsedKeyCombo.key = keyName; - parsedKeyCombo.event = event || 'keydown'; - } - return parsedKeyCombo; - }, { - combo: keyComboString.split(':').shift() - }); - } - function parseEventString(eventString) { - return eventString.trim().split(' ').map(function(keyComboString) { - return parseKeyComboString(keyComboString); - }); - } - Polymer.IronA11yKeysBehavior = { - properties: { - keyEventTarget: { - type: Object, - value: function() { - return this; - } - }, - stopKeyboardEventPropagation: { - type: Boolean, - value: false - }, - _boundKeyHandlers: { - type: Array, - value: function() { - return []; - } - }, - _imperativeKeyBindings: { - type: Object, - value: function() { - return {}; - } - } - }, - observers: [ '_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)' ], - keyBindings: {}, - registered: function() { - this._prepKeyBindings(); - }, - attached: function() { - this._listenKeyEventListeners(); - }, - detached: function() { - this._unlistenKeyEventListeners(); - }, - addOwnKeyBinding: function(eventString, handlerName) { - this._imperativeKeyBindings[eventString] = handlerName; - this._prepKeyBindings(); - this._resetKeyEventListeners(); - }, - removeOwnKeyBindings: function() { - this._imperativeKeyBindings = {}; - this._prepKeyBindings(); - this._resetKeyEventListeners(); - }, - keyboardEventMatchesKeys: function(event, eventString) { - var keyCombos = parseEventString(eventString); - for (var i = 0; i < keyCombos.length; ++i) { - if (keyComboMatchesEvent(keyCombos[i], event)) { - return true; - } - } - return false; - }, - _collectKeyBindings: function() { - var keyBindings = this.behaviors.map(function(behavior) { - return behavior.keyBindings; - }); - if (keyBindings.indexOf(this.keyBindings) === -1) { - keyBindings.push(this.keyBindings); - } - return keyBindings; - }, - _prepKeyBindings: function() { - this._keyBindings = {}; - this._collectKeyBindings().forEach(function(keyBindings) { - for (var eventString in keyBindings) { - this._addKeyBinding(eventString, keyBindings[eventString]); - } - }, this); - for (var eventString in this._imperativeKeyBindings) { - this._addKeyBinding(eventString, this._imperativeKeyBindings[eventString]); - } - for (var eventName in this._keyBindings) { - this._keyBindings[eventName].sort(function(kb1, kb2) { - var b1 = kb1[0].hasModifiers; - var b2 = kb2[0].hasModifiers; - return b1 === b2 ? 0 : b1 ? -1 : 1; - }); - } - }, - _addKeyBinding: function(eventString, handlerName) { - parseEventString(eventString).forEach(function(keyCombo) { - this._keyBindings[keyCombo.event] = this._keyBindings[keyCombo.event] || []; - this._keyBindings[keyCombo.event].push([ keyCombo, handlerName ]); - }, this); - }, - _resetKeyEventListeners: function() { - this._unlistenKeyEventListeners(); - if (this.isAttached) { - this._listenKeyEventListeners(); - } - }, - _listenKeyEventListeners: function() { - if (!this.keyEventTarget) { - return; - } - Object.keys(this._keyBindings).forEach(function(eventName) { - var keyBindings = this._keyBindings[eventName]; - var boundKeyHandler = this._onKeyBindingEvent.bind(this, keyBindings); - this._boundKeyHandlers.push([ this.keyEventTarget, eventName, boundKeyHandler ]); - this.keyEventTarget.addEventListener(eventName, boundKeyHandler); - }, this); - }, - _unlistenKeyEventListeners: function() { - var keyHandlerTuple; - var keyEventTarget; - var eventName; - var boundKeyHandler; - while (this._boundKeyHandlers.length) { - keyHandlerTuple = this._boundKeyHandlers.pop(); - keyEventTarget = keyHandlerTuple[0]; - eventName = keyHandlerTuple[1]; - boundKeyHandler = keyHandlerTuple[2]; - keyEventTarget.removeEventListener(eventName, boundKeyHandler); - } - }, - _onKeyBindingEvent: function(keyBindings, event) { - if (this.stopKeyboardEventPropagation) { - event.stopPropagation(); - } - if (event.defaultPrevented) { - return; - } - for (var i = 0; i < keyBindings.length; i++) { - var keyCombo = keyBindings[i][0]; - var handlerName = keyBindings[i][1]; - if (keyComboMatchesEvent(keyCombo, event)) { - this._triggerKeyHandler(keyCombo, handlerName, event); - if (event.defaultPrevented) { - return; - } - } - } - }, - _triggerKeyHandler: function(keyCombo, handlerName, keyboardEvent) { - var detail = Object.create(keyCombo); - detail.keyboardEvent = keyboardEvent; - var event = new CustomEvent(keyCombo.event, { - detail: detail, - cancelable: true - }); - this[handlerName].call(this, event); - if (event.defaultPrevented) { - keyboardEvent.preventDefault(); - } - } - }; -})(); - -Polymer.IronControlState = { - properties: { - focused: { - type: Boolean, - value: false, - notify: true, - readOnly: true, - reflectToAttribute: true - }, - disabled: { - type: Boolean, - value: false, - notify: true, - observer: '_disabledChanged', - reflectToAttribute: true - }, - _oldTabIndex: { - type: Number - }, - _boundFocusBlurHandler: { - type: Function, - value: function() { - return this._focusBlurHandler.bind(this); - } - } - }, - observers: [ '_changedControlState(focused, disabled)' ], - ready: function() { - this.addEventListener('focus', this._boundFocusBlurHandler, true); - this.addEventListener('blur', this._boundFocusBlurHandler, true); - }, - _focusBlurHandler: function(event) { - if (event.target === this) { - this._setFocused(event.type === 'focus'); - } else if (!this.shadowRoot) { - var target = Polymer.dom(event).localTarget; - if (!this.isLightDescendant(target)) { - this.fire(event.type, { - sourceEvent: event - }, { - node: this, - bubbles: event.bubbles, - cancelable: event.cancelable - }); - } - } - }, - _disabledChanged: function(disabled, old) { - this.setAttribute('aria-disabled', disabled ? 'true' : 'false'); - this.style.pointerEvents = disabled ? 'none' : ''; - if (disabled) { - this._oldTabIndex = this.tabIndex; - this._setFocused(false); - this.tabIndex = -1; - this.blur(); - } else if (this._oldTabIndex !== undefined) { - this.tabIndex = this._oldTabIndex; - } - }, - _changedControlState: function() { - if (this._controlStateChanged) { - this._controlStateChanged(); - } - } -}; - -Polymer.IronButtonStateImpl = { - properties: { - pressed: { - type: Boolean, - readOnly: true, - value: false, - reflectToAttribute: true, - observer: '_pressedChanged' - }, - toggles: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - active: { - type: Boolean, - value: false, - notify: true, - reflectToAttribute: true - }, - pointerDown: { - type: Boolean, - readOnly: true, - value: false - }, - receivedFocusFromKeyboard: { - type: Boolean, - readOnly: true - }, - ariaActiveAttribute: { - type: String, - value: 'aria-pressed', - observer: '_ariaActiveAttributeChanged' - } - }, - listeners: { - down: '_downHandler', - up: '_upHandler', - tap: '_tapHandler' - }, - observers: [ '_detectKeyboardFocus(focused)', '_activeChanged(active, ariaActiveAttribute)' ], - keyBindings: { - 'enter:keydown': '_asyncClick', - 'space:keydown': '_spaceKeyDownHandler', - 'space:keyup': '_spaceKeyUpHandler' - }, - _mouseEventRe: /^mouse/, - _tapHandler: function() { - if (this.toggles) { - this._userActivate(!this.active); - } else { - this.active = false; - } - }, - _detectKeyboardFocus: function(focused) { - this._setReceivedFocusFromKeyboard(!this.pointerDown && focused); - }, - _userActivate: function(active) { - if (this.active !== active) { - this.active = active; - this.fire('change'); - } - }, - _downHandler: function(event) { - this._setPointerDown(true); - this._setPressed(true); - this._setReceivedFocusFromKeyboard(false); - }, - _upHandler: function() { - this._setPointerDown(false); - this._setPressed(false); - }, - _spaceKeyDownHandler: function(event) { - var keyboardEvent = event.detail.keyboardEvent; - var target = Polymer.dom(keyboardEvent).localTarget; - if (this.isLightDescendant(target)) return; - keyboardEvent.preventDefault(); - keyboardEvent.stopImmediatePropagation(); - this._setPressed(true); - }, - _spaceKeyUpHandler: function(event) { - var keyboardEvent = event.detail.keyboardEvent; - var target = Polymer.dom(keyboardEvent).localTarget; - if (this.isLightDescendant(target)) return; - if (this.pressed) { - this._asyncClick(); - } - this._setPressed(false); - }, - _asyncClick: function() { - this.async(function() { - this.click(); - }, 1); - }, - _pressedChanged: function(pressed) { - this._changedButtonState(); - }, - _ariaActiveAttributeChanged: function(value, oldValue) { - if (oldValue && oldValue != value && this.hasAttribute(oldValue)) { - this.removeAttribute(oldValue); - } - }, - _activeChanged: function(active, ariaActiveAttribute) { - if (this.toggles) { - this.setAttribute(this.ariaActiveAttribute, active ? 'true' : 'false'); - } else { - this.removeAttribute(this.ariaActiveAttribute); - } - this._changedButtonState(); - }, - _controlStateChanged: function() { - if (this.disabled) { - this._setPressed(false); - } else { - this._changedButtonState(); - } - }, - _changedButtonState: function() { - if (this._buttonStateChanged) { - this._buttonStateChanged(); - } - } -}; - -Polymer.IronButtonState = [ Polymer.IronA11yKeysBehavior, Polymer.IronButtonStateImpl ]; - -(function() { - var Utility = { - distance: function(x1, y1, x2, y2) { - var xDelta = x1 - x2; - var yDelta = y1 - y2; - return Math.sqrt(xDelta * xDelta + yDelta * yDelta); - }, - now: window.performance && window.performance.now ? window.performance.now.bind(window.performance) : Date.now - }; - function ElementMetrics(element) { - this.element = element; - this.width = this.boundingRect.width; - this.height = this.boundingRect.height; - this.size = Math.max(this.width, this.height); - } - ElementMetrics.prototype = { - get boundingRect() { - return this.element.getBoundingClientRect(); - }, - furthestCornerDistanceFrom: function(x, y) { - var topLeft = Utility.distance(x, y, 0, 0); - var topRight = Utility.distance(x, y, this.width, 0); - var bottomLeft = Utility.distance(x, y, 0, this.height); - var bottomRight = Utility.distance(x, y, this.width, this.height); - return Math.max(topLeft, topRight, bottomLeft, bottomRight); - } - }; - function Ripple(element) { - this.element = element; - this.color = window.getComputedStyle(element).color; - this.wave = document.createElement('div'); - this.waveContainer = document.createElement('div'); - this.wave.style.backgroundColor = this.color; - this.wave.classList.add('wave'); - this.waveContainer.classList.add('wave-container'); - Polymer.dom(this.waveContainer).appendChild(this.wave); - this.resetInteractionState(); - } - Ripple.MAX_RADIUS = 300; - Ripple.prototype = { - get recenters() { - return this.element.recenters; - }, - get center() { - return this.element.center; - }, - get mouseDownElapsed() { - var elapsed; - if (!this.mouseDownStart) { - return 0; - } - elapsed = Utility.now() - this.mouseDownStart; - if (this.mouseUpStart) { - elapsed -= this.mouseUpElapsed; - } - return elapsed; - }, - get mouseUpElapsed() { - return this.mouseUpStart ? Utility.now() - this.mouseUpStart : 0; - }, - get mouseDownElapsedSeconds() { - return this.mouseDownElapsed / 1e3; - }, - get mouseUpElapsedSeconds() { - return this.mouseUpElapsed / 1e3; - }, - get mouseInteractionSeconds() { - return this.mouseDownElapsedSeconds + this.mouseUpElapsedSeconds; - }, - get initialOpacity() { - return this.element.initialOpacity; - }, - get opacityDecayVelocity() { - return this.element.opacityDecayVelocity; - }, - get radius() { - var width2 = this.containerMetrics.width * this.containerMetrics.width; - var height2 = this.containerMetrics.height * this.containerMetrics.height; - var waveRadius = Math.min(Math.sqrt(width2 + height2), Ripple.MAX_RADIUS) * 1.1 + 5; - var duration = 1.1 - .2 * (waveRadius / Ripple.MAX_RADIUS); - var timeNow = this.mouseInteractionSeconds / duration; - var size = waveRadius * (1 - Math.pow(80, -timeNow)); - return Math.abs(size); - }, - get opacity() { - if (!this.mouseUpStart) { - return this.initialOpacity; - } - return Math.max(0, this.initialOpacity - this.mouseUpElapsedSeconds * this.opacityDecayVelocity); - }, - get outerOpacity() { - var outerOpacity = this.mouseUpElapsedSeconds * .3; - var waveOpacity = this.opacity; - return Math.max(0, Math.min(outerOpacity, waveOpacity)); - }, - get isOpacityFullyDecayed() { - return this.opacity < .01 && this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS); - }, - get isRestingAtMaxRadius() { - return this.opacity >= this.initialOpacity && this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS); - }, - get isAnimationComplete() { - return this.mouseUpStart ? this.isOpacityFullyDecayed : this.isRestingAtMaxRadius; - }, - get translationFraction() { - return Math.min(1, this.radius / this.containerMetrics.size * 2 / Math.sqrt(2)); - }, - get xNow() { - if (this.xEnd) { - return this.xStart + this.translationFraction * (this.xEnd - this.xStart); - } - return this.xStart; - }, - get yNow() { - if (this.yEnd) { - return this.yStart + this.translationFraction * (this.yEnd - this.yStart); - } - return this.yStart; - }, - get isMouseDown() { - return this.mouseDownStart && !this.mouseUpStart; - }, - resetInteractionState: function() { - this.maxRadius = 0; - this.mouseDownStart = 0; - this.mouseUpStart = 0; - this.xStart = 0; - this.yStart = 0; - this.xEnd = 0; - this.yEnd = 0; - this.slideDistance = 0; - this.containerMetrics = new ElementMetrics(this.element); - }, - draw: function() { - var scale; - var translateString; - var dx; - var dy; - this.wave.style.opacity = this.opacity; - scale = this.radius / (this.containerMetrics.size / 2); - dx = this.xNow - this.containerMetrics.width / 2; - dy = this.yNow - this.containerMetrics.height / 2; - this.waveContainer.style.webkitTransform = 'translate(' + dx + 'px, ' + dy + 'px)'; - this.waveContainer.style.transform = 'translate3d(' + dx + 'px, ' + dy + 'px, 0)'; - this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')'; - this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)'; - }, - downAction: function(event) { - var xCenter = this.containerMetrics.width / 2; - var yCenter = this.containerMetrics.height / 2; - this.resetInteractionState(); - this.mouseDownStart = Utility.now(); - if (this.center) { - this.xStart = xCenter; - this.yStart = yCenter; - this.slideDistance = Utility.distance(this.xStart, this.yStart, this.xEnd, this.yEnd); - } else { - this.xStart = event ? event.detail.x - this.containerMetrics.boundingRect.left : this.containerMetrics.width / 2; - this.yStart = event ? event.detail.y - this.containerMetrics.boundingRect.top : this.containerMetrics.height / 2; - } - if (this.recenters) { - this.xEnd = xCenter; - this.yEnd = yCenter; - this.slideDistance = Utility.distance(this.xStart, this.yStart, this.xEnd, this.yEnd); - } - this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom(this.xStart, this.yStart); - this.waveContainer.style.top = (this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px'; - this.waveContainer.style.left = (this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px'; - this.waveContainer.style.width = this.containerMetrics.size + 'px'; - this.waveContainer.style.height = this.containerMetrics.size + 'px'; - }, - upAction: function(event) { - if (!this.isMouseDown) { - return; - } - this.mouseUpStart = Utility.now(); - }, - remove: function() { - Polymer.dom(this.waveContainer.parentNode).removeChild(this.waveContainer); - } - }; - Polymer({ - is: 'paper-ripple', - behaviors: [ Polymer.IronA11yKeysBehavior ], - properties: { - initialOpacity: { - type: Number, - value: .25 - }, - opacityDecayVelocity: { - type: Number, - value: .8 - }, - recenters: { - type: Boolean, - value: false - }, - center: { - type: Boolean, - value: false - }, - ripples: { - type: Array, - value: function() { - return []; - } - }, - animating: { - type: Boolean, - readOnly: true, - reflectToAttribute: true, - value: false - }, - holdDown: { - type: Boolean, - value: false, - observer: '_holdDownChanged' - }, - noink: { - type: Boolean, - value: false - }, - _animating: { - type: Boolean - }, - _boundAnimate: { - type: Function, - value: function() { - return this.animate.bind(this); - } - } - }, - get target() { - return this.keyEventTarget; - }, - keyBindings: { - 'enter:keydown': '_onEnterKeydown', - 'space:keydown': '_onSpaceKeydown', - 'space:keyup': '_onSpaceKeyup' - }, - attached: function() { - if (this.parentNode.nodeType == 11) { - this.keyEventTarget = Polymer.dom(this).getOwnerRoot().host; - } else { - this.keyEventTarget = this.parentNode; - } - var keyEventTarget = this.keyEventTarget; - this.listen(keyEventTarget, 'up', 'uiUpAction'); - this.listen(keyEventTarget, 'down', 'uiDownAction'); - }, - detached: function() { - this.unlisten(this.keyEventTarget, 'up', 'uiUpAction'); - this.unlisten(this.keyEventTarget, 'down', 'uiDownAction'); - this.keyEventTarget = null; - }, - get shouldKeepAnimating() { - for (var index = 0; index < this.ripples.length; ++index) { - if (!this.ripples[index].isAnimationComplete) { - return true; - } - } - return false; - }, - simulatedRipple: function() { - this.downAction(null); - this.async(function() { - this.upAction(); - }, 1); - }, - uiDownAction: function(event) { - if (!this.noink) { - this.downAction(event); - } - }, - downAction: function(event) { - if (this.holdDown && this.ripples.length > 0) { - return; - } - var ripple = this.addRipple(); - ripple.downAction(event); - if (!this._animating) { - this._animating = true; - this.animate(); - } - }, - uiUpAction: function(event) { - if (!this.noink) { - this.upAction(event); - } - }, - upAction: function(event) { - if (this.holdDown) { - return; - } - this.ripples.forEach(function(ripple) { - ripple.upAction(event); - }); - this._animating = true; - this.animate(); - }, - onAnimationComplete: function() { - this._animating = false; - this.$.background.style.backgroundColor = null; - this.fire('transitionend'); - }, - addRipple: function() { - var ripple = new Ripple(this); - Polymer.dom(this.$.waves).appendChild(ripple.waveContainer); - this.$.background.style.backgroundColor = ripple.color; - this.ripples.push(ripple); - this._setAnimating(true); - return ripple; - }, - removeRipple: function(ripple) { - var rippleIndex = this.ripples.indexOf(ripple); - if (rippleIndex < 0) { - return; - } - this.ripples.splice(rippleIndex, 1); - ripple.remove(); - if (!this.ripples.length) { - this._setAnimating(false); - } - }, - animate: function() { - if (!this._animating) { - return; - } - var index; - var ripple; - for (index = 0; index < this.ripples.length; ++index) { - ripple = this.ripples[index]; - ripple.draw(); - this.$.background.style.opacity = ripple.outerOpacity; - if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) { - this.removeRipple(ripple); - } - } - if (!this.shouldKeepAnimating && this.ripples.length === 0) { - this.onAnimationComplete(); - } else { - window.requestAnimationFrame(this._boundAnimate); - } - }, - _onEnterKeydown: function() { - this.uiDownAction(); - this.async(this.uiUpAction, 1); - }, - _onSpaceKeydown: function() { - this.uiDownAction(); - }, - _onSpaceKeyup: function() { - this.uiUpAction(); - }, - _holdDownChanged: function(newVal, oldVal) { - if (oldVal === undefined) { - return; - } - if (newVal) { - this.downAction(); - } else { - this.upAction(); - } - } - }); -})(); - -Polymer.PaperRippleBehavior = { - properties: { - noink: { - type: Boolean, - observer: '_noinkChanged' - }, - _rippleContainer: { - type: Object - } - }, - _buttonStateChanged: function() { - if (this.focused) { - this.ensureRipple(); - } - }, - _downHandler: function(event) { - Polymer.IronButtonStateImpl._downHandler.call(this, event); - if (this.pressed) { - this.ensureRipple(event); - } - }, - ensureRipple: function(optTriggeringEvent) { - 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); - } - if (optTriggeringEvent) { - var domContainer = Polymer.dom(this._rippleContainer || this); - var target = Polymer.dom(optTriggeringEvent).rootTarget; - if (domContainer.deepContains(target)) { - this._ripple.uiDownAction(optTriggeringEvent); - } - } - } - }, - getRipple: function() { - this.ensureRipple(); - return this._ripple; - }, - hasRipple: function() { - return Boolean(this._ripple); - }, - _createRipple: function() { - return document.createElement('paper-ripple'); - }, - _noinkChanged: function(noink) { - if (this.hasRipple()) { - this._ripple.noink = noink; - } - } -}; - -Polymer.PaperInkyFocusBehaviorImpl = { - observers: [ '_focusedChanged(receivedFocusFromKeyboard)' ], - _focusedChanged: function(receivedFocusFromKeyboard) { - if (receivedFocusFromKeyboard) { - this.ensureRipple(); - } - if (this.hasRipple()) { - this._ripple.holdDown = receivedFocusFromKeyboard; - } - }, - _createRipple: function() { - var ripple = Polymer.PaperRippleBehavior._createRipple(); - ripple.id = 'ink'; - ripple.setAttribute('center', ''); - ripple.classList.add('circle'); - return ripple; - } -}; - -Polymer.PaperInkyFocusBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, Polymer.PaperRippleBehavior, Polymer.PaperInkyFocusBehaviorImpl ]; - -Polymer({ - is: 'paper-icon-button', - hostAttributes: { - role: 'button', - tabindex: '0' - }, - behaviors: [ Polymer.PaperInkyFocusBehavior ], - properties: { - src: { - type: String - }, - icon: { - type: String - }, - alt: { - type: String, - observer: "_altChanged" - } - }, - _altChanged: function(newValue, oldValue) { - var label = this.getAttribute('aria-label'); - if (!label || oldValue == label) { - this.setAttribute('aria-label', newValue); - } - } -}); - -Polymer({ - is: 'iron-iconset-svg', - properties: { - name: { - type: String, - observer: '_nameChanged' - }, - size: { - type: Number, - value: 24 - } - }, - attached: function() { - this.style.display = 'none'; - }, - getIconNames: function() { - this._icons = this._createIconMap(); - return Object.keys(this._icons).map(function(n) { - return this.name + ':' + n; - }, this); - }, - applyIcon: function(element, iconName) { - element = element.root || element; - this.removeIcon(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; - }, - removeIcon: function(element) { - if (element._svgIcon) { - Polymer.dom(element).removeChild(element._svgIcon); - element._svgIcon = null; - } - }, - _nameChanged: function() { - new Polymer.IronMeta({ - type: 'iconset', - key: this.name, - value: this - }); - this.async(function() { - this.fire('iron-iconset-added', this, { - node: window - }); - }); - }, - _createIconMap: function() { - var icons = Object.create(null); - Polymer.dom(this).querySelectorAll('[id]').forEach(function(icon) { - icons[icon.id] = icon; - }); - return icons; - }, - _cloneIcon: function(id) { - this._icons = this._icons || this._createIconMap(); - return this._prepareSvgClone(this._icons[id], this.size); - }, - _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'); - svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;'; - svg.appendChild(content).removeAttribute('id'); - return svg; - } - return null; - } -}); - +dy=this.yNow-this.containerMetrics.height/2;this.waveContainer.style.webkitTransform="translate("+dx+"px, "+dy+"px)";this.waveContainer.style.transform="translate3d("+dx+"px, "+dy+"px, 0)";this.wave.style.webkitTransform="scale("+scale+","+scale+")";this.wave.style.transform="scale3d("+scale+","+scale+",1)"},downAction:function(event){var xCenter=this.containerMetrics.width/2;var yCenter=this.containerMetrics.height/2;this.resetInteractionState();this.mouseDownStart=Utility.now();if(this.center){this.xStart=xCenter;this.yStart=yCenter;this.slideDistance=Utility.distance(this.xStart,this.yStart,this.xEnd,this.yEnd)}else{this.xStart=event?event.detail.x-this.containerMetrics.boundingRect.left:this.containerMetrics.width/2;this.yStart=event?event.detail.y-this.containerMetrics.boundingRect.top:this.containerMetrics.height/2}if(this.recenters){this.xEnd=xCenter;this.yEnd=yCenter;this.slideDistance=Utility.distance(this.xStart,this.yStart,this.xEnd,this.yEnd)}this.maxRadius=this.containerMetrics.furthestCornerDistanceFrom(this.xStart,this.yStart);this.waveContainer.style.top=(this.containerMetrics.height-this.containerMetrics.size)/2+"px";this.waveContainer.style.left=(this.containerMetrics.width-this.containerMetrics.size)/2+"px";this.waveContainer.style.width=this.containerMetrics.size+"px";this.waveContainer.style.height=this.containerMetrics.size+"px"},upAction:function(event){if(!this.isMouseDown){return}this.mouseUpStart=Utility.now()},remove:function(){Polymer.dom(this.waveContainer.parentNode).removeChild(this.waveContainer)}};Polymer({is:"paper-ripple",behaviors:[Polymer.IronA11yKeysBehavior],properties:{initialOpacity:{type:Number,value:.25},opacityDecayVelocity:{type:Number,value:.8},recenters:{type:Boolean,value:false},center:{type:Boolean,value:false},ripples:{type:Array,value:function(){return[]}},animating:{type:Boolean,readOnly:true,reflectToAttribute:true,value:false},holdDown:{type:Boolean,value:false,observer:"_holdDownChanged"},noink:{type:Boolean,value:false},_animating:{type:Boolean},_boundAnimate:{type:Function,value:function(){return this.animate.bind(this)}}},get target(){return this.keyEventTarget},keyBindings:{"enter:keydown":"_onEnterKeydown","space:keydown":"_onSpaceKeydown","space:keyup":"_onSpaceKeyup"},attached:function(){if(this.parentNode.nodeType==11){this.keyEventTarget=Polymer.dom(this).getOwnerRoot().host}else{this.keyEventTarget=this.parentNode}var keyEventTarget=this.keyEventTarget;this.listen(keyEventTarget,"up","uiUpAction");this.listen(keyEventTarget,"down","uiDownAction")},detached:function(){this.unlisten(this.keyEventTarget,"up","uiUpAction");this.unlisten(this.keyEventTarget,"down","uiDownAction");this.keyEventTarget=null},get shouldKeepAnimating(){for(var index=0;index<this.ripples.length;++index){if(!this.ripples[index].isAnimationComplete){return true}}return false},simulatedRipple:function(){this.downAction(null);this.async(function(){this.upAction()},1)},uiDownAction:function(event){if(!this.noink){this.downAction(event)}},downAction:function(event){if(this.holdDown&&this.ripples.length>0){return}var ripple=this.addRipple();ripple.downAction(event);if(!this._animating){this._animating=true;this.animate()}},uiUpAction:function(event){if(!this.noink){this.upAction(event)}},upAction:function(event){if(this.holdDown){return}this.ripples.forEach(function(ripple){ripple.upAction(event)});this._animating=true;this.animate()},onAnimationComplete:function(){this._animating=false;this.$.background.style.backgroundColor=null;this.fire("transitionend")},addRipple:function(){var ripple=new Ripple(this);Polymer.dom(this.$.waves).appendChild(ripple.waveContainer);this.$.background.style.backgroundColor=ripple.color;this.ripples.push(ripple);this._setAnimating(true);return ripple},removeRipple:function(ripple){var rippleIndex=this.ripples.indexOf(ripple);if(rippleIndex<0){return}this.ripples.splice(rippleIndex,1);ripple.remove();if(!this.ripples.length){this._setAnimating(false)}},animate:function(){if(!this._animating){return}var index;var ripple;for(index=0;index<this.ripples.length;++index){ripple=this.ripples[index];ripple.draw();this.$.background.style.opacity=ripple.outerOpacity;if(ripple.isOpacityFullyDecayed&&!ripple.isRestingAtMaxRadius){this.removeRipple(ripple)}}if(!this.shouldKeepAnimating&&this.ripples.length===0){this.onAnimationComplete()}else{window.requestAnimationFrame(this._boundAnimate)}},_onEnterKeydown:function(){this.uiDownAction();this.async(this.uiUpAction,1)},_onSpaceKeydown:function(){this.uiDownAction()},_onSpaceKeyup:function(){this.uiUpAction()},_holdDownChanged:function(newVal,oldVal){if(oldVal===undefined){return}if(newVal){this.downAction()}else{this.upAction()}}})})();Polymer.PaperRippleBehavior={properties:{noink:{type:Boolean,observer:"_noinkChanged"},_rippleContainer:{type:Object}},_buttonStateChanged:function(){if(this.focused){this.ensureRipple()}},_downHandler:function(event){Polymer.IronButtonStateImpl._downHandler.call(this,event);if(this.pressed){this.ensureRipple(event)}},ensureRipple:function(optTriggeringEvent){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)}if(optTriggeringEvent){var domContainer=Polymer.dom(this._rippleContainer||this);var target=Polymer.dom(optTriggeringEvent).rootTarget;if(domContainer.deepContains(target)){this._ripple.uiDownAction(optTriggeringEvent)}}}},getRipple:function(){this.ensureRipple();return this._ripple},hasRipple:function(){return Boolean(this._ripple)},_createRipple:function(){return document.createElement("paper-ripple")},_noinkChanged:function(noink){if(this.hasRipple()){this._ripple.noink=noink}}};Polymer.PaperInkyFocusBehaviorImpl={observers:["_focusedChanged(receivedFocusFromKeyboard)"],_focusedChanged:function(receivedFocusFromKeyboard){if(receivedFocusFromKeyboard){this.ensureRipple()}if(this.hasRipple()){this._ripple.holdDown=receivedFocusFromKeyboard}},_createRipple:function(){var ripple=Polymer.PaperRippleBehavior._createRipple();ripple.id="ink";ripple.setAttribute("center","");ripple.classList.add("circle");return ripple}};Polymer.PaperInkyFocusBehavior=[Polymer.IronButtonState,Polymer.IronControlState,Polymer.PaperRippleBehavior,Polymer.PaperInkyFocusBehaviorImpl];Polymer({is:"paper-icon-button",hostAttributes:{role:"button",tabindex:"0"},behaviors:[Polymer.PaperInkyFocusBehavior],properties:{src:{type:String},icon:{type:String},alt:{type:String,observer:"_altChanged"}},_altChanged:function(newValue,oldValue){var label=this.getAttribute("aria-label");if(!label||oldValue==label){this.setAttribute("aria-label",newValue)}}});Polymer({is:"iron-iconset-svg",properties:{name:{type:String,observer:"_nameChanged"},size:{type:Number,value:24}},attached:function(){this.style.display="none"},getIconNames:function(){this._icons=this._createIconMap();return Object.keys(this._icons).map(function(n){return this.name+":"+n},this)},applyIcon:function(element,iconName){element=element.root||element;this.removeIcon(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},removeIcon:function(element){if(element._svgIcon){Polymer.dom(element).removeChild(element._svgIcon);element._svgIcon=null}},_nameChanged:function(){new Polymer.IronMeta({type:"iconset",key:this.name,value:this});this.async(function(){this.fire("iron-iconset-added",this,{node:window})})},_createIconMap:function(){var icons=Object.create(null);Polymer.dom(this).querySelectorAll("[id]").forEach(function(icon){icons[icon.id]=icon});return icons},_cloneIcon:function(id){this._icons=this._icons||this._createIconMap();return this._prepareSvgClone(this._icons[id],this.size)},_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");svg.style.cssText="pointer-events: none; display: block; width: 100%; height: 100%;";svg.appendChild(content).removeAttribute("id");return svg}return null}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'cr-lazy-render', - "extends": 'template', - behaviors: [ Polymer.Templatizer ], - child_: null, - get: function() { - if (!this.child_) this.render_(); - return this.child_; - }, - getIfExists: function() { - return this.child_; - }, - render_: function() { - if (!this.ctor) this.templatize(this); - var parentNode = this.parentNode; - if (parentNode && !this.child_) { - var instance = this.stamp({}); - this.child_ = instance.root.firstElementChild; - parentNode.insertBefore(instance.root, this); - } - }, - _forwardParentProp: function(prop, value) { - if (this.child_) this.child_._templateInstance[prop] = value; - }, - _forwardParentPath: function(path, value) { - if (this.child_) this.child_._templateInstance.notifyPath(path, value, true); - } -}); - -(function() { - 'use strict'; - Polymer.IronA11yAnnouncer = Polymer({ - is: 'iron-a11y-announcer', - properties: { - mode: { - type: String, - value: 'polite' - }, - _text: { - type: String, - value: '' - } - }, - created: function() { - if (!Polymer.IronA11yAnnouncer.instance) { - Polymer.IronA11yAnnouncer.instance = this; - } - document.body.addEventListener('iron-announce', this._onIronAnnounce.bind(this)); - }, - announce: function(text) { - this._text = ''; - this.async(function() { - this._text = text; - }, 100); - }, - _onIronAnnounce: function(event) { - if (event.detail && event.detail.text) { - this.announce(event.detail.text); - } - } - }); - Polymer.IronA11yAnnouncer.instance = null; - Polymer.IronA11yAnnouncer.requestAvailability = function() { - if (!Polymer.IronA11yAnnouncer.instance) { - Polymer.IronA11yAnnouncer.instance = document.createElement('iron-a11y-announcer'); - } - document.body.appendChild(Polymer.IronA11yAnnouncer.instance); - }; -})(); - -Polymer.IronValidatableBehaviorMeta = null; - -Polymer.IronValidatableBehavior = { - properties: { - validator: { - type: String - }, - invalid: { - notify: true, - reflectToAttribute: true, - type: Boolean, - value: false - }, - _validatorMeta: { - type: Object - }, - validatorType: { - type: String, - value: 'validator' - }, - _validator: { - type: Object, - computed: '__computeValidator(validator)' - } - }, - observers: [ '_invalidChanged(invalid)' ], - registered: function() { - Polymer.IronValidatableBehaviorMeta = new Polymer.IronMeta({ - type: 'validator' - }); - }, - _invalidChanged: function() { - if (this.invalid) { - this.setAttribute('aria-invalid', 'true'); - } else { - this.removeAttribute('aria-invalid'); - } - }, - hasValidator: function() { - return this._validator != null; - }, - validate: function(value) { - this.invalid = !this._getValidity(value); - return !this.invalid; - }, - _getValidity: function(value) { - if (this.hasValidator()) { - return this._validator.validate(value); - } - return true; - }, - __computeValidator: function() { - return Polymer.IronValidatableBehaviorMeta && Polymer.IronValidatableBehaviorMeta.byKey(this.validator); - } -}; - -Polymer({ - is: 'iron-input', - "extends": 'input', - behaviors: [ Polymer.IronValidatableBehavior ], - properties: { - bindValue: { - observer: '_bindValueChanged', - type: String - }, - preventInvalidInput: { - type: Boolean - }, - allowedPattern: { - type: String, - observer: "_allowedPatternChanged" - }, - _previousValidInput: { - type: String, - value: '' - }, - _patternAlreadyChecked: { - type: Boolean, - value: false - } - }, - listeners: { - input: '_onInput', - keypress: '_onKeypress' - }, - registered: function() { - if (!this._canDispatchEventOnDisabled()) { - this._origDispatchEvent = this.dispatchEvent; - this.dispatchEvent = this._dispatchEventFirefoxIE; - } - }, - created: function() { - Polymer.IronA11yAnnouncer.requestAvailability(); - }, - _canDispatchEventOnDisabled: function() { - var input = document.createElement('input'); - var canDispatch = false; - input.disabled = true; - input.addEventListener('feature-check-dispatch-event', function() { - canDispatch = true; - }); - try { - input.dispatchEvent(new Event('feature-check-dispatch-event')); - } catch (e) {} - return canDispatch; - }, - _dispatchEventFirefoxIE: function() { - var disabled = this.disabled; - this.disabled = false; - this._origDispatchEvent.apply(this, arguments); - this.disabled = disabled; - }, - get _patternRegExp() { - var pattern; - if (this.allowedPattern) { - pattern = new RegExp(this.allowedPattern); - } else { - switch (this.type) { - case 'number': - pattern = /[0-9.,e-]/; - break; - } - } - return pattern; - }, - ready: function() { - this.bindValue = this.value; - }, - _bindValueChanged: function() { - if (this.value !== this.bindValue) { - this.value = !(this.bindValue || this.bindValue === 0 || this.bindValue === false) ? '' : this.bindValue; - } - this.fire('bind-value-changed', { - value: this.bindValue - }); - }, - _allowedPatternChanged: function() { - this.preventInvalidInput = this.allowedPattern ? true : false; - }, - _onInput: function() { - if (this.preventInvalidInput && !this._patternAlreadyChecked) { - var valid = this._checkPatternValidity(); - if (!valid) { - this._announceInvalidCharacter('Invalid string of characters not entered.'); - this.value = this._previousValidInput; - } - } - this.bindValue = this.value; - this._previousValidInput = this.value; - this._patternAlreadyChecked = false; - }, - _isPrintable: function(event) { - var anyNonPrintable = event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 13 || event.keyCode == 27; - var mozNonPrintable = event.keyCode == 19 || event.keyCode == 20 || event.keyCode == 45 || event.keyCode == 46 || event.keyCode == 144 || event.keyCode == 145 || event.keyCode > 32 && event.keyCode < 41 || event.keyCode > 111 && event.keyCode < 124; - return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable); - }, - _onKeypress: function(event) { - if (!this.preventInvalidInput && this.type !== 'number') { - return; - } - var regexp = this._patternRegExp; - if (!regexp) { - return; - } - if (event.metaKey || event.ctrlKey || event.altKey) return; - this._patternAlreadyChecked = true; - var thisChar = String.fromCharCode(event.charCode); - if (this._isPrintable(event) && !regexp.test(thisChar)) { - event.preventDefault(); - this._announceInvalidCharacter('Invalid character ' + thisChar + ' not entered.'); - } - }, - _checkPatternValidity: function() { - var regexp = this._patternRegExp; - if (!regexp) { - return true; - } - for (var i = 0; i < this.value.length; i++) { - if (!regexp.test(this.value[i])) { - return false; - } - } - return true; - }, - validate: function() { - var valid = this.checkValidity(); - if (valid) { - if (this.required && this.value === '') { - valid = false; - } else if (this.hasValidator()) { - valid = Polymer.IronValidatableBehavior.validate.call(this, this.value); - } - } - this.invalid = !valid; - this.fire('iron-input-validate'); - return valid; - }, - _announceInvalidCharacter: function(message) { - this.fire('iron-announce', { - text: message - }); - } -}); - -Polymer({ - is: 'paper-input-container', - properties: { - noLabelFloat: { - type: Boolean, - value: false - }, - alwaysFloatLabel: { - type: Boolean, - value: false - }, - attrForValue: { - type: String, - value: 'bind-value' - }, - autoValidate: { - type: Boolean, - value: false - }, - invalid: { - observer: '_invalidChanged', - type: Boolean, - value: false - }, - focused: { - readOnly: true, - type: Boolean, - value: false, - notify: true - }, - _addons: { - type: Array - }, - _inputHasContent: { - type: Boolean, - value: false - }, - _inputSelector: { - type: String, - value: 'input,textarea,.paper-input-input' - }, - _boundOnFocus: { - type: Function, - value: function() { - return this._onFocus.bind(this); - } - }, - _boundOnBlur: { - type: Function, - value: function() { - return this._onBlur.bind(this); - } - }, - _boundOnInput: { - type: Function, - value: function() { - return this._onInput.bind(this); - } - }, - _boundValueChanged: { - type: Function, - value: function() { - return this._onValueChanged.bind(this); - } - } - }, - listeners: { - 'addon-attached': '_onAddonAttached', - 'iron-input-validate': '_onIronInputValidate' - }, - get _valueChangedEvent() { - return this.attrForValue + '-changed'; - }, - get _propertyForValue() { - return Polymer.CaseMap.dashToCamelCase(this.attrForValue); - }, - get _inputElement() { - return Polymer.dom(this).querySelector(this._inputSelector); - }, - get _inputElementValue() { - return this._inputElement[this._propertyForValue] || this._inputElement.value; - }, - ready: function() { - if (!this._addons) { - this._addons = []; - } - this.addEventListener('focus', this._boundOnFocus, true); - this.addEventListener('blur', this._boundOnBlur, true); - }, - attached: function() { - if (this.attrForValue) { - this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged); - } else { - this.addEventListener('input', this._onInput); - } - if (this._inputElementValue != '') { - this._handleValueAndAutoValidate(this._inputElement); - } else { - this._handleValue(this._inputElement); - } - }, - _onAddonAttached: function(event) { - if (!this._addons) { - this._addons = []; - } - var target = event.target; - if (this._addons.indexOf(target) === -1) { - this._addons.push(target); - if (this.isAttached) { - this._handleValue(this._inputElement); - } - } - }, - _onFocus: function() { - this._setFocused(true); - }, - _onBlur: function() { - this._setFocused(false); - this._handleValueAndAutoValidate(this._inputElement); - }, - _onInput: function(event) { - this._handleValueAndAutoValidate(event.target); - }, - _onValueChanged: function(event) { - this._handleValueAndAutoValidate(event.target); - }, - _handleValue: function(inputElement) { - var value = this._inputElementValue; - if (value || value === 0 || inputElement.type === 'number' && !inputElement.checkValidity()) { - this._inputHasContent = true; - } else { - this._inputHasContent = false; - } - this.updateAddons({ - inputElement: inputElement, - value: value, - invalid: this.invalid - }); - }, - _handleValueAndAutoValidate: function(inputElement) { - if (this.autoValidate) { - var valid; - if (inputElement.validate) { - valid = inputElement.validate(this._inputElementValue); - } else { - valid = inputElement.checkValidity(); - } - this.invalid = !valid; - } - this._handleValue(inputElement); - }, - _onIronInputValidate: function(event) { - this.invalid = this._inputElement.invalid; - }, - _invalidChanged: function() { - if (this._addons) { - this.updateAddons({ - invalid: this.invalid - }); - } - }, - updateAddons: function(state) { - for (var addon, index = 0; addon = this._addons[index]; index++) { - addon.update(state); - } - }, - _computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) { - var cls = 'input-content'; - if (!noLabelFloat) { - var label = this.querySelector('label'); - if (alwaysFloatLabel || _inputHasContent) { - cls += ' label-is-floating'; - this.$.labelAndInputContainer.style.position = 'static'; - if (invalid) { - cls += ' is-invalid'; - } else if (focused) { - cls += " label-is-highlighted"; - } - } else { - if (label) { - this.$.labelAndInputContainer.style.position = 'relative'; - } - } - } else { - if (_inputHasContent) { - cls += ' label-is-hidden'; - } - } - return cls; - }, - _computeUnderlineClass: function(focused, invalid) { - var cls = 'underline'; - if (invalid) { - cls += ' is-invalid'; - } else if (focused) { - cls += ' is-highlighted'; - } - return cls; - }, - _computeAddOnContentClass: function(focused, invalid) { - var cls = 'add-on-content'; - if (invalid) { - cls += ' is-invalid'; - } else if (focused) { - cls += ' is-highlighted'; - } - return cls; - } -}); - -Polymer.PaperSpinnerBehavior = { - listeners: { - animationend: '__reset', - webkitAnimationEnd: '__reset' - }, - properties: { - active: { - type: Boolean, - value: false, - reflectToAttribute: true, - observer: '__activeChanged' - }, - alt: { - type: String, - value: 'loading', - observer: '__altChanged' - }, - __coolingDown: { - type: Boolean, - value: false - } - }, - __computeContainerClasses: function(active, coolingDown) { - return [ active || coolingDown ? 'active' : '', coolingDown ? 'cooldown' : '' ].join(' '); - }, - __activeChanged: function(active, old) { - this.__setAriaHidden(!active); - this.__coolingDown = !active && old; - }, - __altChanged: function(alt) { - if (alt === this.getPropertyInfo('alt').value) { - this.alt = this.getAttribute('aria-label') || alt; - } else { - this.__setAriaHidden(alt === ''); - this.setAttribute('aria-label', alt); - } - }, - __setAriaHidden: function(hidden) { - var attr = 'aria-hidden'; - if (hidden) { - this.setAttribute(attr, 'true'); - } else { - this.removeAttribute(attr); - } - }, - __reset: function() { - this.active = false; - this.__coolingDown = false; - } -}; - -Polymer({ - is: 'paper-spinner-lite', - behaviors: [ Polymer.PaperSpinnerBehavior ] -}); - +Polymer({is:"cr-lazy-render","extends":"template",behaviors:[Polymer.Templatizer],child_:null,get:function(){if(!this.child_)this.render_();return this.child_},getIfExists:function(){return this.child_},render_:function(){if(!this.ctor)this.templatize(this);var parentNode=this.parentNode;if(parentNode&&!this.child_){var instance=this.stamp({});this.child_=instance.root.firstElementChild;parentNode.insertBefore(instance.root,this)}},_forwardParentProp:function(prop,value){if(this.child_)this.child_._templateInstance[prop]=value},_forwardParentPath:function(path,value){if(this.child_)this.child_._templateInstance.notifyPath(path,value,true)}});(function(){"use strict";Polymer.IronA11yAnnouncer=Polymer({is:"iron-a11y-announcer",properties:{mode:{type:String,value:"polite"},_text:{type:String,value:""}},created:function(){if(!Polymer.IronA11yAnnouncer.instance){Polymer.IronA11yAnnouncer.instance=this}document.body.addEventListener("iron-announce",this._onIronAnnounce.bind(this))},announce:function(text){this._text="";this.async(function(){this._text=text},100)},_onIronAnnounce:function(event){if(event.detail&&event.detail.text){this.announce(event.detail.text)}}});Polymer.IronA11yAnnouncer.instance=null;Polymer.IronA11yAnnouncer.requestAvailability=function(){if(!Polymer.IronA11yAnnouncer.instance){Polymer.IronA11yAnnouncer.instance=document.createElement("iron-a11y-announcer")}document.body.appendChild(Polymer.IronA11yAnnouncer.instance)}})();Polymer.IronValidatableBehaviorMeta=null;Polymer.IronValidatableBehavior={properties:{validator:{type:String},invalid:{notify:true,reflectToAttribute:true,type:Boolean,value:false},_validatorMeta:{type:Object},validatorType:{type:String,value:"validator"},_validator:{type:Object,computed:"__computeValidator(validator)"}},observers:["_invalidChanged(invalid)"],registered:function(){Polymer.IronValidatableBehaviorMeta=new Polymer.IronMeta({type:"validator"})},_invalidChanged:function(){if(this.invalid){this.setAttribute("aria-invalid","true")}else{this.removeAttribute("aria-invalid")}},hasValidator:function(){return this._validator!=null},validate:function(value){this.invalid=!this._getValidity(value);return!this.invalid},_getValidity:function(value){if(this.hasValidator()){return this._validator.validate(value)}return true},__computeValidator:function(){return Polymer.IronValidatableBehaviorMeta&&Polymer.IronValidatableBehaviorMeta.byKey(this.validator)}};Polymer({is:"iron-input","extends":"input",behaviors:[Polymer.IronValidatableBehavior],properties:{bindValue:{observer:"_bindValueChanged",type:String},preventInvalidInput:{type:Boolean},allowedPattern:{type:String,observer:"_allowedPatternChanged"},_previousValidInput:{type:String,value:""},_patternAlreadyChecked:{type:Boolean,value:false}},listeners:{input:"_onInput",keypress:"_onKeypress"},registered:function(){if(!this._canDispatchEventOnDisabled()){this._origDispatchEvent=this.dispatchEvent;this.dispatchEvent=this._dispatchEventFirefoxIE}},created:function(){Polymer.IronA11yAnnouncer.requestAvailability()},_canDispatchEventOnDisabled:function(){var input=document.createElement("input");var canDispatch=false;input.disabled=true;input.addEventListener("feature-check-dispatch-event",function(){canDispatch=true});try{input.dispatchEvent(new Event("feature-check-dispatch-event"))}catch(e){}return canDispatch},_dispatchEventFirefoxIE:function(){var disabled=this.disabled;this.disabled=false;this._origDispatchEvent.apply(this,arguments);this.disabled=disabled},get _patternRegExp(){var pattern;if(this.allowedPattern){pattern=new RegExp(this.allowedPattern)}else{switch(this.type){case"number":pattern=/[0-9.,e-]/;break}}return pattern},ready:function(){this.bindValue=this.value},_bindValueChanged:function(){if(this.value!==this.bindValue){this.value=!(this.bindValue||this.bindValue===0||this.bindValue===false)?"":this.bindValue}this.fire("bind-value-changed",{value:this.bindValue})},_allowedPatternChanged:function(){this.preventInvalidInput=this.allowedPattern?true:false},_onInput:function(){if(this.preventInvalidInput&&!this._patternAlreadyChecked){var valid=this._checkPatternValidity();if(!valid){this._announceInvalidCharacter("Invalid string of characters not entered.");this.value=this._previousValidInput}}this.bindValue=this.value;this._previousValidInput=this.value;this._patternAlreadyChecked=false},_isPrintable:function(event){var anyNonPrintable=event.keyCode==8||event.keyCode==9||event.keyCode==13||event.keyCode==27;var mozNonPrintable=event.keyCode==19||event.keyCode==20||event.keyCode==45||event.keyCode==46||event.keyCode==144||event.keyCode==145||event.keyCode>32&&event.keyCode<41||event.keyCode>111&&event.keyCode<124;return!anyNonPrintable&&!(event.charCode==0&&mozNonPrintable)},_onKeypress:function(event){if(!this.preventInvalidInput&&this.type!=="number"){return}var regexp=this._patternRegExp;if(!regexp){return}if(event.metaKey||event.ctrlKey||event.altKey)return;this._patternAlreadyChecked=true;var thisChar=String.fromCharCode(event.charCode);if(this._isPrintable(event)&&!regexp.test(thisChar)){event.preventDefault();this._announceInvalidCharacter("Invalid character "+thisChar+" not entered.")}},_checkPatternValidity:function(){var regexp=this._patternRegExp;if(!regexp){return true}for(var i=0;i<this.value.length;i++){if(!regexp.test(this.value[i])){return false}}return true},validate:function(){var valid=this.checkValidity();if(valid){if(this.required&&this.value===""){valid=false}else if(this.hasValidator()){valid=Polymer.IronValidatableBehavior.validate.call(this,this.value)}}this.invalid=!valid;this.fire("iron-input-validate");return valid},_announceInvalidCharacter:function(message){this.fire("iron-announce",{text:message})}});Polymer({is:"paper-input-container",properties:{noLabelFloat:{type:Boolean,value:false},alwaysFloatLabel:{type:Boolean,value:false},attrForValue:{type:String,value:"bind-value"},autoValidate:{type:Boolean,value:false},invalid:{observer:"_invalidChanged",type:Boolean,value:false},focused:{readOnly:true,type:Boolean,value:false,notify:true},_addons:{type:Array},_inputHasContent:{type:Boolean,value:false},_inputSelector:{type:String,value:"input,textarea,.paper-input-input"},_boundOnFocus:{type:Function,value:function(){return this._onFocus.bind(this)}},_boundOnBlur:{type:Function,value:function(){return this._onBlur.bind(this)}},_boundOnInput:{type:Function,value:function(){return this._onInput.bind(this)}},_boundValueChanged:{type:Function,value:function(){return this._onValueChanged.bind(this)}}},listeners:{"addon-attached":"_onAddonAttached","iron-input-validate":"_onIronInputValidate"},get _valueChangedEvent(){return this.attrForValue+"-changed"},get _propertyForValue(){return Polymer.CaseMap.dashToCamelCase(this.attrForValue)},get _inputElement(){return Polymer.dom(this).querySelector(this._inputSelector)},get _inputElementValue(){return this._inputElement[this._propertyForValue]||this._inputElement.value},ready:function(){if(!this._addons){this._addons=[]}this.addEventListener("focus",this._boundOnFocus,true);this.addEventListener("blur",this._boundOnBlur,true)},attached:function(){if(this.attrForValue){this._inputElement.addEventListener(this._valueChangedEvent,this._boundValueChanged)}else{this.addEventListener("input",this._onInput)}if(this._inputElementValue!=""){this._handleValueAndAutoValidate(this._inputElement)}else{this._handleValue(this._inputElement)}},_onAddonAttached:function(event){if(!this._addons){this._addons=[]}var target=event.target;if(this._addons.indexOf(target)===-1){this._addons.push(target);if(this.isAttached){this._handleValue(this._inputElement)}}},_onFocus:function(){this._setFocused(true)},_onBlur:function(){this._setFocused(false);this._handleValueAndAutoValidate(this._inputElement)},_onInput:function(event){this._handleValueAndAutoValidate(event.target)},_onValueChanged:function(event){this._handleValueAndAutoValidate(event.target)},_handleValue:function(inputElement){var value=this._inputElementValue;if(value||value===0||inputElement.type==="number"&&!inputElement.checkValidity()){this._inputHasContent=true}else{this._inputHasContent=false}this.updateAddons({inputElement:inputElement,value:value,invalid:this.invalid})},_handleValueAndAutoValidate:function(inputElement){if(this.autoValidate){var valid;if(inputElement.validate){valid=inputElement.validate(this._inputElementValue)}else{valid=inputElement.checkValidity()}this.invalid=!valid}this._handleValue(inputElement)},_onIronInputValidate:function(event){this.invalid=this._inputElement.invalid},_invalidChanged:function(){if(this._addons){this.updateAddons({invalid:this.invalid})}},updateAddons:function(state){for(var addon,index=0;addon=this._addons[index];index++){addon.update(state)}},_computeInputContentClass:function(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent){var cls="input-content";if(!noLabelFloat){var label=this.querySelector("label");if(alwaysFloatLabel||_inputHasContent){cls+=" label-is-floating";this.$.labelAndInputContainer.style.position="static";if(invalid){cls+=" is-invalid"}else if(focused){cls+=" label-is-highlighted"}}else{if(label){this.$.labelAndInputContainer.style.position="relative"}}}else{if(_inputHasContent){cls+=" label-is-hidden"}}return cls},_computeUnderlineClass:function(focused,invalid){var cls="underline";if(invalid){cls+=" is-invalid"}else if(focused){cls+=" is-highlighted"}return cls},_computeAddOnContentClass:function(focused,invalid){var cls="add-on-content";if(invalid){cls+=" is-invalid"}else if(focused){cls+=" is-highlighted"}return cls}});Polymer.PaperSpinnerBehavior={listeners:{animationend:"__reset",webkitAnimationEnd:"__reset"},properties:{active:{type:Boolean,value:false,reflectToAttribute:true,observer:"__activeChanged"},alt:{type:String,value:"loading",observer:"__altChanged"},__coolingDown:{type:Boolean,value:false}},__computeContainerClasses:function(active,coolingDown){return[active||coolingDown?"active":"",coolingDown?"cooldown":""].join(" ")},__activeChanged:function(active,old){this.__setAriaHidden(!active);this.__coolingDown=!active&&old},__altChanged:function(alt){if(alt===this.getPropertyInfo("alt").value){this.alt=this.getAttribute("aria-label")||alt}else{this.__setAriaHidden(alt==="");this.setAttribute("aria-label",alt)}},__setAriaHidden:function(hidden){var attr="aria-hidden";if(hidden){this.setAttribute(attr,"true")}else{this.removeAttribute(attr)}},__reset:function(){this.active=false;this.__coolingDown=false}};Polymer({is:"paper-spinner-lite",behaviors:[Polymer.PaperSpinnerBehavior]}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var CrSearchFieldBehavior = { - properties: { - label: { - type: String, - value: '' - }, - clearLabel: { - type: String, - value: '' - }, - showingSearch: { - type: Boolean, - value: false, - notify: true, - observer: 'showingSearchChanged_', - reflectToAttribute: true - }, - lastValue_: { - type: String, - value: '' - } - }, - getSearchInput: function() {}, - getValue: function() { - return this.getSearchInput().value; - }, - setValue: function(value) { - this.getSearchInput().bindValue = value; - this.onValueChanged_(value); - }, - showAndFocus: function() { - this.showingSearch = true; - this.focus_(); - }, - focus_: function() { - this.getSearchInput().focus(); - }, - onSearchTermSearch: function() { - this.onValueChanged_(this.getValue()); - }, - onValueChanged_: function(newValue) { - if (newValue == this.lastValue_) return; - this.fire('search-changed', newValue); - this.lastValue_ = newValue; - }, - onSearchTermKeydown: function(e) { - if (e.key == 'Escape') this.showingSearch = false; - }, - showingSearchChanged_: function() { - if (this.showingSearch) { - this.focus_(); - return; - } - this.setValue(''); - this.getSearchInput().blur(); - } -}; - +var CrSearchFieldBehavior={properties:{label:{type:String,value:""},clearLabel:{type:String,value:""},showingSearch:{type:Boolean,value:false,notify:true,observer:"showingSearchChanged_",reflectToAttribute:true},lastValue_:{type:String,value:""}},getSearchInput:function(){},getValue:function(){return this.getSearchInput().value},setValue:function(value){this.getSearchInput().bindValue=value;this.onValueChanged_(value)},showAndFocus:function(){this.showingSearch=true;this.focus_()},focus_:function(){this.getSearchInput().focus()},onSearchTermSearch:function(){this.onValueChanged_(this.getValue())},onValueChanged_:function(newValue){if(newValue==this.lastValue_)return;this.fire("search-changed",newValue);this.lastValue_=newValue},onSearchTermKeydown:function(e){if(e.key=="Escape")this.showingSearch=false},showingSearchChanged_:function(){if(this.showingSearch){this.focus_();return}this.setValue("");this.getSearchInput().blur()}}; // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'cr-toolbar-search-field', - behaviors: [ CrSearchFieldBehavior ], - properties: { - narrow: { - type: Boolean, - reflectToAttribute: true - }, - label: String, - clearLabel: String, - spinnerActive: { - type: Boolean, - reflectToAttribute: true - }, - hasSearchText_: Boolean, - isSpinnerShown_: { - type: Boolean, - computed: 'computeIsSpinnerShown_(spinnerActive, showingSearch)' - } - }, - listeners: { - tap: 'showSearch_', - 'searchInput.bind-value-changed': 'onBindValueChanged_' - }, - getSearchInput: function() { - return this.$.searchInput; - }, - isSearchFocused: function() { - return this.$.searchTerm.focused; - }, - computeIconTabIndex_: function(narrow) { - return narrow ? 0 : -1; - }, - computeIsSpinnerShown_: function() { - return this.spinnerActive && this.showingSearch; - }, - onInputBlur_: function() { - if (!this.hasSearchText_) this.showingSearch = false; - }, - onBindValueChanged_: function() { - var newValue = this.$.searchInput.bindValue; - this.hasSearchText_ = newValue != ''; - if (newValue != '') this.showingSearch = true; - }, - showSearch_: function(e) { - if (e.target != this.$.clearSearch) this.showingSearch = true; - }, - hideSearch_: function(e) { - this.showingSearch = false; - e.stopPropagation(); - } -}); - +Polymer({is:"cr-toolbar-search-field",behaviors:[CrSearchFieldBehavior],properties:{narrow:{type:Boolean,reflectToAttribute:true},label:String,clearLabel:String,spinnerActive:{type:Boolean,reflectToAttribute:true},hasSearchText_:Boolean,isSpinnerShown_:{type:Boolean,computed:"computeIsSpinnerShown_(spinnerActive, showingSearch)"}},listeners:{tap:"showSearch_","searchInput.bind-value-changed":"onBindValueChanged_"},getSearchInput:function(){return this.$.searchInput},isSearchFocused:function(){return this.$.searchTerm.focused},computeIconTabIndex_:function(narrow){return narrow?0:-1},computeIsSpinnerShown_:function(){return this.spinnerActive&&this.showingSearch},onInputBlur_:function(){if(!this.hasSearchText_)this.showingSearch=false},onBindValueChanged_:function(){var newValue=this.$.searchInput.bindValue;this.hasSearchText_=newValue!="";if(newValue!="")this.showingSearch=true},showSearch_:function(e){if(e.target!=this.$.clearSearch)this.showingSearch=true},hideSearch_:function(e){this.showingSearch=false;e.stopPropagation()}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'cr-toolbar', - properties: { - pageName: String, - searchPrompt: String, - clearLabel: String, - menuLabel: String, - menuPromo: String, - spinnerActive: Boolean, - showMenu: { - type: Boolean, - value: false - }, - showMenuPromo: { - type: Boolean, - value: false - }, - closeMenuPromo: String, - narrow_: { - type: Boolean, - reflectToAttribute: true - }, - showingSearch_: { - type: Boolean, - reflectToAttribute: true - } - }, - observers: [ 'possiblyShowMenuPromo_(showMenu, showMenuPromo, showingSearch_)' ], - getSearchField: function() { - return this.$.search; - }, - onClosePromoTap_: function() { - this.showMenuPromo = false; - }, - onMenuTap_: function() { - this.fire('cr-menu-tap'); - this.onClosePromoTap_(); - }, - possiblyShowMenuPromo_: function() { - Polymer.RenderStatus.afterNextRender(this, function() { - if (this.showMenu && this.showMenuPromo && !this.showingSearch_) { - this.$$('#menuPromo').animate({ - opacity: [ 0, .9 ] - }, { - duration: 500, - fill: 'forwards' - }); - this.fire('cr-menu-promo-shown'); - } - }.bind(this)); - }, - titleIfNotShowMenuPromo_: function(title, showMenuPromo) { - return showMenuPromo ? '' : title; - } -}); - +Polymer({is:"cr-toolbar",properties:{pageName:String,searchPrompt:String,clearLabel:String,menuLabel:String,menuPromo:String,spinnerActive:Boolean,showMenu:{type:Boolean,value:false},showMenuPromo:{type:Boolean,value:false},closeMenuPromo:String,narrow_:{type:Boolean,reflectToAttribute:true},showingSearch_:{type:Boolean,reflectToAttribute:true}},observers:["possiblyShowMenuPromo_(showMenu, showMenuPromo, showingSearch_)"],getSearchField:function(){return this.$.search},onClosePromoTap_:function(){this.showMenuPromo=false},onMenuTap_:function(){this.fire("cr-menu-tap");this.onClosePromoTap_()},possiblyShowMenuPromo_:function(){Polymer.RenderStatus.afterNextRender(this,function(){if(this.showMenu&&this.showMenuPromo&&!this.showingSearch_){this.$$("#menuPromo").animate({opacity:[0,.9]},{duration:500,fill:"forwards"});this.fire("cr-menu-promo-shown")}}.bind(this))},titleIfNotShowMenuPromo_:function(title,showMenuPromo){return showMenuPromo?"":title}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -cr.define('md_history', function() { - function BrowserService() { - this.pendingDeleteItems_ = null; - this.pendingDeletePromise_ = null; - } - BrowserService.prototype = { - deleteItems: function(items) { - if (this.pendingDeleteItems_ != null) { - return new Promise(function(resolve, reject) { - reject(items); - }); - } - var removalList = items.map(function(item) { - return { - url: item.url, - timestamps: item.allTimestamps - }; - }); - this.pendingDeleteItems_ = items; - this.pendingDeletePromise_ = new PromiseResolver(); - chrome.send('removeVisits', removalList); - return this.pendingDeletePromise_.promise; - }, - removeBookmark: function(url) { - chrome.send('removeBookmark', [ url ]); - }, - openForeignSessionAllTabs: function(sessionTag) { - chrome.send('openForeignSession', [ sessionTag ]); - }, - openForeignSessionTab: function(sessionTag, windowId, tabId, e) { - chrome.send('openForeignSession', [ sessionTag, String(windowId), String(tabId), e.button || 0, e.altKey, e.ctrlKey, e.metaKey, e.shiftKey ]); - }, - deleteForeignSession: function(sessionTag) { - chrome.send('deleteForeignSession', [ sessionTag ]); - }, - openClearBrowsingData: function() { - chrome.send('clearBrowsingData'); - }, - recordHistogram: function(histogram, value, max) { - chrome.send('metricsHandler:recordInHistogram', [ histogram, value, max ]); - }, - recordAction: function(action) { - if (action.indexOf('_') == -1) action = 'HistoryPage_' + action; - chrome.send('metricsHandler:recordAction', [ action ]); - }, - resolveDelete_: function(successful) { - if (this.pendingDeleteItems_ == null || this.pendingDeletePromise_ == null) { - return; - } - if (successful) this.pendingDeletePromise_.resolve(this.pendingDeleteItems_); else this.pendingDeletePromise_.reject(this.pendingDeleteItems_); - this.pendingDeleteItems_ = null; - this.pendingDeletePromise_ = null; - }, - menuPromoShown: function() { - chrome.send('menuPromoShown'); - } - }; - cr.addSingletonGetter(BrowserService); - return { - BrowserService: BrowserService - }; -}); - -function deleteComplete() { - md_history.BrowserService.getInstance().resolveDelete_(true); -} - -function deleteFailed() { - md_history.BrowserService.getInstance().resolveDelete_(false); -} - +cr.define("md_history",function(){function BrowserService(){this.pendingDeleteItems_=null;this.pendingDeletePromise_=null}BrowserService.prototype={deleteItems:function(items){if(this.pendingDeleteItems_!=null){return new Promise(function(resolve,reject){reject(items)})}var removalList=items.map(function(item){return{url:item.url,timestamps:item.allTimestamps}});this.pendingDeleteItems_=items;this.pendingDeletePromise_=new PromiseResolver;chrome.send("removeVisits",removalList);return this.pendingDeletePromise_.promise},removeBookmark:function(url){chrome.send("removeBookmark",[url])},openForeignSessionAllTabs:function(sessionTag){chrome.send("openForeignSession",[sessionTag])},openForeignSessionTab:function(sessionTag,windowId,tabId,e){chrome.send("openForeignSession",[sessionTag,String(windowId),String(tabId),e.button||0,e.altKey,e.ctrlKey,e.metaKey,e.shiftKey])},deleteForeignSession:function(sessionTag){chrome.send("deleteForeignSession",[sessionTag])},openClearBrowsingData:function(){chrome.send("clearBrowsingData")},recordHistogram:function(histogram,value,max){chrome.send("metricsHandler:recordInHistogram",[histogram,value,max])},recordAction:function(action){if(action.indexOf("_")==-1)action="HistoryPage_"+action;chrome.send("metricsHandler:recordAction",[action])},resolveDelete_:function(successful){if(this.pendingDeleteItems_==null||this.pendingDeletePromise_==null){return}if(successful)this.pendingDeletePromise_.resolve(this.pendingDeleteItems_);else this.pendingDeletePromise_.reject(this.pendingDeleteItems_);this.pendingDeleteItems_=null;this.pendingDeletePromise_=null},menuPromoShown:function(){chrome.send("menuPromoShown")}};cr.addSingletonGetter(BrowserService);return{BrowserService:BrowserService}});function deleteComplete(){md_history.BrowserService.getInstance().resolveDelete_(true)}function deleteFailed(){md_history.BrowserService.getInstance().resolveDelete_(false)} // 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. -Polymer({ - is: 'history-toolbar', - properties: { - count: { - type: Number, - value: 0, - observer: 'changeToolbarView_' - }, - itemsSelected_: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - searchTerm: { - type: String, - observer: 'searchTermChanged_', - notify: true - }, - spinnerActive: { - type: Boolean, - value: false - }, - hasDrawer: { - type: Boolean, - observer: 'hasDrawerChanged_', - reflectToAttribute: true - }, - showSyncNotice: Boolean, - isGroupedMode: { - type: Boolean, - reflectToAttribute: true - }, - groupedRange: { - type: Number, - value: 0, - reflectToAttribute: true, - notify: true - }, - queryStartTime: String, - queryEndTime: String, - showMenuPromo_: { - type: Boolean, - value: function() { - return loadTimeData.getBoolean('showMenuPromo'); - } - } - }, - get searchField() { - return this.$['main-toolbar'].getSearchField(); - }, - showSearchField: function() { - this.searchField.showAndFocus(); - }, - changeToolbarView_: function() { - this.itemsSelected_ = this.count > 0; - }, - searchTermChanged_: function() { - if (this.searchField.getValue() != this.searchTerm) { - this.searchField.showAndFocus(); - this.searchField.setValue(this.searchTerm); - } - }, - onMenuPromoShown_: function() { - md_history.BrowserService.getInstance().menuPromoShown(); - }, - onSearchChanged_: function(event) { - this.searchTerm = event.detail; - }, - onInfoButtonTap_: function() { - var dropdown = this.$.syncNotice.get(); - dropdown.positionTarget = this.$$('#info-button-icon'); - if (dropdown.style.display == 'none') dropdown.open(); - }, - onClearSelectionTap_: function() { - this.fire('unselect-all'); - }, - onDeleteTap_: function() { - this.fire('delete-selected'); - }, - deletingAllowed_: function() { - return loadTimeData.getBoolean('allowDeletingHistory'); - }, - numberOfItemsSelected_: function(count) { - return count > 0 ? loadTimeData.getStringF('itemsSelected', count) : ''; - }, - getHistoryInterval_: function(queryStartTime, queryEndTime) { - return loadTimeData.getStringF('historyInterval', queryStartTime, queryEndTime); - }, - hasDrawerChanged_: function() { - this.updateStyles(); - } -}); - -(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 = 3; - var HIDDEN_Y = '-10000px'; - var DEFAULT_GRID_SIZE = 200; - var SECRET_TABINDEX = -100; - Polymer({ - is: 'iron-list', - properties: { - items: { - type: Array - }, - maxPhysicalCount: { - type: Number, - value: 500 - }, - as: { - type: String, - value: 'item' - }, - indexAs: { - type: String, - value: 'index' - }, - selectedAs: { - type: String, - value: 'selected' - }, - grid: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - selectionEnabled: { - type: Boolean, - value: false - }, - selectedItem: { - type: Object, - notify: true - }, - selectedItems: { - type: Object, - notify: true - }, - multiSelection: { - type: Boolean, - value: false - } - }, - observers: [ '_itemsChanged(items.*)', '_selectionEnabledChanged(selectionEnabled)', '_multiSelectionChanged(multiSelection)', '_setOverflow(scrollTarget)' ], - behaviors: [ Polymer.Templatizer, Polymer.IronResizableBehavior, Polymer.IronA11yKeysBehavior, Polymer.IronScrollTargetBehavior ], - keyBindings: { - up: '_didMoveUp', - down: '_didMoveDown', - enter: '_didEnter' - }, - _ratio: .5, - _scrollerPaddingTop: 0, - _scrollPosition: 0, - _physicalSize: 0, - _physicalAverage: 0, - _physicalAverageCount: 0, - _physicalTop: 0, - _virtualCount: 0, - _physicalIndexForKey: null, - _estScrollHeight: 0, - _scrollHeight: 0, - _viewportHeight: 0, - _viewportWidth: 0, - _physicalItems: null, - _physicalSizes: null, - _firstVisibleIndexVal: null, - _lastVisibleIndexVal: null, - _collection: null, - _maxPages: 3, - _focusedItem: null, - _focusedIndex: -1, - _offscreenFocusedItem: null, - _focusBackfillItem: null, - _itemsPerRow: 1, - _itemWidth: 0, - _rowHeight: 0, - _templateCost: 0, - get _physicalBottom() { - return this._physicalTop + this._physicalSize; - }, - get _scrollBottom() { - return this._scrollPosition + this._viewportHeight; - }, - get _virtualEnd() { - return this._virtualStart + this._physicalCount - 1; - }, - get _hiddenContentSize() { - var size = this.grid ? this._physicalRows * this._rowHeight : this._physicalSize; - return size - this._viewportHeight; - }, - get _maxScrollTop() { - return this._estScrollHeight - this._viewportHeight + this._scrollerPaddingTop; - }, - _minVirtualStart: 0, - get _maxVirtualStart() { - return Math.max(0, this._virtualCount - this._physicalCount); - }, - _virtualStartVal: 0, - set _virtualStart(val) { - this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val)); - }, - get _virtualStart() { - return this._virtualStartVal || 0; - }, - _physicalStartVal: 0, - set _physicalStart(val) { - this._physicalStartVal = val % this._physicalCount; - if (this._physicalStartVal < 0) { - this._physicalStartVal = this._physicalCount + this._physicalStartVal; - } - this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount; - }, - get _physicalStart() { - return this._physicalStartVal || 0; - }, - _physicalCountVal: 0, - set _physicalCount(val) { - this._physicalCountVal = val; - this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount; - }, - get _physicalCount() { - return this._physicalCountVal; - }, - _physicalEnd: 0, - get _optPhysicalSize() { - if (this.grid) { - return this._estRowsInView * this._rowHeight * this._maxPages; - } - return this._viewportHeight * this._maxPages; - }, - get _optPhysicalCount() { - return this._estRowsInView * this._itemsPerRow * this._maxPages; - }, - get _isVisible() { - return Boolean(this.offsetWidth || this.offsetHeight); - }, - get firstVisibleIndex() { - if (this._firstVisibleIndexVal === null) { - var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddingTop); - this._firstVisibleIndexVal = this._iterateItems(function(pidx, vidx) { - physicalOffset += this._getPhysicalSizeIncrement(pidx); - if (physicalOffset > this._scrollPosition) { - return this.grid ? vidx - vidx % this._itemsPerRow : vidx; - } - if (this.grid && this._virtualCount - 1 === vidx) { - return vidx - vidx % this._itemsPerRow; - } - }) || 0; - } - return this._firstVisibleIndexVal; - }, - get lastVisibleIndex() { - if (this._lastVisibleIndexVal === null) { - if (this.grid) { - var lastIndex = this.firstVisibleIndex + this._estRowsInView * this._itemsPerRow - 1; - this._lastVisibleIndexVal = Math.min(this._virtualCount, lastIndex); - } else { - var physicalOffset = this._physicalTop; - this._iterateItems(function(pidx, vidx) { - if (physicalOffset < this._scrollBottom) { - this._lastVisibleIndexVal = vidx; - } else { - return true; - } - physicalOffset += this._getPhysicalSizeIncrement(pidx); - }); - } - } - return this._lastVisibleIndexVal; - }, - get _defaultScrollTarget() { - return this; - }, - get _virtualRowCount() { - return Math.ceil(this._virtualCount / this._itemsPerRow); - }, - get _estRowsInView() { - return Math.ceil(this._viewportHeight / this._rowHeight); - }, - get _physicalRows() { - return Math.ceil(this._physicalCount / this._itemsPerRow); - }, - ready: function() { - this.addEventListener('focus', this._didFocus.bind(this), true); - }, - attached: function() { - this.updateViewportBoundaries(); - if (this._physicalCount === 0) { - this._debounceTemplate(this._render); - } - this.listen(this, 'iron-resize', '_resizeHandler'); - }, - detached: function() { - this.unlisten(this, 'iron-resize', '_resizeHandler'); - }, - _setOverflow: function(scrollTarget) { - this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; - this.style.overflow = scrollTarget === this ? 'auto' : ''; - }, - updateViewportBoundaries: function() { - this._scrollerPaddingTop = this.scrollTarget === this ? 0 : parseInt(window.getComputedStyle(this)['padding-top'], 10); - this._viewportHeight = this._scrollTargetHeight; - if (this.grid) { - this._updateGridMetrics(); - } - }, - _scrollHandler: function() { - var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop)); - var delta = scrollTop - this._scrollPosition; - var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBottom; - var ratio = this._ratio; - var recycledTiles = 0; - var hiddenContentSize = this._hiddenContentSize; - var currentRatio = ratio; - var movingUp = []; - this._scrollPosition = scrollTop; - this._firstVisibleIndexVal = null; - this._lastVisibleIndexVal = null; - scrollBottom = this._scrollBottom; - physicalBottom = this._physicalBottom; - if (Math.abs(delta) > this._physicalSize) { - this._physicalTop += delta; - recycledTiles = Math.round(delta / this._physicalAverage); - } else if (delta < 0) { - var topSpace = scrollTop - this._physicalTop; - var virtualStart = this._virtualStart; - recycledTileSet = []; - kth = this._physicalEnd; - currentRatio = topSpace / hiddenContentSize; - while (currentRatio < ratio && recycledTiles < this._physicalCount && virtualStart - recycledTiles > 0 && physicalBottom - this._getPhysicalSizeIncrement(kth) > scrollBottom) { - tileHeight = this._getPhysicalSizeIncrement(kth); - currentRatio += tileHeight / hiddenContentSize; - physicalBottom -= tileHeight; - recycledTileSet.push(kth); - recycledTiles++; - kth = kth === 0 ? this._physicalCount - 1 : kth - 1; - } - movingUp = recycledTileSet; - recycledTiles = -recycledTiles; - } else if (delta > 0) { - var bottomSpace = physicalBottom - scrollBottom; - var virtualEnd = this._virtualEnd; - var lastVirtualItemIndex = this._virtualCount - 1; - recycledTileSet = []; - kth = this._physicalStart; - currentRatio = bottomSpace / hiddenContentSize; - while (currentRatio < ratio && recycledTiles < this._physicalCount && virtualEnd + recycledTiles < lastVirtualItemIndex && this._physicalTop + this._getPhysicalSizeIncrement(kth) < scrollTop) { - tileHeight = this._getPhysicalSizeIncrement(kth); - currentRatio += tileHeight / hiddenContentSize; - this._physicalTop += tileHeight; - recycledTileSet.push(kth); - recycledTiles++; - kth = (kth + 1) % this._physicalCount; - } - } - if (recycledTiles === 0) { - if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { - this._increasePoolIfNeeded(); - } - } else { - this._virtualStart = this._virtualStart + recycledTiles; - this._physicalStart = this._physicalStart + recycledTiles; - this._update(recycledTileSet, movingUp); - } - }, - _update: function(itemSet, movingUp) { - this._manageFocus(); - this._assignModels(itemSet); - this._updateMetrics(itemSet); - if (movingUp) { - while (movingUp.length) { - var idx = movingUp.pop(); - this._physicalTop -= this._getPhysicalSizeIncrement(idx); - } - } - this._positionItems(); - this._updateScrollerSize(); - this._increasePoolIfNeeded(); - }, - _createPool: function(size) { - var physicalItems = new Array(size); - this._ensureTemplatized(); - for (var i = 0; i < size; i++) { - var inst = this.stamp(null); - physicalItems[i] = inst.root.querySelector('*'); - Polymer.dom(this).appendChild(inst.root); - } - return physicalItems; - }, - _increasePoolIfNeeded: function() { - if (this._viewportHeight === 0) { - return false; - } - var self = this; - var isClientFull = this._physicalBottom >= this._scrollBottom && this._physicalTop <= this._scrollPosition; - if (this._physicalSize >= this._optPhysicalSize && isClientFull) { - return false; - } - var maxPoolSize = Math.round(this._physicalCount * .5); - if (!isClientFull) { - this._debounceTemplate(this._increasePool.bind(this, maxPoolSize)); - return true; - } - this._yield(function() { - self._increasePool(Math.min(maxPoolSize, Math.max(1, Math.round(50 / self._templateCost)))); - }); - return true; - }, - _yield: function(cb) { - var g = window; - var handle = g.requestIdleCallback ? g.requestIdleCallback(cb) : g.setTimeout(cb, 16); - Polymer.dom.addDebouncer({ - complete: function() { - g.cancelIdleCallback ? g.cancelIdleCallback(handle) : g.clearTimeout(handle); - cb(); - } - }); - }, - _increasePool: function(missingItems) { - var nextPhysicalCount = Math.min(this._physicalCount + missingItems, this._virtualCount - this._virtualStart, Math.max(this.maxPhysicalCount, DEFAULT_PHYSICAL_COUNT)); - var prevPhysicalCount = this._physicalCount; - var delta = nextPhysicalCount - prevPhysicalCount; - var ts = window.performance.now(); - if (delta <= 0) { - return; - } - [].push.apply(this._physicalItems, this._createPool(delta)); - [].push.apply(this._physicalSizes, new Array(delta)); - this._physicalCount = prevPhysicalCount + delta; - if (this._physicalStart > this._physicalEnd && this._isIndexRendered(this._focusedIndex) && this._getPhysicalIndex(this._focusedIndex) < this._physicalEnd) { - this._physicalStart = this._physicalStart + delta; - } - this._update(); - this._templateCost = (window.performance.now() - ts) / delta; - }, - _render: function() { - if (this.isAttached && this._isVisible) { - if (this._physicalCount === 0) { - this._increasePool(DEFAULT_PHYSICAL_COUNT); - } else { - this._update(); - } - } - }, - _ensureTemplatized: function() { - if (!this.ctor) { - var props = {}; - props.__key__ = true; - props[this.as] = true; - props[this.indexAs] = true; - props[this.selectedAs] = true; - props.tabIndex = 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'); - } - } - }, - _getStampedChildren: function() { - return this._physicalItems; - }, - _forwardInstancePath: function(inst, path, value) { - if (path.indexOf(this.as + '.') === 0) { - this.notifyPath('items.' + inst.__key__ + '.' + path.slice(this.as.length + 1), value); - } - }, - _forwardParentProp: function(prop, value) { - if (this._physicalItems) { - this._physicalItems.forEach(function(item) { - item._templateInstance[prop] = value; - }, this); - } - }, - _forwardParentPath: function(path, value) { - if (this._physicalItems) { - this._physicalItems.forEach(function(item) { - item._templateInstance.notifyPath(path, value, true); - }, this); - } - }, - _forwardItemPath: function(path, value) { - if (!this._physicalIndexForKey) { - return; - } - var dot = path.indexOf('.'); - var key = path.substring(0, dot < 0 ? path.length : dot); - var idx = this._physicalIndexForKey[key]; - var offscreenItem = this._offscreenFocusedItem; - var el = offscreenItem && offscreenItem._templateInstance.__key__ === key ? offscreenItem : this._physicalItems[idx]; - if (!el || el._templateInstance.__key__ !== key) { - return; - } - if (dot >= 0) { - path = this.as + '.' + path.substring(dot + 1); - el._templateInstance.notifyPath(path, value, true); - } else { - var currentItem = el._templateInstance[this.as]; - if (Array.isArray(this.selectedItems)) { - for (var i = 0; i < this.selectedItems.length; i++) { - if (this.selectedItems[i] === currentItem) { - this.set('selectedItems.' + i, value); - break; - } - } - } else if (this.selectedItem === currentItem) { - this.set('selectedItem', value); - } - el._templateInstance[this.as] = value; - } - }, - _itemsChanged: function(change) { - if (change.path === 'items') { - this._virtualStart = 0; - this._physicalTop = 0; - this._virtualCount = this.items ? this.items.length : 0; - this._collection = this.items ? Polymer.Collection.get(this.items) : null; - this._physicalIndexForKey = {}; - this._firstVisibleIndexVal = null; - this._lastVisibleIndexVal = null; - this._physicalCount = this._physicalCount || 0; - this._physicalItems = this._physicalItems || []; - this._physicalSizes = this._physicalSizes || []; - this._physicalStart = 0; - this._resetScrollPosition(0); - this._removeFocusedItem(); - this._debounceTemplate(this._render); - } else if (change.path === 'items.splices') { - this._adjustVirtualIndex(change.value.indexSplices); - this._virtualCount = this.items ? this.items.length : 0; - this._debounceTemplate(this._render); - } else { - this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.value); - } - }, - _adjustVirtualIndex: function(splices) { - splices.forEach(function(splice) { - splice.removed.forEach(this._removeItem, this); - if (splice.index < this._virtualStart) { - var delta = Math.max(splice.addedCount - splice.removed.length, splice.index - this._virtualStart); - this._virtualStart = this._virtualStart + delta; - if (this._focusedIndex >= 0) { - this._focusedIndex = this._focusedIndex + delta; - } - } - }, this); - }, - _removeItem: function(item) { - this.$.selector.deselect(item); - if (this._focusedItem && this._focusedItem._templateInstance[this.as] === item) { - this._removeFocusedItem(); - } - }, - _iterateItems: function(fn, itemSet) { - var pidx, vidx, rtn, i; - if (arguments.length === 2 && itemSet) { - for (i = 0; i < itemSet.length; i++) { - pidx = itemSet[i]; - vidx = this._computeVidx(pidx); - if ((rtn = fn.call(this, pidx, vidx)) != null) { - return rtn; - } - } - } else { - pidx = this._physicalStart; - vidx = this._virtualStart; - for (;pidx < this._physicalCount; pidx++, vidx++) { - if ((rtn = fn.call(this, pidx, vidx)) != null) { - return rtn; - } - } - for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) { - if ((rtn = fn.call(this, pidx, vidx)) != null) { - return rtn; - } - } - } - }, - _computeVidx: function(pidx) { - if (pidx >= this._physicalStart) { - return this._virtualStart + (pidx - this._physicalStart); - } - return this._virtualStart + (this._physicalCount - this._physicalStart) + pidx; - }, - _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 != null) { - inst[this.as] = item; - inst.__key__ = this._collection.getKey(item); - inst[this.selectedAs] = this.$.selector.isSelected(item); - inst[this.indexAs] = vidx; - inst.tabIndex = this._focusedIndex === vidx ? 0 : -1; - this._physicalIndexForKey[inst.__key__] = pidx; - el.removeAttribute('hidden'); - } else { - inst.__key__ = null; - el.setAttribute('hidden', ''); - } - }, itemSet); - }, - _updateMetrics: function(itemSet) { - Polymer.dom.flush(); - var newPhysicalSize = 0; - var oldPhysicalSize = 0; - var prevAvgCount = this._physicalAverageCount; - var prevPhysicalAvg = this._physicalAverage; - 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._viewportHeight = this._scrollTargetHeight; - if (this.grid) { - this._updateGridMetrics(); - this._physicalSize = Math.ceil(this._physicalCount / this._itemsPerRow) * this._rowHeight; - } else { - this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize; - } - if (this._physicalAverageCount !== prevAvgCount) { - this._physicalAverage = Math.round((prevPhysicalAvg * prevAvgCount + newPhysicalSize) / this._physicalAverageCount); - } - }, - _updateGridMetrics: function() { - this._viewportWidth = this.$.items.offsetWidth; - this._itemWidth = this._physicalCount > 0 ? this._physicalItems[0].getBoundingClientRect().width : DEFAULT_GRID_SIZE; - this._rowHeight = this._physicalCount > 0 ? this._physicalItems[0].offsetHeight : DEFAULT_GRID_SIZE; - this._itemsPerRow = this._itemWidth ? Math.floor(this._viewportWidth / this._itemWidth) : this._itemsPerRow; - }, - _positionItems: function() { - this._adjustScrollPosition(); - var y = this._physicalTop; - if (this.grid) { - var totalItemWidth = this._itemsPerRow * this._itemWidth; - var rowOffset = (this._viewportWidth - totalItemWidth) / 2; - this._iterateItems(function(pidx, vidx) { - var modulus = vidx % this._itemsPerRow; - var x = Math.floor(modulus * this._itemWidth + rowOffset); - this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]); - if (this._shouldRenderNextRow(vidx)) { - y += this._rowHeight; - } - }); - } else { - this._iterateItems(function(pidx, vidx) { - this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); - y += this._physicalSizes[pidx]; - }); - } - }, - _getPhysicalSizeIncrement: function(pidx) { - if (!this.grid) { - return this._physicalSizes[pidx]; - } - if (this._computeVidx(pidx) % this._itemsPerRow !== this._itemsPerRow - 1) { - return 0; - } - return this._rowHeight; - }, - _shouldRenderNextRow: function(vidx) { - return vidx % this._itemsPerRow === this._itemsPerRow - 1; - }, - _adjustScrollPosition: function() { - var deltaHeight = this._virtualStart === 0 ? this._physicalTop : Math.min(this._scrollPosition + this._physicalTop, 0); - if (deltaHeight) { - this._physicalTop = this._physicalTop - deltaHeight; - if (!IOS_TOUCH_SCROLLING && this._physicalTop !== 0) { - this._resetScrollPosition(this._scrollTop - deltaHeight); - } - } - }, - _resetScrollPosition: function(pos) { - if (this.scrollTarget) { - this._scrollTop = pos; - this._scrollPosition = this._scrollTop; - } - }, - _updateScrollerSize: function(forceUpdate) { - if (this.grid) { - this._estScrollHeight = this._virtualRowCount * this._rowHeight; - } else { - this._estScrollHeight = this._physicalBottom + Math.max(this._virtualCount - this._physicalCount - this._virtualStart, 0) * this._physicalAverage; - } - forceUpdate = forceUpdate || this._scrollHeight === 0; - forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize; - forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this._estScrollHeight; - if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) { - this.$.items.style.height = this._estScrollHeight + 'px'; - this._scrollHeight = this._estScrollHeight; - } - }, - scrollToItem: function(item) { - return this.scrollToIndex(this.items.indexOf(item)); - }, - scrollToIndex: function(idx) { - if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) { - return; - } - Polymer.dom.flush(); - if (this._physicalCount === 0) { - return; - } - idx = Math.min(Math.max(idx, 0), this._virtualCount - 1); - if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { - this._virtualStart = this.grid ? idx - this._itemsPerRow * 2 : idx - 1; - } - this._manageFocus(); - this._assignModels(); - this._updateMetrics(); - this._physicalTop = Math.floor(this._virtualStart / this._itemsPerRow) * this._physicalAverage; - var currentTopItem = this._physicalStart; - var currentVirtualItem = this._virtualStart; - var targetOffsetTop = 0; - var hiddenContentSize = this._hiddenContentSize; - while (currentVirtualItem < idx && targetOffsetTop <= hiddenContentSize) { - targetOffsetTop = targetOffsetTop + this._getPhysicalSizeIncrement(currentTopItem); - currentTopItem = (currentTopItem + 1) % this._physicalCount; - currentVirtualItem++; - } - this._updateScrollerSize(true); - this._positionItems(); - this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + targetOffsetTop); - this._increasePoolIfNeeded(); - this._firstVisibleIndexVal = null; - this._lastVisibleIndexVal = null; - }, - _resetAverage: function() { - this._physicalAverage = 0; - this._physicalAverageCount = 0; - }, - _resizeHandler: function() { - if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100) { - return; - } - Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() { - this.updateViewportBoundaries(); - this._render(); - if (this._physicalCount > 0 && this._isVisible) { - this._resetAverage(); - this.scrollToIndex(this.firstVisibleIndex); - } - }.bind(this), 1)); - }, - _getModelFromItem: function(item) { - var key = this._collection.getKey(item); - var pidx = this._physicalIndexForKey[key]; - if (pidx != null) { - return this._physicalItems[pidx]._templateInstance; - } - return null; - }, - _getNormalizedItem: function(item) { - if (this._collection.getKey(item) === undefined) { - if (typeof item === 'number') { - item = this.items[item]; - if (!item) { - throw new RangeError('<item> not found'); - } - return item; - } - throw new TypeError('<item> should be a valid item'); - } - return item; - }, - 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); - this.updateSizeForItem(item); - }, - deselectItem: function(item) { - item = this._getNormalizedItem(item); - var model = this._getModelFromItem(item); - if (model) { - model[this.selectedAs] = false; - } - this.$.selector.deselect(item); - this.updateSizeForItem(item); - }, - toggleSelectionForItem: function(item) { - item = this._getNormalizedItem(item); - if (this.$.selector.isSelected(item)) { - this.deselectItem(item); - } else { - this.selectItem(item); - } - }, - 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); - } - this.$.selector.clearSelection(); - }, - _selectionEnabledChanged: function(selectionEnabled) { - var handler = selectionEnabled ? this.listen : this.unlisten; - handler.call(this, this, 'tap', '_selectionHandler'); - }, - _selectionHandler: function(e) { - var model = this.modelForElement(e.target); - if (!model) { - return; - } - var modelTabIndex, activeElTabIndex; - var target = Polymer.dom(e).path[0]; - var activeEl = Polymer.dom(this.domHost ? this.domHost.root : document).activeElement; - var physicalItem = this._physicalItems[this._getPhysicalIndex(model[this.indexAs])]; - if (target.localName === 'input' || target.localName === 'button' || target.localName === 'select') { - return; - } - modelTabIndex = model.tabIndex; - model.tabIndex = SECRET_TABINDEX; - activeElTabIndex = activeEl ? activeEl.tabIndex : -1; - model.tabIndex = modelTabIndex; - if (activeEl && physicalItem !== activeEl && physicalItem.contains(activeEl) && activeElTabIndex !== SECRET_TABINDEX) { - return; - } - this.toggleSelectionForItem(model[this.as]); - }, - _multiSelectionChanged: function(multiSelection) { - this.clearSelection(); - this.$.selector.multi = multiSelection; - }, - updateSizeForItem: function(item) { - item = this._getNormalizedItem(item); - var key = this._collection.getKey(item); - var pidx = this._physicalIndexForKey[key]; - if (pidx != null) { - this._updateMetrics([ pidx ]); - this._positionItems(); - } - }, - _manageFocus: function() { - var fidx = this._focusedIndex; - if (fidx >= 0 && fidx < this._virtualCount) { - if (this._isIndexRendered(fidx)) { - this._restoreFocusedItem(); - } else { - this._createFocusBackfillItem(); - } - } else if (this._virtualCount > 0 && this._physicalCount > 0) { - this._focusedIndex = this._virtualStart; - this._focusedItem = this._physicalItems[this._physicalStart]; - } - }, - _isIndexRendered: function(idx) { - return idx >= this._virtualStart && idx <= this._virtualEnd; - }, - _isIndexVisible: function(idx) { - return idx >= this.firstVisibleIndex && idx <= this.lastVisibleIndex; - }, - _getPhysicalIndex: function(idx) { - return this._physicalIndexForKey[this._collection.getKey(this._getNormalizedItem(idx))]; - }, - _focusPhysicalItem: function(idx) { - if (idx < 0 || idx >= this._virtualCount) { - return; - } - this._restoreFocusedItem(); - if (!this._isIndexRendered(idx)) { - this.scrollToIndex(idx); - } - var physicalItem = this._physicalItems[this._getPhysicalIndex(idx)]; - var model = physicalItem._templateInstance; - var focusable; - model.tabIndex = SECRET_TABINDEX; - if (physicalItem.tabIndex === SECRET_TABINDEX) { - focusable = physicalItem; - } - if (!focusable) { - focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECRET_TABINDEX + '"]'); - } - model.tabIndex = 0; - this._focusedIndex = idx; - focusable && focusable.focus(); - }, - _removeFocusedItem: function() { - if (this._offscreenFocusedItem) { - Polymer.dom(this).removeChild(this._offscreenFocusedItem); - } - this._offscreenFocusedItem = null; - this._focusBackfillItem = null; - this._focusedItem = null; - this._focusedIndex = -1; - }, - _createFocusBackfillItem: function() { - var pidx, fidx = this._focusedIndex; - if (this._offscreenFocusedItem || fidx < 0) { - return; - } - if (!this._focusBackfillItem) { - var stampedTemplate = this.stamp(null); - this._focusBackfillItem = stampedTemplate.root.querySelector('*'); - Polymer.dom(this).appendChild(stampedTemplate.root); - } - pidx = this._getPhysicalIndex(fidx); - if (pidx != null) { - this._offscreenFocusedItem = this._physicalItems[pidx]; - this._physicalItems[pidx] = this._focusBackfillItem; - this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem); - } - }, - _restoreFocusedItem: function() { - var pidx, fidx = this._focusedIndex; - if (!this._offscreenFocusedItem || this._focusedIndex < 0) { - return; - } - this._assignModels(); - pidx = this._getPhysicalIndex(fidx); - if (pidx != null) { - this._focusBackfillItem = this._physicalItems[pidx]; - this._physicalItems[pidx] = this._offscreenFocusedItem; - this._offscreenFocusedItem = null; - this.translate3d(0, HIDDEN_Y, 0, this._focusBackfillItem); - } - }, - _didFocus: function(e) { - var targetModel = this.modelForElement(e.target); - var focusedModel = this._focusedItem ? this._focusedItem._templateInstance : null; - var hasOffscreenFocusedItem = this._offscreenFocusedItem !== null; - var fidx = this._focusedIndex; - if (!targetModel || !focusedModel) { - return; - } - if (focusedModel === targetModel) { - if (!this._isIndexVisible(fidx)) { - this.scrollToIndex(fidx); - } - } else { - this._restoreFocusedItem(); - focusedModel.tabIndex = -1; - targetModel.tabIndex = 0; - fidx = targetModel[this.indexAs]; - this._focusedIndex = fidx; - this._focusedItem = this._physicalItems[this._getPhysicalIndex(fidx)]; - if (hasOffscreenFocusedItem && !this._offscreenFocusedItem) { - this._update(); - } - } - }, - _didMoveUp: function() { - this._focusPhysicalItem(this._focusedIndex - 1); - }, - _didMoveDown: function(e) { - e.detail.keyboardEvent.preventDefault(); - this._focusPhysicalItem(this._focusedIndex + 1); - }, - _didEnter: function(e) { - this._focusPhysicalItem(this._focusedIndex); - this._selectionHandler(e.detail.keyboardEvent); - } - }); -})(); - -Polymer({ - is: 'iron-scroll-threshold', - properties: { - upperThreshold: { - type: Number, - value: 100 - }, - lowerThreshold: { - type: Number, - value: 100 - }, - upperTriggered: { - type: Boolean, - value: false, - notify: true, - readOnly: true - }, - lowerTriggered: { - type: Boolean, - value: false, - notify: true, - readOnly: true - }, - horizontal: { - type: Boolean, - value: false - } - }, - behaviors: [ Polymer.IronScrollTargetBehavior ], - observers: [ '_setOverflow(scrollTarget)', '_initCheck(horizontal, isAttached)' ], - get _defaultScrollTarget() { - return this; - }, - _setOverflow: function(scrollTarget) { - this.style.overflow = scrollTarget === this ? 'auto' : ''; - }, - _scrollHandler: function() { - var THROTTLE_THRESHOLD = 200; - if (!this.isDebouncerActive('_checkTheshold')) { - this.debounce('_checkTheshold', function() { - this.checkScrollThesholds(); - }, THROTTLE_THRESHOLD); - } - }, - _initCheck: function(horizontal, isAttached) { - if (isAttached) { - this.debounce('_init', function() { - this.clearTriggers(); - this.checkScrollThesholds(); - }); - } - }, - checkScrollThesholds: function() { - if (!this.scrollTarget || this.lowerTriggered && this.upperTriggered) { - return; - } - var upperScrollValue = this.horizontal ? this._scrollLeft : this._scrollTop; - var lowerScrollValue = this.horizontal ? this.scrollTarget.scrollWidth - this._scrollTargetWidth - this._scrollLeft : this.scrollTarget.scrollHeight - this._scrollTargetHeight - this._scrollTop; - if (upperScrollValue <= this.upperThreshold && !this.upperTriggered) { - this._setUpperTriggered(true); - this.fire('upper-threshold'); - } - if (lowerScrollValue <= this.lowerThreshold && !this.lowerTriggered) { - this._setLowerTriggered(true); - this.fire('lower-threshold'); - } - }, - clearTriggers: function() { - this._setUpperTriggered(false); - this._setLowerTriggered(false); - } -}); - +Polymer({is:"history-toolbar",properties:{count:{type:Number,value:0,observer:"changeToolbarView_"},itemsSelected_:{type:Boolean,value:false,reflectToAttribute:true},searchTerm:{type:String,observer:"searchTermChanged_",notify:true},spinnerActive:{type:Boolean,value:false},hasDrawer:{type:Boolean,observer:"hasDrawerChanged_",reflectToAttribute:true},showSyncNotice:Boolean,isGroupedMode:{type:Boolean,reflectToAttribute:true},groupedRange:{type:Number,value:0,reflectToAttribute:true,notify:true},queryStartTime:String,queryEndTime:String,showMenuPromo_:{type:Boolean,value:function(){return loadTimeData.getBoolean("showMenuPromo")}}},get searchField(){return this.$["main-toolbar"].getSearchField()},showSearchField:function(){this.searchField.showAndFocus()},changeToolbarView_:function(){this.itemsSelected_=this.count>0},searchTermChanged_:function(){if(this.searchField.getValue()!=this.searchTerm){this.searchField.showAndFocus();this.searchField.setValue(this.searchTerm)}},onMenuPromoShown_:function(){md_history.BrowserService.getInstance().menuPromoShown()},onSearchChanged_:function(event){this.searchTerm=event.detail},onInfoButtonTap_:function(){var dropdown=this.$.syncNotice.get();dropdown.positionTarget=this.$$("#info-button-icon");if(dropdown.style.display=="none")dropdown.open()},onClearSelectionTap_:function(){this.fire("unselect-all")},onDeleteTap_:function(){this.fire("delete-selected")},deletingAllowed_:function(){return loadTimeData.getBoolean("allowDeletingHistory")},numberOfItemsSelected_:function(count){return count>0?loadTimeData.getStringF("itemsSelected",count):""},getHistoryInterval_:function(queryStartTime,queryEndTime){return loadTimeData.getStringF("historyInterval",queryStartTime,queryEndTime)},hasDrawerChanged_:function(){this.updateStyles()}});(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=3;var HIDDEN_Y="-10000px";var DEFAULT_GRID_SIZE=200;var SECRET_TABINDEX=-100;Polymer({is:"iron-list",properties:{items:{type:Array},maxPhysicalCount:{type:Number,value:500},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},selectedAs:{type:String,value:"selected"},grid:{type:Boolean,value:false,reflectToAttribute:true},selectionEnabled:{type:Boolean,value:false},selectedItem:{type:Object,notify:true},selectedItems:{type:Object,notify:true},multiSelection:{type:Boolean,value:false}},observers:["_itemsChanged(items.*)","_selectionEnabledChanged(selectionEnabled)","_multiSelectionChanged(multiSelection)","_setOverflow(scrollTarget)"],behaviors:[Polymer.Templatizer,Polymer.IronResizableBehavior,Polymer.IronA11yKeysBehavior,Polymer.IronScrollTargetBehavior],keyBindings:{up:"_didMoveUp",down:"_didMoveDown",enter:"_didEnter"},_ratio:.5,_scrollerPaddingTop:0,_scrollPosition:0,_physicalSize:0,_physicalAverage:0,_physicalAverageCount:0,_physicalTop:0,_virtualCount:0,_physicalIndexForKey:null,_estScrollHeight:0,_scrollHeight:0,_viewportHeight:0,_viewportWidth:0,_physicalItems:null,_physicalSizes:null,_firstVisibleIndexVal:null,_lastVisibleIndexVal:null,_collection:null,_maxPages:3,_focusedItem:null,_focusedIndex:-1,_offscreenFocusedItem:null,_focusBackfillItem:null,_itemsPerRow:1,_itemWidth:0,_rowHeight:0,_templateCost:0,get _physicalBottom(){return this._physicalTop+this._physicalSize},get _scrollBottom(){return this._scrollPosition+this._viewportHeight},get _virtualEnd(){return this._virtualStart+this._physicalCount-1},get _hiddenContentSize(){var size=this.grid?this._physicalRows*this._rowHeight:this._physicalSize;return size-this._viewportHeight},get _maxScrollTop(){return this._estScrollHeight-this._viewportHeight+this._scrollerPaddingTop},_minVirtualStart:0,get _maxVirtualStart(){return Math.max(0,this._virtualCount-this._physicalCount)},_virtualStartVal:0,set _virtualStart(val){this._virtualStartVal=Math.min(this._maxVirtualStart,Math.max(this._minVirtualStart,val))},get _virtualStart(){return this._virtualStartVal||0},_physicalStartVal:0,set _physicalStart(val){this._physicalStartVal=val%this._physicalCount;if(this._physicalStartVal<0){this._physicalStartVal=this._physicalCount+this._physicalStartVal}this._physicalEnd=(this._physicalStart+this._physicalCount-1)%this._physicalCount},get _physicalStart(){return this._physicalStartVal||0},_physicalCountVal:0,set _physicalCount(val){this._physicalCountVal=val;this._physicalEnd=(this._physicalStart+this._physicalCount-1)%this._physicalCount},get _physicalCount(){return this._physicalCountVal},_physicalEnd:0,get _optPhysicalSize(){if(this.grid){return this._estRowsInView*this._rowHeight*this._maxPages}return this._viewportHeight*this._maxPages},get _optPhysicalCount(){return this._estRowsInView*this._itemsPerRow*this._maxPages},get _isVisible(){return Boolean(this.offsetWidth||this.offsetHeight)},get firstVisibleIndex(){if(this._firstVisibleIndexVal===null){var physicalOffset=Math.floor(this._physicalTop+this._scrollerPaddingTop);this._firstVisibleIndexVal=this._iterateItems(function(pidx,vidx){physicalOffset+=this._getPhysicalSizeIncrement(pidx);if(physicalOffset>this._scrollPosition){return this.grid?vidx-vidx%this._itemsPerRow:vidx}if(this.grid&&this._virtualCount-1===vidx){return vidx-vidx%this._itemsPerRow}})||0}return this._firstVisibleIndexVal},get lastVisibleIndex(){if(this._lastVisibleIndexVal===null){if(this.grid){var lastIndex=this.firstVisibleIndex+this._estRowsInView*this._itemsPerRow-1;this._lastVisibleIndexVal=Math.min(this._virtualCount,lastIndex)}else{var physicalOffset=this._physicalTop;this._iterateItems(function(pidx,vidx){if(physicalOffset<this._scrollBottom){this._lastVisibleIndexVal=vidx}else{return true}physicalOffset+=this._getPhysicalSizeIncrement(pidx)})}}return this._lastVisibleIndexVal},get _defaultScrollTarget(){return this},get _virtualRowCount(){return Math.ceil(this._virtualCount/this._itemsPerRow)},get _estRowsInView(){return Math.ceil(this._viewportHeight/this._rowHeight)},get _physicalRows(){return Math.ceil(this._physicalCount/this._itemsPerRow)},ready:function(){this.addEventListener("focus",this._didFocus.bind(this),true)},attached:function(){this.updateViewportBoundaries();if(this._physicalCount===0){this._debounceTemplate(this._render)}this.listen(this,"iron-resize","_resizeHandler")},detached:function(){this.unlisten(this,"iron-resize","_resizeHandler")},_setOverflow:function(scrollTarget){this.style.webkitOverflowScrolling=scrollTarget===this?"touch":"";this.style.overflow=scrollTarget===this?"auto":""},updateViewportBoundaries:function(){this._scrollerPaddingTop=this.scrollTarget===this?0:parseInt(window.getComputedStyle(this)["padding-top"],10);this._viewportHeight=this._scrollTargetHeight;if(this.grid){this._updateGridMetrics()}},_scrollHandler:function(){var scrollTop=Math.max(0,Math.min(this._maxScrollTop,this._scrollTop));var delta=scrollTop-this._scrollPosition;var tileHeight,tileTop,kth,recycledTileSet,scrollBottom,physicalBottom;var ratio=this._ratio;var recycledTiles=0;var hiddenContentSize=this._hiddenContentSize;var currentRatio=ratio;var movingUp=[];this._scrollPosition=scrollTop;this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null;scrollBottom=this._scrollBottom;physicalBottom=this._physicalBottom;if(Math.abs(delta)>this._physicalSize){this._physicalTop+=delta;recycledTiles=Math.round(delta/this._physicalAverage)}else if(delta<0){var topSpace=scrollTop-this._physicalTop;var virtualStart=this._virtualStart;recycledTileSet=[];kth=this._physicalEnd;currentRatio=topSpace/hiddenContentSize;while(currentRatio<ratio&&recycledTiles<this._physicalCount&&virtualStart-recycledTiles>0&&physicalBottom-this._getPhysicalSizeIncrement(kth)>scrollBottom){tileHeight=this._getPhysicalSizeIncrement(kth);currentRatio+=tileHeight/hiddenContentSize;physicalBottom-=tileHeight;recycledTileSet.push(kth);recycledTiles++;kth=kth===0?this._physicalCount-1:kth-1}movingUp=recycledTileSet;recycledTiles=-recycledTiles}else if(delta>0){var bottomSpace=physicalBottom-scrollBottom;var virtualEnd=this._virtualEnd;var lastVirtualItemIndex=this._virtualCount-1;recycledTileSet=[];kth=this._physicalStart;currentRatio=bottomSpace/hiddenContentSize;while(currentRatio<ratio&&recycledTiles<this._physicalCount&&virtualEnd+recycledTiles<lastVirtualItemIndex&&this._physicalTop+this._getPhysicalSizeIncrement(kth)<scrollTop){tileHeight=this._getPhysicalSizeIncrement(kth);currentRatio+=tileHeight/hiddenContentSize;this._physicalTop+=tileHeight;recycledTileSet.push(kth);recycledTiles++;kth=(kth+1)%this._physicalCount}}if(recycledTiles===0){if(physicalBottom<scrollBottom||this._physicalTop>scrollTop){this._increasePoolIfNeeded()}}else{this._virtualStart=this._virtualStart+recycledTiles;this._physicalStart=this._physicalStart+recycledTiles;this._update(recycledTileSet,movingUp)}},_update:function(itemSet,movingUp){this._manageFocus();this._assignModels(itemSet);this._updateMetrics(itemSet);if(movingUp){while(movingUp.length){var idx=movingUp.pop();this._physicalTop-=this._getPhysicalSizeIncrement(idx)}}this._positionItems();this._updateScrollerSize();this._increasePoolIfNeeded()},_createPool:function(size){var physicalItems=new Array(size);this._ensureTemplatized();for(var i=0;i<size;i++){var inst=this.stamp(null);physicalItems[i]=inst.root.querySelector("*");Polymer.dom(this).appendChild(inst.root)}return physicalItems},_increasePoolIfNeeded:function(){if(this._viewportHeight===0){return false}var self=this;var isClientFull=this._physicalBottom>=this._scrollBottom&&this._physicalTop<=this._scrollPosition;if(this._physicalSize>=this._optPhysicalSize&&isClientFull){return false}var maxPoolSize=Math.round(this._physicalCount*.5);if(!isClientFull){this._debounceTemplate(this._increasePool.bind(this,maxPoolSize));return true}this._yield(function(){self._increasePool(Math.min(maxPoolSize,Math.max(1,Math.round(50/self._templateCost))))});return true},_yield:function(cb){var g=window;var handle=g.requestIdleCallback?g.requestIdleCallback(cb):g.setTimeout(cb,16);Polymer.dom.addDebouncer({complete:function(){g.cancelIdleCallback?g.cancelIdleCallback(handle):g.clearTimeout(handle);cb()}})},_increasePool:function(missingItems){var nextPhysicalCount=Math.min(this._physicalCount+missingItems,this._virtualCount-this._virtualStart,Math.max(this.maxPhysicalCount,DEFAULT_PHYSICAL_COUNT));var prevPhysicalCount=this._physicalCount;var delta=nextPhysicalCount-prevPhysicalCount;var ts=window.performance.now();if(delta<=0){return}[].push.apply(this._physicalItems,this._createPool(delta));[].push.apply(this._physicalSizes,new Array(delta));this._physicalCount=prevPhysicalCount+delta;if(this._physicalStart>this._physicalEnd&&this._isIndexRendered(this._focusedIndex)&&this._getPhysicalIndex(this._focusedIndex)<this._physicalEnd){this._physicalStart=this._physicalStart+delta}this._update();this._templateCost=(window.performance.now()-ts)/delta},_render:function(){if(this.isAttached&&this._isVisible){if(this._physicalCount===0){this._increasePool(DEFAULT_PHYSICAL_COUNT)}else{this._update()}}},_ensureTemplatized:function(){if(!this.ctor){var props={};props.__key__=true;props[this.as]=true;props[this.indexAs]=true;props[this.selectedAs]=true;props.tabIndex=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")}}},_getStampedChildren:function(){return this._physicalItems},_forwardInstancePath:function(inst,path,value){if(path.indexOf(this.as+".")===0){this.notifyPath("items."+inst.__key__+"."+path.slice(this.as.length+1),value)}},_forwardParentProp:function(prop,value){if(this._physicalItems){this._physicalItems.forEach(function(item){item._templateInstance[prop]=value},this)}},_forwardParentPath:function(path,value){if(this._physicalItems){this._physicalItems.forEach(function(item){item._templateInstance.notifyPath(path,value,true)},this)}},_forwardItemPath:function(path,value){if(!this._physicalIndexForKey){return}var dot=path.indexOf(".");var key=path.substring(0,dot<0?path.length:dot);var idx=this._physicalIndexForKey[key];var offscreenItem=this._offscreenFocusedItem;var el=offscreenItem&&offscreenItem._templateInstance.__key__===key?offscreenItem:this._physicalItems[idx];if(!el||el._templateInstance.__key__!==key){return}if(dot>=0){path=this.as+"."+path.substring(dot+1);el._templateInstance.notifyPath(path,value,true)}else{var currentItem=el._templateInstance[this.as];if(Array.isArray(this.selectedItems)){for(var i=0;i<this.selectedItems.length;i++){if(this.selectedItems[i]===currentItem){this.set("selectedItems."+i,value);break}}}else if(this.selectedItem===currentItem){this.set("selectedItem",value)}el._templateInstance[this.as]=value}},_itemsChanged:function(change){if(change.path==="items"){this._virtualStart=0;this._physicalTop=0;this._virtualCount=this.items?this.items.length:0;this._collection=this.items?Polymer.Collection.get(this.items):null;this._physicalIndexForKey={};this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null;this._physicalCount=this._physicalCount||0;this._physicalItems=this._physicalItems||[];this._physicalSizes=this._physicalSizes||[];this._physicalStart=0;this._resetScrollPosition(0);this._removeFocusedItem();this._debounceTemplate(this._render)}else if(change.path==="items.splices"){this._adjustVirtualIndex(change.value.indexSplices);this._virtualCount=this.items?this.items.length:0;this._debounceTemplate(this._render)}else{this._forwardItemPath(change.path.split(".").slice(1).join("."),change.value)}},_adjustVirtualIndex:function(splices){splices.forEach(function(splice){splice.removed.forEach(this._removeItem,this);if(splice.index<this._virtualStart){var delta=Math.max(splice.addedCount-splice.removed.length,splice.index-this._virtualStart);this._virtualStart=this._virtualStart+delta;if(this._focusedIndex>=0){this._focusedIndex=this._focusedIndex+delta}}},this)},_removeItem:function(item){this.$.selector.deselect(item);if(this._focusedItem&&this._focusedItem._templateInstance[this.as]===item){this._removeFocusedItem()}},_iterateItems:function(fn,itemSet){var pidx,vidx,rtn,i;if(arguments.length===2&&itemSet){for(i=0;i<itemSet.length;i++){pidx=itemSet[i];vidx=this._computeVidx(pidx);if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}}else{pidx=this._physicalStart;vidx=this._virtualStart;for(;pidx<this._physicalCount;pidx++,vidx++){if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}for(pidx=0;pidx<this._physicalStart;pidx++,vidx++){if((rtn=fn.call(this,pidx,vidx))!=null){return rtn}}}},_computeVidx:function(pidx){if(pidx>=this._physicalStart){return this._virtualStart+(pidx-this._physicalStart)}return this._virtualStart+(this._physicalCount-this._physicalStart)+pidx},_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!=null){inst[this.as]=item;inst.__key__=this._collection.getKey(item);inst[this.selectedAs]=this.$.selector.isSelected(item);inst[this.indexAs]=vidx;inst.tabIndex=this._focusedIndex===vidx?0:-1;this._physicalIndexForKey[inst.__key__]=pidx;el.removeAttribute("hidden")}else{inst.__key__=null;el.setAttribute("hidden","")}},itemSet)},_updateMetrics:function(itemSet){Polymer.dom.flush();var newPhysicalSize=0;var oldPhysicalSize=0;var prevAvgCount=this._physicalAverageCount;var prevPhysicalAvg=this._physicalAverage;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._viewportHeight=this._scrollTargetHeight;if(this.grid){this._updateGridMetrics();this._physicalSize=Math.ceil(this._physicalCount/this._itemsPerRow)*this._rowHeight}else{this._physicalSize=this._physicalSize+newPhysicalSize-oldPhysicalSize}if(this._physicalAverageCount!==prevAvgCount){this._physicalAverage=Math.round((prevPhysicalAvg*prevAvgCount+newPhysicalSize)/this._physicalAverageCount)}},_updateGridMetrics:function(){this._viewportWidth=this.$.items.offsetWidth;this._itemWidth=this._physicalCount>0?this._physicalItems[0].getBoundingClientRect().width:DEFAULT_GRID_SIZE;this._rowHeight=this._physicalCount>0?this._physicalItems[0].offsetHeight:DEFAULT_GRID_SIZE;this._itemsPerRow=this._itemWidth?Math.floor(this._viewportWidth/this._itemWidth):this._itemsPerRow},_positionItems:function(){this._adjustScrollPosition();var y=this._physicalTop;if(this.grid){var totalItemWidth=this._itemsPerRow*this._itemWidth;var rowOffset=(this._viewportWidth-totalItemWidth)/2;this._iterateItems(function(pidx,vidx){var modulus=vidx%this._itemsPerRow;var x=Math.floor(modulus*this._itemWidth+rowOffset);this.translate3d(x+"px",y+"px",0,this._physicalItems[pidx]);if(this._shouldRenderNextRow(vidx)){y+=this._rowHeight}})}else{this._iterateItems(function(pidx,vidx){this.translate3d(0,y+"px",0,this._physicalItems[pidx]);y+=this._physicalSizes[pidx]})}},_getPhysicalSizeIncrement:function(pidx){if(!this.grid){return this._physicalSizes[pidx]}if(this._computeVidx(pidx)%this._itemsPerRow!==this._itemsPerRow-1){return 0}return this._rowHeight},_shouldRenderNextRow:function(vidx){return vidx%this._itemsPerRow===this._itemsPerRow-1},_adjustScrollPosition:function(){var deltaHeight=this._virtualStart===0?this._physicalTop:Math.min(this._scrollPosition+this._physicalTop,0);if(deltaHeight){this._physicalTop=this._physicalTop-deltaHeight;if(!IOS_TOUCH_SCROLLING&&this._physicalTop!==0){this._resetScrollPosition(this._scrollTop-deltaHeight)}}},_resetScrollPosition:function(pos){if(this.scrollTarget){this._scrollTop=pos;this._scrollPosition=this._scrollTop}},_updateScrollerSize:function(forceUpdate){if(this.grid){this._estScrollHeight=this._virtualRowCount*this._rowHeight}else{this._estScrollHeight=this._physicalBottom+Math.max(this._virtualCount-this._physicalCount-this._virtualStart,0)*this._physicalAverage}forceUpdate=forceUpdate||this._scrollHeight===0;forceUpdate=forceUpdate||this._scrollPosition>=this._estScrollHeight-this._physicalSize;forceUpdate=forceUpdate||this.grid&&this.$.items.style.height<this._estScrollHeight;if(forceUpdate||Math.abs(this._estScrollHeight-this._scrollHeight)>=this._optPhysicalSize){this.$.items.style.height=this._estScrollHeight+"px";this._scrollHeight=this._estScrollHeight}},scrollToItem:function(item){return this.scrollToIndex(this.items.indexOf(item))},scrollToIndex:function(idx){if(typeof idx!=="number"||idx<0||idx>this.items.length-1){return}Polymer.dom.flush();if(this._physicalCount===0){return}idx=Math.min(Math.max(idx,0),this._virtualCount-1);if(!this._isIndexRendered(idx)||idx>=this._maxVirtualStart){this._virtualStart=this.grid?idx-this._itemsPerRow*2:idx-1}this._manageFocus();this._assignModels();this._updateMetrics();this._physicalTop=Math.floor(this._virtualStart/this._itemsPerRow)*this._physicalAverage;var currentTopItem=this._physicalStart;var currentVirtualItem=this._virtualStart;var targetOffsetTop=0;var hiddenContentSize=this._hiddenContentSize;while(currentVirtualItem<idx&&targetOffsetTop<=hiddenContentSize){targetOffsetTop=targetOffsetTop+this._getPhysicalSizeIncrement(currentTopItem);currentTopItem=(currentTopItem+1)%this._physicalCount;currentVirtualItem++}this._updateScrollerSize(true);this._positionItems();this._resetScrollPosition(this._physicalTop+this._scrollerPaddingTop+targetOffsetTop);this._increasePoolIfNeeded();this._firstVisibleIndexVal=null;this._lastVisibleIndexVal=null},_resetAverage:function(){this._physicalAverage=0;this._physicalAverageCount=0},_resizeHandler:function(){if(IOS&&Math.abs(this._viewportHeight-this._scrollTargetHeight)<100){return}Polymer.dom.addDebouncer(this.debounce("_debounceTemplate",function(){this.updateViewportBoundaries();this._render();if(this._physicalCount>0&&this._isVisible){this._resetAverage();this.scrollToIndex(this.firstVisibleIndex)}}.bind(this),1))},_getModelFromItem:function(item){var key=this._collection.getKey(item);var pidx=this._physicalIndexForKey[key];if(pidx!=null){return this._physicalItems[pidx]._templateInstance}return null},_getNormalizedItem:function(item){if(this._collection.getKey(item)===undefined){if(typeof item==="number"){item=this.items[item];if(!item){throw new RangeError("<item> not found")}return item}throw new TypeError("<item> should be a valid item")}return item},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);this.updateSizeForItem(item)},deselectItem:function(item){item=this._getNormalizedItem(item);var model=this._getModelFromItem(item);if(model){model[this.selectedAs]=false}this.$.selector.deselect(item);this.updateSizeForItem(item)},toggleSelectionForItem:function(item){item=this._getNormalizedItem(item);if(this.$.selector.isSelected(item)){this.deselectItem(item)}else{this.selectItem(item)}},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)}this.$.selector.clearSelection()},_selectionEnabledChanged:function(selectionEnabled){var handler=selectionEnabled?this.listen:this.unlisten;handler.call(this,this,"tap","_selectionHandler")},_selectionHandler:function(e){var model=this.modelForElement(e.target);if(!model){return}var modelTabIndex,activeElTabIndex;var target=Polymer.dom(e).path[0];var activeEl=Polymer.dom(this.domHost?this.domHost.root:document).activeElement;var physicalItem=this._physicalItems[this._getPhysicalIndex(model[this.indexAs])];if(target.localName==="input"||target.localName==="button"||target.localName==="select"){return}modelTabIndex=model.tabIndex;model.tabIndex=SECRET_TABINDEX;activeElTabIndex=activeEl?activeEl.tabIndex:-1;model.tabIndex=modelTabIndex;if(activeEl&&physicalItem!==activeEl&&physicalItem.contains(activeEl)&&activeElTabIndex!==SECRET_TABINDEX){return}this.toggleSelectionForItem(model[this.as])},_multiSelectionChanged:function(multiSelection){this.clearSelection();this.$.selector.multi=multiSelection},updateSizeForItem:function(item){item=this._getNormalizedItem(item);var key=this._collection.getKey(item);var pidx=this._physicalIndexForKey[key];if(pidx!=null){this._updateMetrics([pidx]);this._positionItems()}},_manageFocus:function(){var fidx=this._focusedIndex;if(fidx>=0&&fidx<this._virtualCount){if(this._isIndexRendered(fidx)){this._restoreFocusedItem()}else{this._createFocusBackfillItem()}}else if(this._virtualCount>0&&this._physicalCount>0){this._focusedIndex=this._virtualStart;this._focusedItem=this._physicalItems[this._physicalStart]}},_isIndexRendered:function(idx){return idx>=this._virtualStart&&idx<=this._virtualEnd},_isIndexVisible:function(idx){return idx>=this.firstVisibleIndex&&idx<=this.lastVisibleIndex},_getPhysicalIndex:function(idx){return this._physicalIndexForKey[this._collection.getKey(this._getNormalizedItem(idx))]},_focusPhysicalItem:function(idx){if(idx<0||idx>=this._virtualCount){return}this._restoreFocusedItem();if(!this._isIndexRendered(idx)){this.scrollToIndex(idx)}var physicalItem=this._physicalItems[this._getPhysicalIndex(idx)];var model=physicalItem._templateInstance;var focusable;model.tabIndex=SECRET_TABINDEX;if(physicalItem.tabIndex===SECRET_TABINDEX){focusable=physicalItem}if(!focusable){focusable=Polymer.dom(physicalItem).querySelector('[tabindex="'+SECRET_TABINDEX+'"]')}model.tabIndex=0;this._focusedIndex=idx;focusable&&focusable.focus()},_removeFocusedItem:function(){if(this._offscreenFocusedItem){Polymer.dom(this).removeChild(this._offscreenFocusedItem)}this._offscreenFocusedItem=null;this._focusBackfillItem=null;this._focusedItem=null;this._focusedIndex=-1},_createFocusBackfillItem:function(){var pidx,fidx=this._focusedIndex;if(this._offscreenFocusedItem||fidx<0){return}if(!this._focusBackfillItem){var stampedTemplate=this.stamp(null);this._focusBackfillItem=stampedTemplate.root.querySelector("*");Polymer.dom(this).appendChild(stampedTemplate.root)}pidx=this._getPhysicalIndex(fidx);if(pidx!=null){this._offscreenFocusedItem=this._physicalItems[pidx];this._physicalItems[pidx]=this._focusBackfillItem;this.translate3d(0,HIDDEN_Y,0,this._offscreenFocusedItem)}},_restoreFocusedItem:function(){var pidx,fidx=this._focusedIndex;if(!this._offscreenFocusedItem||this._focusedIndex<0){return}this._assignModels();pidx=this._getPhysicalIndex(fidx);if(pidx!=null){this._focusBackfillItem=this._physicalItems[pidx];this._physicalItems[pidx]=this._offscreenFocusedItem;this._offscreenFocusedItem=null;this.translate3d(0,HIDDEN_Y,0,this._focusBackfillItem)}},_didFocus:function(e){var targetModel=this.modelForElement(e.target);var focusedModel=this._focusedItem?this._focusedItem._templateInstance:null;var hasOffscreenFocusedItem=this._offscreenFocusedItem!==null;var fidx=this._focusedIndex;if(!targetModel||!focusedModel){return}if(focusedModel===targetModel){if(!this._isIndexVisible(fidx)){this.scrollToIndex(fidx)}}else{this._restoreFocusedItem();focusedModel.tabIndex=-1;targetModel.tabIndex=0;fidx=targetModel[this.indexAs];this._focusedIndex=fidx;this._focusedItem=this._physicalItems[this._getPhysicalIndex(fidx)];if(hasOffscreenFocusedItem&&!this._offscreenFocusedItem){this._update()}}},_didMoveUp:function(){this._focusPhysicalItem(this._focusedIndex-1)},_didMoveDown:function(e){e.detail.keyboardEvent.preventDefault();this._focusPhysicalItem(this._focusedIndex+1)},_didEnter:function(e){this._focusPhysicalItem(this._focusedIndex);this._selectionHandler(e.detail.keyboardEvent)}})})();Polymer({is:"iron-scroll-threshold",properties:{upperThreshold:{type:Number,value:100},lowerThreshold:{type:Number,value:100},upperTriggered:{type:Boolean,value:false,notify:true,readOnly:true},lowerTriggered:{type:Boolean,value:false,notify:true,readOnly:true},horizontal:{type:Boolean,value:false}},behaviors:[Polymer.IronScrollTargetBehavior],observers:["_setOverflow(scrollTarget)","_initCheck(horizontal, isAttached)"],get _defaultScrollTarget(){return this},_setOverflow:function(scrollTarget){this.style.overflow=scrollTarget===this?"auto":""},_scrollHandler:function(){var THROTTLE_THRESHOLD=200;if(!this.isDebouncerActive("_checkTheshold")){this.debounce("_checkTheshold",function(){this.checkScrollThesholds()},THROTTLE_THRESHOLD)}},_initCheck:function(horizontal,isAttached){if(isAttached){this.debounce("_init",function(){this.clearTriggers();this.checkScrollThesholds()})}},checkScrollThesholds:function(){if(!this.scrollTarget||this.lowerTriggered&&this.upperTriggered){return}var upperScrollValue=this.horizontal?this._scrollLeft:this._scrollTop;var lowerScrollValue=this.horizontal?this.scrollTarget.scrollWidth-this._scrollTargetWidth-this._scrollLeft:this.scrollTarget.scrollHeight-this._scrollTargetHeight-this._scrollTop;if(upperScrollValue<=this.upperThreshold&&!this.upperTriggered){this._setUpperTriggered(true);this.fire("upper-threshold")}if(lowerScrollValue<=this.lowerThreshold&&!this.lowerTriggered){this._setLowerTriggered(true);this.fire("lower-threshold")}},clearTriggers:function(){this._setUpperTriggered(false);this._setLowerTriggered(false)}}); // Copyright (c) 2011 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. -var EventTrackerEntry; - -function EventTracker() { - this.listeners_ = []; -} - -EventTracker.prototype = { - add: function(target, eventType, listener, opt_capture) { - var capture = !!opt_capture; - var h = { - target: target, - eventType: eventType, - listener: listener, - capture: capture - }; - this.listeners_.push(h); - target.addEventListener(eventType, listener, capture); - }, - remove: function(target, eventType) { - this.listeners_ = this.listeners_.filter(function(h) { - if (h.target == target && (!eventType || h.eventType == eventType)) { - EventTracker.removeEventListener_(h); - return false; - } - return true; - }); - }, - removeAll: function() { - this.listeners_.forEach(EventTracker.removeEventListener_); - this.listeners_ = []; - } -}; - -EventTracker.removeEventListener_ = function(h) { - h.target.removeEventListener(h.eventType, h.listener, h.capture); -}; - +var EventTrackerEntry;function EventTracker(){this.listeners_=[]}EventTracker.prototype={add:function(target,eventType,listener,opt_capture){var capture=!!opt_capture;var h={target:target,eventType:eventType,listener:listener,capture:capture};this.listeners_.push(h);target.addEventListener(eventType,listener,capture)},remove:function(target,eventType){this.listeners_=this.listeners_.filter(function(h){if(h.target==target&&(!eventType||h.eventType==eventType)){EventTracker.removeEventListener_(h);return false}return true})},removeAll:function(){this.listeners_.forEach(EventTracker.removeEventListener_);this.listeners_=[]}};EventTracker.removeEventListener_=function(h){h.target.removeEventListener(h.eventType,h.listener,h.capture)}; // 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. -cr.define('cr.ui', function() { - function FocusRow(root, boundary, opt_delegate) { - this.root = root; - this.boundary_ = boundary || document.documentElement; - this.delegate = opt_delegate; - this.eventTracker = new EventTracker(); - } - FocusRow.Delegate = function() {}; - FocusRow.Delegate.prototype = { - onKeydown: assertNotReached, - onFocus: assertNotReached - }; - FocusRow.ACTIVE_CLASS = 'focus-row-active'; - FocusRow.isFocusable = function(element) { - if (!element || element.disabled) return false; - function isVisible(element) { - assertInstanceof(element, Element); - var style = window.getComputedStyle(element); - if (style.visibility == 'hidden' || style.display == 'none') return false; - var parent = element.parentNode; - if (!parent) return false; - if (parent == element.ownerDocument || parent instanceof DocumentFragment) return true; - return isVisible(parent); - } - return isVisible(element); - }; - FocusRow.prototype = { - addItem: function(type, query) { - assert(type); - var element = this.root.querySelector(query); - if (!element) return false; - element.setAttribute('focus-type', type); - element.tabIndex = this.isActive() ? 0 : -1; - this.eventTracker.add(element, 'blur', this.onBlur_.bind(this)); - this.eventTracker.add(element, 'focus', this.onFocus_.bind(this)); - this.eventTracker.add(element, 'keydown', this.onKeydown_.bind(this)); - this.eventTracker.add(element, 'mousedown', this.onMousedown_.bind(this)); - return true; - }, - destroy: function() { - this.eventTracker.removeAll(); - }, - getCustomEquivalent: function(sampleElement) { - return assert(this.getFirstFocusable()); - }, - getElements: function() { - var elements = this.root.querySelectorAll('[focus-type]'); - return Array.prototype.slice.call(elements); - }, - getEquivalentElement: function(sampleElement) { - if (this.getFocusableElements().indexOf(sampleElement) >= 0) return sampleElement; - var sampleFocusType = this.getTypeForElement(sampleElement); - if (sampleFocusType) { - var sameType = this.getFirstFocusable(sampleFocusType); - if (sameType) return sameType; - } - return this.getCustomEquivalent(sampleElement); - }, - getFirstFocusable: function(opt_type) { - var filter = opt_type ? '="' + opt_type + '"' : ''; - var elements = this.root.querySelectorAll('[focus-type' + filter + ']'); - for (var i = 0; i < elements.length; ++i) { - if (cr.ui.FocusRow.isFocusable(elements[i])) return elements[i]; - } - return null; - }, - getFocusableElements: function() { - return this.getElements().filter(cr.ui.FocusRow.isFocusable); - }, - getTypeForElement: function(element) { - return element.getAttribute('focus-type') || ''; - }, - isActive: function() { - return this.root.classList.contains(FocusRow.ACTIVE_CLASS); - }, - makeActive: function(active) { - if (active == this.isActive()) return; - this.getElements().forEach(function(element) { - element.tabIndex = active ? 0 : -1; - }); - this.root.classList.toggle(FocusRow.ACTIVE_CLASS, active); - }, - onBlur_: function(e) { - if (!this.boundary_.contains(e.relatedTarget)) return; - var currentTarget = e.currentTarget; - if (this.getFocusableElements().indexOf(currentTarget) >= 0) this.makeActive(false); - }, - onFocus_: function(e) { - if (this.delegate) this.delegate.onFocus(this, e); - }, - onMousedown_: function(e) { - if (e.button) return; - if (!e.currentTarget.disabled) e.currentTarget.tabIndex = 0; - }, - onKeydown_: function(e) { - var elements = this.getFocusableElements(); - var currentElement = e.currentTarget; - var elementIndex = elements.indexOf(currentElement); - assert(elementIndex >= 0); - if (this.delegate && this.delegate.onKeydown(this, e)) return; - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return; - var index = -1; - if (e.key == 'ArrowLeft') index = elementIndex + (isRTL() ? 1 : -1); else if (e.key == 'ArrowRight') index = elementIndex + (isRTL() ? -1 : 1); else if (e.key == 'Home') index = 0; else if (e.key == 'End') index = elements.length - 1; - var elementToFocus = elements[index]; - if (elementToFocus) { - this.getEquivalentElement(elementToFocus).focus(); - e.preventDefault(); - } - } - }; - return { - FocusRow: FocusRow - }; -}); - +cr.define("cr.ui",function(){function FocusRow(root,boundary,opt_delegate){this.root=root;this.boundary_=boundary||document.documentElement;this.delegate=opt_delegate;this.eventTracker=new EventTracker}FocusRow.Delegate=function(){};FocusRow.Delegate.prototype={onKeydown:assertNotReached,onFocus:assertNotReached};FocusRow.ACTIVE_CLASS="focus-row-active";FocusRow.isFocusable=function(element){if(!element||element.disabled)return false;function isVisible(element){assertInstanceof(element,Element);var style=window.getComputedStyle(element);if(style.visibility=="hidden"||style.display=="none")return false;var parent=element.parentNode;if(!parent)return false;if(parent==element.ownerDocument||parent instanceof DocumentFragment)return true;return isVisible(parent)}return isVisible(element)};FocusRow.prototype={addItem:function(type,query){assert(type);var element=this.root.querySelector(query);if(!element)return false;element.setAttribute("focus-type",type);element.tabIndex=this.isActive()?0:-1;this.eventTracker.add(element,"blur",this.onBlur_.bind(this));this.eventTracker.add(element,"focus",this.onFocus_.bind(this));this.eventTracker.add(element,"keydown",this.onKeydown_.bind(this));this.eventTracker.add(element,"mousedown",this.onMousedown_.bind(this));return true},destroy:function(){this.eventTracker.removeAll()},getCustomEquivalent:function(sampleElement){return assert(this.getFirstFocusable())},getElements:function(){var elements=this.root.querySelectorAll("[focus-type]");return Array.prototype.slice.call(elements)},getEquivalentElement:function(sampleElement){if(this.getFocusableElements().indexOf(sampleElement)>=0)return sampleElement;var sampleFocusType=this.getTypeForElement(sampleElement);if(sampleFocusType){var sameType=this.getFirstFocusable(sampleFocusType);if(sameType)return sameType}return this.getCustomEquivalent(sampleElement)},getFirstFocusable:function(opt_type){var filter=opt_type?'="'+opt_type+'"':"";var elements=this.root.querySelectorAll("[focus-type"+filter+"]");for(var i=0;i<elements.length;++i){if(cr.ui.FocusRow.isFocusable(elements[i]))return elements[i]}return null},getFocusableElements:function(){return this.getElements().filter(cr.ui.FocusRow.isFocusable)},getTypeForElement:function(element){return element.getAttribute("focus-type")||""},isActive:function(){return this.root.classList.contains(FocusRow.ACTIVE_CLASS)},makeActive:function(active){if(active==this.isActive())return;this.getElements().forEach(function(element){element.tabIndex=active?0:-1});this.root.classList.toggle(FocusRow.ACTIVE_CLASS,active)},onBlur_:function(e){if(!this.boundary_.contains(e.relatedTarget))return;var currentTarget=e.currentTarget;if(this.getFocusableElements().indexOf(currentTarget)>=0)this.makeActive(false)},onFocus_:function(e){if(this.delegate)this.delegate.onFocus(this,e)},onMousedown_:function(e){if(e.button)return;if(!e.currentTarget.disabled)e.currentTarget.tabIndex=0},onKeydown_:function(e){var elements=this.getFocusableElements();var currentElement=e.currentTarget;var elementIndex=elements.indexOf(currentElement);assert(elementIndex>=0);if(this.delegate&&this.delegate.onKeydown(this,e))return;if(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey)return;var index=-1;if(e.key=="ArrowLeft")index=elementIndex+(isRTL()?1:-1);else if(e.key=="ArrowRight")index=elementIndex+(isRTL()?-1:1);else if(e.key=="Home")index=0;else if(e.key=="End")index=elements.length-1;var elementToFocus=elements[index];if(elementToFocus){this.getEquivalentElement(elementToFocus).focus();e.preventDefault()}}};return{FocusRow:FocusRow}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -cr.define('cr.icon', function() { - function getSupportedScaleFactors() { - var supportedScaleFactors = []; - if (!cr.isIOS) { - supportedScaleFactors.push(1); - } - if (cr.isMac || cr.isChromeOS || cr.isWindows || cr.isLinux) { - supportedScaleFactors.push(2); - } else { - supportedScaleFactors.push(window.devicePixelRatio); - } - return supportedScaleFactors; - } - function getImageSet(path) { - var supportedScaleFactors = getSupportedScaleFactors(); - var replaceStartIndex = path.indexOf('scalefactor'); - if (replaceStartIndex < 0) return url(path); - var s = ''; - for (var i = 0; i < supportedScaleFactors.length; ++i) { - var scaleFactor = supportedScaleFactors[i]; - var pathWithScaleFactor = path.substr(0, replaceStartIndex) + scaleFactor + path.substr(replaceStartIndex + 'scalefactor'.length); - s += url(pathWithScaleFactor) + ' ' + scaleFactor + 'x'; - if (i != supportedScaleFactors.length - 1) s += ', '; - } - return '-webkit-image-set(' + s + ')'; - } - function getImage(path) { - var chromeThemePath = 'chrome://theme'; - var isChromeThemeUrl = path.slice(0, chromeThemePath.length) == chromeThemePath; - return isChromeThemeUrl ? getImageSet(path + '@scalefactorx') : url(path); - } - var FAVICON_URL_REGEX = /\.ico$/i; - function getFavicon(url, opt_size, opt_type) { - var size = opt_size || 16; - var type = opt_type || 'favicon'; - return getImageSet('chrome://' + type + '/size/' + size + '@scalefactorx/' + (FAVICON_URL_REGEX.test(url) ? 'iconurl/' : '') + url); - } - return { - getImage: getImage, - getFavicon: getFavicon - }; -}); - +cr.define("cr.icon",function(){function getSupportedScaleFactors(){var supportedScaleFactors=[];if(!cr.isIOS){supportedScaleFactors.push(1)}if(cr.isMac||cr.isChromeOS||cr.isWindows||cr.isLinux){supportedScaleFactors.push(2)}else{supportedScaleFactors.push(window.devicePixelRatio)}return supportedScaleFactors}function getImageSet(path){var supportedScaleFactors=getSupportedScaleFactors();var replaceStartIndex=path.indexOf("scalefactor");if(replaceStartIndex<0)return url(path);var s="";for(var i=0;i<supportedScaleFactors.length;++i){var scaleFactor=supportedScaleFactors[i];var pathWithScaleFactor=path.substr(0,replaceStartIndex)+scaleFactor+path.substr(replaceStartIndex+"scalefactor".length);s+=url(pathWithScaleFactor)+" "+scaleFactor+"x";if(i!=supportedScaleFactors.length-1)s+=", "}return"-webkit-image-set("+s+")"}function getImage(path){var chromeThemePath="chrome://theme";var isChromeThemeUrl=path.slice(0,chromeThemePath.length)==chromeThemePath;return isChromeThemeUrl?getImageSet(path+"@scalefactorx"):url(path)}var FAVICON_URL_REGEX=/\.ico$/i;function getFavicon(url,opt_size,opt_type){var size=opt_size||16;var type=opt_type||"favicon";return getImageSet("chrome://"+type+"/size/"+size+"@scalefactorx/"+(FAVICON_URL_REGEX.test(url)?"iconurl/":"")+url)}return{getImage:getImage,getFavicon:getFavicon}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'history-searched-label', - properties: { - title: String, - searchTerm: String - }, - observers: [ 'setSearchedTextToBold_(title, searchTerm)' ], - setSearchedTextToBold_: function() { - var i = 0; - var titleText = this.title; - if (this.searchTerm == '' || this.searchTerm == null) { - this.textContent = titleText; - return; - } - var re = new RegExp(quoteString(this.searchTerm), 'gim'); - var match; - this.textContent = ''; - while (match = re.exec(titleText)) { - if (match.index > i) this.appendChild(document.createTextNode(titleText.slice(i, match.index))); - i = re.lastIndex; - var b = document.createElement('b'); - b.textContent = titleText.substring(match.index, i); - this.appendChild(b); - } - if (i < titleText.length) this.appendChild(document.createTextNode(titleText.slice(i))); - } -}); - +Polymer({is:"history-searched-label",properties:{title:String,searchTerm:String},observers:["setSearchedTextToBold_(title, searchTerm)"],setSearchedTextToBold_:function(){var i=0;var titleText=this.title;if(this.searchTerm==""||this.searchTerm==null){this.textContent=titleText;return}var re=new RegExp(quoteString(this.searchTerm),"gim");var match;this.textContent="";while(match=re.exec(titleText)){if(match.index>i)this.appendChild(document.createTextNode(titleText.slice(i,match.index)));i=re.lastIndex;var b=document.createElement("b");b.textContent=titleText.substring(match.index,i);this.appendChild(b)}if(i<titleText.length)this.appendChild(document.createTextNode(titleText.slice(i)))}}); // 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. -function HistoryFocusRow(root, boundary, delegate) { - cr.ui.FocusRow.call(this, root, boundary, delegate); - this.addItems(); -} - -HistoryFocusRow.prototype = { - __proto__: cr.ui.FocusRow.prototype, - getCustomEquivalent: function(sampleElement) { - var equivalent; - if (this.getTypeForElement(sampleElement) == 'star') equivalent = this.getFirstFocusable('title'); - return equivalent || cr.ui.FocusRow.prototype.getCustomEquivalent.call(this, sampleElement); - }, - addItems: function() { - this.destroy(); - assert(this.addItem('checkbox', '#checkbox')); - assert(this.addItem('title', '#title')); - assert(this.addItem('menu-button', '#menu-button')); - this.addItem('star', '#bookmark-star'); - } -}; - -cr.define('md_history', function() { - function FocusRowDelegate(historyItemElement) { - this.historyItemElement = historyItemElement; - } - FocusRowDelegate.prototype = { - onFocus: function(row, e) { - this.historyItemElement.lastFocused = e.path[0]; - }, - onKeydown: function(row, e) { - if (e.key == 'Enter') e.stopPropagation(); - return false; - } - }; - var HistoryItem = Polymer({ - is: 'history-item', - properties: { - item: { - type: Object, - observer: 'showIcon_' - }, - searchTerm: { - type: String - }, - selected: { - type: Boolean, - reflectToAttribute: true - }, - isCardStart: { - type: Boolean, - reflectToAttribute: true - }, - isCardEnd: { - type: Boolean, - reflectToAttribute: true - }, - embedded: { - type: Boolean, - reflectToAttribute: true - }, - hasTimeGap: { - type: Boolean - }, - numberOfItems: { - type: Number - }, - path: String, - index: Number, - lastFocused: { - type: Object, - notify: true - }, - ironListTabIndex: { - type: Number, - observer: 'ironListTabIndexChanged_' - } - }, - row_: null, - attached: function() { - Polymer.RenderStatus.afterNextRender(this, function() { - this.row_ = new HistoryFocusRow(this.$['sizing-container'], null, new FocusRowDelegate(this)); - this.row_.makeActive(this.ironListTabIndex == 0); - this.listen(this, 'focus', 'onFocus_'); - this.listen(this, 'dom-change', 'onDomChange_'); - }); - }, - detached: function() { - this.unlisten(this, 'focus', 'onFocus_'); - this.unlisten(this, 'dom-change', 'onDomChange_'); - if (this.row_) this.row_.destroy(); - }, - onFocus_: function() { - if (this.lastFocused) this.row_.getEquivalentElement(this.lastFocused).focus(); else this.row_.getFirstFocusable().focus(); - this.tabIndex = -1; - }, - ironListTabIndexChanged_: function() { - if (this.row_) this.row_.makeActive(this.ironListTabIndex == 0); - }, - onDomChange_: function() { - if (this.row_) this.row_.addItems(); - }, - onCheckboxSelected_: function(e) { - this.fire('history-checkbox-select', { - element: this, - shiftKey: e.shiftKey - }); - e.preventDefault(); - }, - onCheckboxMousedown_: function(e) { - if (e.shiftKey) e.preventDefault(); - }, - getEntrySummary_: function() { - var item = this.item; - return loadTimeData.getStringF('entrySummary', item.dateTimeOfDay, item.starred ? loadTimeData.getString('bookmarked') : '', item.title, item.domain); - }, - getAriaChecked_: function(selected) { - return selected ? 'true' : 'false'; - }, - onRemoveBookmarkTap_: function() { - if (!this.item.starred) return; - if (this.$$('#bookmark-star') == this.root.activeElement) this.$['menu-button'].focus(); - var browserService = md_history.BrowserService.getInstance(); - browserService.removeBookmark(this.item.url); - browserService.recordAction('BookmarkStarClicked'); - this.fire('remove-bookmark-stars', this.item.url); - }, - onMenuButtonTap_: function(e) { - this.fire('toggle-menu', { - target: Polymer.dom(e).localTarget, - index: this.index, - item: this.item, - path: this.path - }); - e.stopPropagation(); - }, - onLinkClick_: function() { - var browserService = md_history.BrowserService.getInstance(); - browserService.recordAction('EntryLinkClick'); - if (this.searchTerm) browserService.recordAction('SearchResultClick'); - if (this.index == undefined) return; - browserService.recordHistogram('HistoryPage.ClickPosition', this.index, UMA_MAX_BUCKET_VALUE); - if (this.index <= UMA_MAX_SUBSET_BUCKET_VALUE) { - browserService.recordHistogram('HistoryPage.ClickPositionSubset', this.index, UMA_MAX_SUBSET_BUCKET_VALUE); - } - }, - onLinkRightClick_: function() { - md_history.BrowserService.getInstance().recordAction('EntryLinkRightClick'); - }, - showIcon_: function() { - this.$.icon.style.backgroundImage = cr.icon.getFavicon(this.item.url); - }, - selectionNotAllowed_: function() { - return !loadTimeData.getBoolean('allowDeletingHistory'); - }, - cardTitle_: function(numberOfItems, historyDate, search) { - if (!search) return this.item.dateRelativeDay; - var resultId = numberOfItems == 1 ? 'searchResult' : 'searchResults'; - return loadTimeData.getStringF('foundSearchResults', numberOfItems, loadTimeData.getString(resultId), search); - } - }); - HistoryItem.needsTimeGap = function(visits, currentIndex, searchedTerm) { - if (currentIndex >= visits.length - 1 || visits.length == 0) return false; - var currentItem = visits[currentIndex]; - var nextItem = visits[currentIndex + 1]; - if (searchedTerm) return currentItem.dateShort != nextItem.dateShort; - return currentItem.time - nextItem.time > BROWSING_GAP_TIME && currentItem.dateRelativeDay == nextItem.dateRelativeDay; - }; - return { - HistoryItem: HistoryItem - }; -}); - +function HistoryFocusRow(root,boundary,delegate){cr.ui.FocusRow.call(this,root,boundary,delegate);this.addItems()}HistoryFocusRow.prototype={__proto__:cr.ui.FocusRow.prototype,getCustomEquivalent:function(sampleElement){var equivalent;if(this.getTypeForElement(sampleElement)=="star")equivalent=this.getFirstFocusable("title");return equivalent||cr.ui.FocusRow.prototype.getCustomEquivalent.call(this,sampleElement)},addItems:function(){this.destroy();assert(this.addItem("checkbox","#checkbox"));assert(this.addItem("title","#title"));assert(this.addItem("menu-button","#menu-button"));this.addItem("star","#bookmark-star")}};cr.define("md_history",function(){function FocusRowDelegate(historyItemElement){this.historyItemElement=historyItemElement}FocusRowDelegate.prototype={onFocus:function(row,e){this.historyItemElement.lastFocused=e.path[0]},onKeydown:function(row,e){if(e.key=="Enter")e.stopPropagation();return false}};var HistoryItem=Polymer({is:"history-item",properties:{item:{type:Object,observer:"showIcon_"},searchTerm:{type:String},selected:{type:Boolean,reflectToAttribute:true},isCardStart:{type:Boolean,reflectToAttribute:true},isCardEnd:{type:Boolean,reflectToAttribute:true},embedded:{type:Boolean,reflectToAttribute:true},hasTimeGap:{type:Boolean},numberOfItems:{type:Number},path:String,index:Number,lastFocused:{type:Object,notify:true},ironListTabIndex:{type:Number,observer:"ironListTabIndexChanged_"}},row_:null,attached:function(){Polymer.RenderStatus.afterNextRender(this,function(){this.row_=new HistoryFocusRow(this.$["sizing-container"],null,new FocusRowDelegate(this));this.row_.makeActive(this.ironListTabIndex==0);this.listen(this,"focus","onFocus_");this.listen(this,"dom-change","onDomChange_")})},detached:function(){this.unlisten(this,"focus","onFocus_");this.unlisten(this,"dom-change","onDomChange_");if(this.row_)this.row_.destroy()},onFocus_:function(){if(this.lastFocused)this.row_.getEquivalentElement(this.lastFocused).focus();else this.row_.getFirstFocusable().focus();this.tabIndex=-1},ironListTabIndexChanged_:function(){if(this.row_)this.row_.makeActive(this.ironListTabIndex==0)},onDomChange_:function(){if(this.row_)this.row_.addItems()},onCheckboxSelected_:function(e){this.fire("history-checkbox-select",{element:this,shiftKey:e.shiftKey});e.preventDefault()},onCheckboxMousedown_:function(e){if(e.shiftKey)e.preventDefault()},getEntrySummary_:function(){var item=this.item;return loadTimeData.getStringF("entrySummary",item.dateTimeOfDay,item.starred?loadTimeData.getString("bookmarked"):"",item.title,item.domain)},getAriaChecked_:function(selected){return selected?"true":"false"},onRemoveBookmarkTap_:function(){if(!this.item.starred)return;if(this.$$("#bookmark-star")==this.root.activeElement)this.$["menu-button"].focus();var browserService=md_history.BrowserService.getInstance();browserService.removeBookmark(this.item.url);browserService.recordAction("BookmarkStarClicked");this.fire("remove-bookmark-stars",this.item.url)},onMenuButtonTap_:function(e){this.fire("toggle-menu",{target:Polymer.dom(e).localTarget,index:this.index,item:this.item,path:this.path});e.stopPropagation()},onLinkClick_:function(){var browserService=md_history.BrowserService.getInstance();browserService.recordAction("EntryLinkClick");if(this.searchTerm)browserService.recordAction("SearchResultClick");if(this.index==undefined)return;browserService.recordHistogram("HistoryPage.ClickPosition",this.index,UMA_MAX_BUCKET_VALUE);if(this.index<=UMA_MAX_SUBSET_BUCKET_VALUE){browserService.recordHistogram("HistoryPage.ClickPositionSubset",this.index,UMA_MAX_SUBSET_BUCKET_VALUE)}},onLinkRightClick_:function(){md_history.BrowserService.getInstance().recordAction("EntryLinkRightClick")},showIcon_:function(){this.$.icon.style.backgroundImage=cr.icon.getFavicon(this.item.url)},selectionNotAllowed_:function(){return!loadTimeData.getBoolean("allowDeletingHistory")},cardTitle_:function(numberOfItems,historyDate,search){if(!search)return this.item.dateRelativeDay;var resultId=numberOfItems==1?"searchResult":"searchResults";return loadTimeData.getStringF("foundSearchResults",numberOfItems,loadTimeData.getString(resultId),search)}});HistoryItem.needsTimeGap=function(visits,currentIndex,searchedTerm){if(currentIndex>=visits.length-1||visits.length==0)return false;var currentItem=visits[currentIndex];var nextItem=visits[currentIndex+1];if(searchedTerm)return currentItem.dateShort!=nextItem.dateShort;return currentItem.time-nextItem.time>BROWSING_GAP_TIME&¤tItem.dateRelativeDay==nextItem.dateRelativeDay};return{HistoryItem:HistoryItem}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var SelectionTreeNode = function(currentPath) { - this.currentPath = currentPath; - this.leaf = false; - this.indexes = []; - this.children = []; -}; - -SelectionTreeNode.prototype.addChild = function(index, path) { - this.indexes.push(index); - this.children[index] = new SelectionTreeNode(path); -}; - -var HistoryListBehavior = { - properties: { - selectedPaths: { - type: Object, - value: function() { - return new Set(); - } - }, - lastSelectedPath: String - }, - listeners: { - 'history-checkbox-select': 'itemSelected_' - }, - hasResults: function(historyDataLength) { - return historyDataLength > 0; - }, - noResultsMessage: function(searchedTerm, isLoading) { - if (isLoading) return ''; - var messageId = searchedTerm !== '' ? 'noSearchResults' : 'noResults'; - return loadTimeData.getString(messageId); - }, - unselectAllItems: function() { - this.selectedPaths.forEach(function(path) { - this.set(path + '.selected', false); - }.bind(this)); - this.selectedPaths.clear(); - }, - deleteSelected: function() { - var toBeRemoved = Array.from(this.selectedPaths.values()).map(function(path) { - return this.get(path); - }.bind(this)); - md_history.BrowserService.getInstance().deleteItems(toBeRemoved).then(function() { - this.removeItemsByPath(Array.from(this.selectedPaths)); - this.fire('unselect-all'); - }.bind(this)); - }, - removeItemsByPath: function(paths) { - if (paths.length == 0) return; - this.removeItemsBeneathNode_(this.buildRemovalTree_(paths)); - }, - buildRemovalTree_: function(paths) { - var rootNode = new SelectionTreeNode(paths[0].split('.')[0]); - paths.forEach(function(path) { - var components = path.split('.'); - var node = rootNode; - components.shift(); - while (components.length > 1) { - var index = Number(components.shift()); - var arrayName = components.shift(); - if (!node.children[index]) node.addChild(index, [ node.currentPath, index, arrayName ].join('.')); - node = node.children[index]; - } - node.leaf = true; - node.indexes.push(Number(components.shift())); - }); - return rootNode; - }, - removeItemsBeneathNode_: function(node) { - var array = this.get(node.currentPath); - var splices = []; - node.indexes.sort(function(a, b) { - return b - a; - }); - node.indexes.forEach(function(index) { - if (node.leaf || this.removeItemsBeneathNode_(node.children[index])) { - var item = array.splice(index, 1)[0]; - splices.push({ - index: index, - removed: [ item ], - addedCount: 0, - object: array, - type: 'splice' - }); - } - }.bind(this)); - if (array.length == 0 && node.currentPath.indexOf('.') != -1) return true; - this.notifySplices(node.currentPath, splices); - return false; - }, - itemSelected_: function(e) { - var item = e.detail.element; - var paths = []; - var itemPath = item.path; - if (e.detail.shiftKey && this.lastSelectedPath) { - var itemPathComponents = itemPath.split('.'); - var itemIndex = Number(itemPathComponents.pop()); - var itemArrayPath = itemPathComponents.join('.'); - var lastItemPathComponents = this.lastSelectedPath.split('.'); - var lastItemIndex = Number(lastItemPathComponents.pop()); - if (itemArrayPath == lastItemPathComponents.join('.')) { - for (var i = Math.min(itemIndex, lastItemIndex); i <= Math.max(itemIndex, lastItemIndex); i++) { - paths.push(itemArrayPath + '.' + i); - } - } - } - if (paths.length == 0) paths.push(item.path); - var selected = !this.selectedPaths.has(item.path); - paths.forEach(function(path) { - this.set(path + '.selected', selected); - if (selected) { - this.selectedPaths.add(path); - return; - } - this.selectedPaths.delete(path); - }.bind(this)); - this.lastSelectedPath = itemPath; - } -}; - +var SelectionTreeNode=function(currentPath){this.currentPath=currentPath;this.leaf=false;this.indexes=[];this.children=[]};SelectionTreeNode.prototype.addChild=function(index,path){this.indexes.push(index);this.children[index]=new SelectionTreeNode(path)};var HistoryListBehavior={properties:{selectedPaths:{type:Object,value:function(){return new Set}},lastSelectedPath:String},listeners:{"history-checkbox-select":"itemSelected_"},hasResults:function(historyDataLength){return historyDataLength>0},noResultsMessage:function(searchedTerm,isLoading){if(isLoading)return"";var messageId=searchedTerm!==""?"noSearchResults":"noResults";return loadTimeData.getString(messageId)},unselectAllItems:function(){this.selectedPaths.forEach(function(path){this.set(path+".selected",false)}.bind(this));this.selectedPaths.clear()},deleteSelected:function(){var toBeRemoved=Array.from(this.selectedPaths.values()).map(function(path){return this.get(path)}.bind(this));md_history.BrowserService.getInstance().deleteItems(toBeRemoved).then(function(){this.removeItemsByPath(Array.from(this.selectedPaths));this.fire("unselect-all")}.bind(this))},removeItemsByPath:function(paths){if(paths.length==0)return;this.removeItemsBeneathNode_(this.buildRemovalTree_(paths))},buildRemovalTree_:function(paths){var rootNode=new SelectionTreeNode(paths[0].split(".")[0]);paths.forEach(function(path){var components=path.split(".");var node=rootNode;components.shift();while(components.length>1){var index=Number(components.shift());var arrayName=components.shift();if(!node.children[index])node.addChild(index,[node.currentPath,index,arrayName].join("."));node=node.children[index]}node.leaf=true;node.indexes.push(Number(components.shift()))});return rootNode},removeItemsBeneathNode_:function(node){var array=this.get(node.currentPath);var splices=[];node.indexes.sort(function(a,b){return b-a});node.indexes.forEach(function(index){if(node.leaf||this.removeItemsBeneathNode_(node.children[index])){var item=array.splice(index,1)[0];splices.push({index:index,removed:[item],addedCount:0,object:array,type:"splice"})}}.bind(this));if(array.length==0&&node.currentPath.indexOf(".")!=-1)return true;this.notifySplices(node.currentPath,splices);return false},itemSelected_:function(e){var item=e.detail.element;var paths=[];var itemPath=item.path;if(e.detail.shiftKey&&this.lastSelectedPath){var itemPathComponents=itemPath.split(".");var itemIndex=Number(itemPathComponents.pop());var itemArrayPath=itemPathComponents.join(".");var lastItemPathComponents=this.lastSelectedPath.split(".");var lastItemIndex=Number(lastItemPathComponents.pop());if(itemArrayPath==lastItemPathComponents.join(".")){for(var i=Math.min(itemIndex,lastItemIndex);i<=Math.max(itemIndex,lastItemIndex);i++){paths.push(itemArrayPath+"."+i)}}}if(paths.length==0)paths.push(item.path);var selected=!this.selectedPaths.has(item.path);paths.forEach(function(path){this.set(path+".selected",selected);if(selected){this.selectedPaths.add(path);return}this.selectedPaths.delete(path)}.bind(this));this.lastSelectedPath=itemPath}}; // 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. -Polymer({ - is: 'history-list', - behaviors: [ HistoryListBehavior ], - properties: { - searchedTerm: { - type: String, - value: '' - }, - querying: Boolean, - historyData_: Array, - resultLoadingDisabled_: { - type: Boolean, - value: false - }, - lastFocused_: Object - }, - listeners: { - scroll: 'notifyListScroll_', - 'remove-bookmark-stars': 'removeBookmarkStars_' - }, - attached: function() { - this.$['infinite-list'].notifyResize(); - this.$['infinite-list'].scrollTarget = this; - this.$['scroll-threshold'].scrollTarget = this; - }, - removeBookmarkStars_: function(e) { - var url = e.detail; - if (this.historyData_ === undefined) return; - for (var i = 0; i < this.historyData_.length; i++) { - if (this.historyData_[i].url == url) this.set('historyData_.' + i + '.starred', false); - } - }, - disableResultLoading: function() { - this.resultLoadingDisabled_ = true; - }, - addNewResults: function(historyResults, incremental) { - var results = historyResults.slice(); - this.$['scroll-threshold'].clearTriggers(); - if (!incremental) { - this.resultLoadingDisabled_ = false; - if (this.historyData_) this.splice('historyData_', 0, this.historyData_.length); - this.fire('unselect-all'); - } - if (this.historyData_) { - results.unshift('historyData_'); - this.push.apply(this, results); - } else { - this.set('historyData_', results); - } - }, - loadMoreData_: function() { - if (this.resultLoadingDisabled_ || this.querying) return; - this.fire('load-more-history'); - }, - needsTimeGap_: function(item, index, length) { - return md_history.HistoryItem.needsTimeGap(this.historyData_, index, this.searchedTerm); - }, - isCardStart_: function(item, i, length) { - if (length == 0 || i > length - 1) return false; - return i == 0 || this.historyData_[i].dateRelativeDay != this.historyData_[i - 1].dateRelativeDay; - }, - isCardEnd_: function(item, i, length) { - if (length == 0 || i > length - 1) return false; - return i == length - 1 || this.historyData_[i].dateRelativeDay != this.historyData_[i + 1].dateRelativeDay; - }, - notifyListScroll_: function() { - this.fire('history-list-scrolled'); - }, - pathForItem_: function(index) { - return 'historyData_.' + index; - } -}); - +Polymer({is:"history-list",behaviors:[HistoryListBehavior],properties:{searchedTerm:{type:String,value:""},querying:Boolean,historyData_:Array,resultLoadingDisabled_:{type:Boolean,value:false},lastFocused_:Object},listeners:{scroll:"notifyListScroll_","remove-bookmark-stars":"removeBookmarkStars_"},attached:function(){this.$["infinite-list"].notifyResize();this.$["infinite-list"].scrollTarget=this;this.$["scroll-threshold"].scrollTarget=this},removeBookmarkStars_:function(e){var url=e.detail;if(this.historyData_===undefined)return;for(var i=0;i<this.historyData_.length;i++){if(this.historyData_[i].url==url)this.set("historyData_."+i+".starred",false)}},disableResultLoading:function(){this.resultLoadingDisabled_=true},addNewResults:function(historyResults,incremental){var results=historyResults.slice();this.$["scroll-threshold"].clearTriggers();if(!incremental){this.resultLoadingDisabled_=false;if(this.historyData_)this.splice("historyData_",0,this.historyData_.length);this.fire("unselect-all")}if(this.historyData_){results.unshift("historyData_");this.push.apply(this,results)}else{this.set("historyData_",results)}},loadMoreData_:function(){if(this.resultLoadingDisabled_||this.querying)return;this.fire("load-more-history")},needsTimeGap_:function(item,index,length){return md_history.HistoryItem.needsTimeGap(this.historyData_,index,this.searchedTerm)},isCardStart_:function(item,i,length){if(length==0||i>length-1)return false;return i==0||this.historyData_[i].dateRelativeDay!=this.historyData_[i-1].dateRelativeDay},isCardEnd_:function(item,i,length){if(length==0||i>length-1)return false;return i==length-1||this.historyData_[i].dateRelativeDay!=this.historyData_[i+1].dateRelativeDay},notifyListScroll_:function(){this.fire("history-list-scrolled")},pathForItem_:function(index){return"historyData_."+index}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'history-list-container', - properties: { - selectedPage_: String, - grouped: Boolean, - groupedRange: { - type: Number, - observer: 'groupedRangeChanged_' - }, - queryState: Object, - queryResult: Object - }, - observers: [ 'searchTermChanged_(queryState.searchTerm)' ], - listeners: { - 'history-list-scrolled': 'closeMenu_', - 'load-more-history': 'loadMoreHistory_', - 'toggle-menu': 'toggleMenu_' - }, - historyResult: function(info, results) { - this.initializeResults_(info, results); - this.closeMenu_(); - if (this.selectedPage_ == 'grouped-list') { - this.$$('#grouped-list').historyData = results; - return; - } - var list = this.$['infinite-list']; - list.addNewResults(results, this.queryState.incremental); - if (info.finished) list.disableResultLoading(); - }, - queryHistory: function(incremental) { - var queryState = this.queryState; - var noResults = !this.queryResult || this.queryResult.results == null; - if (queryState.queryingDisabled || !this.queryState.searchTerm && noResults) { - return; - } - var dialog = this.$.dialog.getIfExists(); - if (!incremental && dialog && dialog.open) dialog.close(); - this.set('queryState.querying', true); - this.set('queryState.incremental', incremental); - var lastVisitTime = 0; - if (incremental) { - var lastVisit = this.queryResult.results.slice(-1)[0]; - lastVisitTime = lastVisit ? lastVisit.time : 0; - } - var maxResults = this.groupedRange == HistoryRange.ALL_TIME ? RESULTS_PER_PAGE : 0; - chrome.send('queryHistory', [ queryState.searchTerm, queryState.groupedOffset, queryState.range, lastVisitTime, maxResults ]); - }, - historyDeleted: function() { - if (this.getSelectedItemCount() > 0) return; - this.queryHistory(false); - }, - getContentScrollTarget: function() { - return this.getSelectedList_(); - }, - getSelectedItemCount: function() { - return this.getSelectedList_().selectedPaths.size; - }, - unselectAllItems: function(count) { - var selectedList = this.getSelectedList_(); - if (selectedList) selectedList.unselectAllItems(count); - }, - deleteSelectedWithPrompt: function() { - if (!loadTimeData.getBoolean('allowDeletingHistory')) return; - var browserService = md_history.BrowserService.getInstance(); - browserService.recordAction('RemoveSelected'); - if (this.queryState.searchTerm != '') browserService.recordAction('SearchResultRemove'); - this.$.dialog.get().showModal(); - }, - groupedRangeChanged_: function(range, oldRange) { - this.selectedPage_ = range == HistoryRange.ALL_TIME ? 'infinite-list' : 'grouped-list'; - if (oldRange == undefined) return; - this.queryHistory(false); - this.fire('history-view-changed'); - }, - searchTermChanged_: function() { - this.queryHistory(false); - if (this.queryState.searchTerm) md_history.BrowserService.getInstance().recordAction('Search'); - }, - loadMoreHistory_: function() { - this.queryHistory(true); - }, - initializeResults_: function(info, results) { - if (results.length == 0) return; - var currentDate = results[0].dateRelativeDay; - for (var i = 0; i < results.length; i++) { - results[i].selected = false; - results[i].readableTimestamp = info.term == '' ? results[i].dateTimeOfDay : results[i].dateShort; - if (results[i].dateRelativeDay != currentDate) { - currentDate = results[i].dateRelativeDay; - } - } - }, - onDialogConfirmTap_: function() { - md_history.BrowserService.getInstance().recordAction('ConfirmRemoveSelected'); - this.getSelectedList_().deleteSelected(); - var dialog = assert(this.$.dialog.getIfExists()); - dialog.close(); - }, - onDialogCancelTap_: function() { - md_history.BrowserService.getInstance().recordAction('CancelRemoveSelected'); - var dialog = assert(this.$.dialog.getIfExists()); - dialog.close(); - }, - closeMenu_: function() { - var menu = this.$.sharedMenu.getIfExists(); - if (menu) menu.closeMenu(); - }, - toggleMenu_: function(e) { - var target = e.detail.target; - var menu = this.$.sharedMenu.get(); - menu.toggleMenu(target, e.detail); - }, - onMoreFromSiteTap_: function() { - md_history.BrowserService.getInstance().recordAction('EntryMenuShowMoreFromSite'); - var menu = assert(this.$.sharedMenu.getIfExists()); - this.set('queryState.searchTerm', menu.itemData.item.domain); - menu.closeMenu(); - }, - onRemoveFromHistoryTap_: function() { - var browserService = md_history.BrowserService.getInstance(); - browserService.recordAction('EntryMenuRemoveFromHistory'); - var menu = assert(this.$.sharedMenu.getIfExists()); - var itemData = menu.itemData; - browserService.deleteItems([ itemData.item ]).then(function(items) { - this.getSelectedList_().removeItemsByPath([ itemData.path ]); - this.fire('unselect-all'); - var index = itemData.index; - if (index == undefined) return; - var browserService = md_history.BrowserService.getInstance(); - browserService.recordHistogram('HistoryPage.RemoveEntryPosition', index, UMA_MAX_BUCKET_VALUE); - if (index <= UMA_MAX_SUBSET_BUCKET_VALUE) { - browserService.recordHistogram('HistoryPage.RemoveEntryPositionSubset', index, UMA_MAX_SUBSET_BUCKET_VALUE); - } - }.bind(this)); - menu.closeMenu(); - }, - getSelectedList_: function() { - return this.$.content.selectedItem; - } -}); - -(function() { - 'use strict'; - Polymer({ - is: 'iron-location', - properties: { - path: { - type: String, - notify: true, - value: function() { - return window.decodeURIComponent(window.location.pathname); - } - }, - query: { - type: String, - notify: true, - value: function() { - return window.decodeURIComponent(window.location.search.slice(1)); - } - }, - hash: { - type: String, - notify: true, - value: function() { - return window.decodeURIComponent(window.location.hash.slice(1)); - } - }, - dwellTime: { - type: Number, - value: 2e3 - }, - urlSpaceRegex: { - type: String, - value: '' - }, - _urlSpaceRegExp: { - computed: '_makeRegExp(urlSpaceRegex)' - }, - _lastChangedAt: { - type: Number - }, - _initialized: { - type: Boolean, - value: false - } - }, - hostAttributes: { - hidden: true - }, - observers: [ '_updateUrl(path, query, hash)' ], - attached: function() { - this.listen(window, 'hashchange', '_hashChanged'); - this.listen(window, 'location-changed', '_urlChanged'); - this.listen(window, 'popstate', '_urlChanged'); - this.listen(document.body, 'click', '_globalOnClick'); - this._lastChangedAt = window.performance.now() - (this.dwellTime - 200); - this._initialized = true; - this._urlChanged(); - }, - detached: function() { - this.unlisten(window, 'hashchange', '_hashChanged'); - this.unlisten(window, 'location-changed', '_urlChanged'); - this.unlisten(window, 'popstate', '_urlChanged'); - this.unlisten(document.body, 'click', '_globalOnClick'); - this._initialized = false; - }, - _hashChanged: function() { - this.hash = window.decodeURIComponent(window.location.hash.substring(1)); - }, - _urlChanged: function() { - this._dontUpdateUrl = true; - this._hashChanged(); - this.path = window.decodeURIComponent(window.location.pathname); - this.query = window.decodeURIComponent(window.location.search.substring(1)); - this._dontUpdateUrl = false; - this._updateUrl(); - }, - _getUrl: function() { - var partiallyEncodedPath = window.encodeURI(this.path).replace(/\#/g, '%23').replace(/\?/g, '%3F'); - var partiallyEncodedQuery = ''; - if (this.query) { - partiallyEncodedQuery = '?' + window.encodeURI(this.query).replace(/\#/g, '%23'); - } - var partiallyEncodedHash = ''; - if (this.hash) { - partiallyEncodedHash = '#' + window.encodeURI(this.hash); - } - return partiallyEncodedPath + partiallyEncodedQuery + partiallyEncodedHash; - }, - _updateUrl: function() { - if (this._dontUpdateUrl || !this._initialized) { - return; - } - if (this.path === window.decodeURIComponent(window.location.pathname) && this.query === window.decodeURIComponent(window.location.search.substring(1)) && this.hash === window.decodeURIComponent(window.location.hash.substring(1))) { - return; - } - var newUrl = this._getUrl(); - var fullNewUrl = new URL(newUrl, window.location.protocol + '//' + window.location.host).href; - var now = window.performance.now(); - var shouldReplace = this._lastChangedAt + this.dwellTime > now; - this._lastChangedAt = now; - if (shouldReplace) { - window.history.replaceState({}, '', fullNewUrl); - } else { - window.history.pushState({}, '', fullNewUrl); - } - this.fire('location-changed', {}, { - node: window - }); - }, - _globalOnClick: function(event) { - if (event.defaultPrevented) { - return; - } - var href = this._getSameOriginLinkHref(event); - if (!href) { - return; - } - event.preventDefault(); - if (href === window.location.href) { - return; - } - window.history.pushState({}, '', href); - this.fire('location-changed', {}, { - node: window - }); - }, - _getSameOriginLinkHref: function(event) { - if (event.button !== 0) { - return null; - } - if (event.metaKey || event.ctrlKey) { - return null; - } - var eventPath = Polymer.dom(event).path; - var anchor = null; - for (var i = 0; i < eventPath.length; i++) { - var element = eventPath[i]; - if (element.tagName === 'A' && element.href) { - anchor = element; - break; - } - } - if (!anchor) { - return null; - } - if (anchor.target === '_blank') { - return null; - } - if ((anchor.target === '_top' || anchor.target === '_parent') && window.top !== window) { - return null; - } - var href = anchor.href; - var url; - if (document.baseURI != null) { - url = new URL(href, document.baseURI); - } else { - url = new URL(href); - } - var origin; - if (window.location.origin) { - origin = window.location.origin; - } else { - origin = window.location.protocol + '//' + window.location.hostname; - if (window.location.port) { - origin += ':' + window.location.port; - } - } - if (url.origin !== origin) { - return null; - } - var normalizedHref = url.pathname + url.search + url.hash; - if (this._urlSpaceRegExp && !this._urlSpaceRegExp.test(normalizedHref)) { - return null; - } - var fullNormalizedHref = new URL(normalizedHref, window.location.href).href; - return fullNormalizedHref; - }, - _makeRegExp: function(urlSpaceRegex) { - return RegExp(urlSpaceRegex); - } - }); -})(); - -'use strict'; - -Polymer({ - is: 'iron-query-params', - properties: { - paramsString: { - type: String, - notify: true, - observer: 'paramsStringChanged' - }, - paramsObject: { - type: Object, - notify: true, - value: function() { - return {}; - } - }, - _dontReact: { - type: Boolean, - value: false - } - }, - hostAttributes: { - hidden: true - }, - observers: [ 'paramsObjectChanged(paramsObject.*)' ], - paramsStringChanged: function() { - this._dontReact = true; - this.paramsObject = this._decodeParams(this.paramsString); - this._dontReact = false; - }, - paramsObjectChanged: function() { - if (this._dontReact) { - return; - } - this.paramsString = this._encodeParams(this.paramsObject); - }, - _encodeParams: function(params) { - var encodedParams = []; - for (var key in params) { - var value = params[key]; - if (value === '') { - encodedParams.push(encodeURIComponent(key)); - } else if (value) { - encodedParams.push(encodeURIComponent(key) + '=' + encodeURIComponent(value.toString())); - } - } - return encodedParams.join('&'); - }, - _decodeParams: function(paramString) { - var params = {}; - paramString = (paramString || '').replace(/\+/g, '%20'); - var paramList = paramString.split('&'); - for (var i = 0; i < paramList.length; i++) { - var param = paramList[i].split('='); - if (param[0]) { - params[decodeURIComponent(param[0])] = decodeURIComponent(param[1] || ''); - } - } - return params; - } -}); - +Polymer({is:"history-list-container",properties:{selectedPage_:String,grouped:Boolean,groupedRange:{type:Number,observer:"groupedRangeChanged_"},queryState:Object,queryResult:Object},observers:["searchTermChanged_(queryState.searchTerm)"],listeners:{"history-list-scrolled":"closeMenu_","load-more-history":"loadMoreHistory_","toggle-menu":"toggleMenu_"},historyResult:function(info,results){this.initializeResults_(info,results);this.closeMenu_();if(this.selectedPage_=="grouped-list"){this.$$("#grouped-list").historyData=results;return}var list=this.$["infinite-list"];list.addNewResults(results,this.queryState.incremental);if(info.finished)list.disableResultLoading()},queryHistory:function(incremental){var queryState=this.queryState;var noResults=!this.queryResult||this.queryResult.results==null;if(queryState.queryingDisabled||!this.queryState.searchTerm&&noResults){return}var dialog=this.$.dialog.getIfExists();if(!incremental&&dialog&&dialog.open)dialog.close();this.set("queryState.querying",true);this.set("queryState.incremental",incremental);var lastVisitTime=0;if(incremental){var lastVisit=this.queryResult.results.slice(-1)[0];lastVisitTime=lastVisit?lastVisit.time:0}var maxResults=this.groupedRange==HistoryRange.ALL_TIME?RESULTS_PER_PAGE:0;chrome.send("queryHistory",[queryState.searchTerm,queryState.groupedOffset,queryState.range,lastVisitTime,maxResults])},historyDeleted:function(){if(this.getSelectedItemCount()>0)return;this.queryHistory(false)},getContentScrollTarget:function(){return this.getSelectedList_()},getSelectedItemCount:function(){return this.getSelectedList_().selectedPaths.size},unselectAllItems:function(count){var selectedList=this.getSelectedList_();if(selectedList)selectedList.unselectAllItems(count)},deleteSelectedWithPrompt:function(){if(!loadTimeData.getBoolean("allowDeletingHistory"))return;var browserService=md_history.BrowserService.getInstance();browserService.recordAction("RemoveSelected");if(this.queryState.searchTerm!="")browserService.recordAction("SearchResultRemove");this.$.dialog.get().showModal()},groupedRangeChanged_:function(range,oldRange){this.selectedPage_=range==HistoryRange.ALL_TIME?"infinite-list":"grouped-list";if(oldRange==undefined)return;this.queryHistory(false);this.fire("history-view-changed")},searchTermChanged_:function(){this.queryHistory(false);if(this.queryState.searchTerm)md_history.BrowserService.getInstance().recordAction("Search")},loadMoreHistory_:function(){this.queryHistory(true)},initializeResults_:function(info,results){if(results.length==0)return;var currentDate=results[0].dateRelativeDay;for(var i=0;i<results.length;i++){results[i].selected=false;results[i].readableTimestamp=info.term==""?results[i].dateTimeOfDay:results[i].dateShort;if(results[i].dateRelativeDay!=currentDate){currentDate=results[i].dateRelativeDay}}},onDialogConfirmTap_:function(){md_history.BrowserService.getInstance().recordAction("ConfirmRemoveSelected");this.getSelectedList_().deleteSelected();var dialog=assert(this.$.dialog.getIfExists());dialog.close()},onDialogCancelTap_:function(){md_history.BrowserService.getInstance().recordAction("CancelRemoveSelected");var dialog=assert(this.$.dialog.getIfExists());dialog.close()},closeMenu_:function(){var menu=this.$.sharedMenu.getIfExists();if(menu)menu.closeMenu()},toggleMenu_:function(e){var target=e.detail.target;var menu=this.$.sharedMenu.get();menu.toggleMenu(target,e.detail)},onMoreFromSiteTap_:function(){md_history.BrowserService.getInstance().recordAction("EntryMenuShowMoreFromSite");var menu=assert(this.$.sharedMenu.getIfExists());this.set("queryState.searchTerm",menu.itemData.item.domain);menu.closeMenu()},onRemoveFromHistoryTap_:function(){var browserService=md_history.BrowserService.getInstance();browserService.recordAction("EntryMenuRemoveFromHistory");var menu=assert(this.$.sharedMenu.getIfExists());var itemData=menu.itemData;browserService.deleteItems([itemData.item]).then(function(items){this.getSelectedList_().removeItemsByPath([itemData.path]);this.fire("unselect-all");var index=itemData.index;if(index==undefined)return;var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram("HistoryPage.RemoveEntryPosition",index,UMA_MAX_BUCKET_VALUE);if(index<=UMA_MAX_SUBSET_BUCKET_VALUE){browserService.recordHistogram("HistoryPage.RemoveEntryPositionSubset",index,UMA_MAX_SUBSET_BUCKET_VALUE)}}.bind(this));menu.closeMenu()},getSelectedList_:function(){return this.$.content.selectedItem}});(function(){"use strict";Polymer({is:"iron-location",properties:{path:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.pathname)}},query:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.search.slice(1))}},hash:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.hash.slice(1))}},dwellTime:{type:Number,value:2e3},urlSpaceRegex:{type:String,value:""},_urlSpaceRegExp:{computed:"_makeRegExp(urlSpaceRegex)"},_lastChangedAt:{type:Number},_initialized:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["_updateUrl(path, query, hash)"],attached:function(){this.listen(window,"hashchange","_hashChanged");this.listen(window,"location-changed","_urlChanged");this.listen(window,"popstate","_urlChanged");this.listen(document.body,"click","_globalOnClick");this._lastChangedAt=window.performance.now()-(this.dwellTime-200);this._initialized=true;this._urlChanged()},detached:function(){this.unlisten(window,"hashchange","_hashChanged");this.unlisten(window,"location-changed","_urlChanged");this.unlisten(window,"popstate","_urlChanged");this.unlisten(document.body,"click","_globalOnClick");this._initialized=false},_hashChanged:function(){this.hash=window.decodeURIComponent(window.location.hash.substring(1))},_urlChanged:function(){this._dontUpdateUrl=true;this._hashChanged();this.path=window.decodeURIComponent(window.location.pathname);this.query=window.decodeURIComponent(window.location.search.substring(1));this._dontUpdateUrl=false;this._updateUrl()},_getUrl:function(){var partiallyEncodedPath=window.encodeURI(this.path).replace(/\#/g,"%23").replace(/\?/g,"%3F");var partiallyEncodedQuery="";if(this.query){partiallyEncodedQuery="?"+window.encodeURI(this.query).replace(/\#/g,"%23")}var partiallyEncodedHash="";if(this.hash){partiallyEncodedHash="#"+window.encodeURI(this.hash)}return partiallyEncodedPath+partiallyEncodedQuery+partiallyEncodedHash},_updateUrl:function(){if(this._dontUpdateUrl||!this._initialized){return}if(this.path===window.decodeURIComponent(window.location.pathname)&&this.query===window.decodeURIComponent(window.location.search.substring(1))&&this.hash===window.decodeURIComponent(window.location.hash.substring(1))){return}var newUrl=this._getUrl();var fullNewUrl=new URL(newUrl,window.location.protocol+"//"+window.location.host).href;var now=window.performance.now();var shouldReplace=this._lastChangedAt+this.dwellTime>now;this._lastChangedAt=now;if(shouldReplace){window.history.replaceState({},"",fullNewUrl)}else{window.history.pushState({},"",fullNewUrl)}this.fire("location-changed",{},{node:window})},_globalOnClick:function(event){if(event.defaultPrevented){return}var href=this._getSameOriginLinkHref(event);if(!href){return}event.preventDefault();if(href===window.location.href){return}window.history.pushState({},"",href);this.fire("location-changed",{},{node:window})},_getSameOriginLinkHref:function(event){if(event.button!==0){return null}if(event.metaKey||event.ctrlKey){return null}var eventPath=Polymer.dom(event).path;var anchor=null;for(var i=0;i<eventPath.length;i++){var element=eventPath[i];if(element.tagName==="A"&&element.href){anchor=element;break}}if(!anchor){return null}if(anchor.target==="_blank"){return null}if((anchor.target==="_top"||anchor.target==="_parent")&&window.top!==window){return null}var href=anchor.href;var url;if(document.baseURI!=null){url=new URL(href,document.baseURI)}else{url=new URL(href)}var origin;if(window.location.origin){origin=window.location.origin}else{origin=window.location.protocol+"//"+window.location.hostname;if(window.location.port){origin+=":"+window.location.port}}if(url.origin!==origin){return null}var normalizedHref=url.pathname+url.search+url.hash;if(this._urlSpaceRegExp&&!this._urlSpaceRegExp.test(normalizedHref)){return null}var fullNormalizedHref=new URL(normalizedHref,window.location.href).href;return fullNormalizedHref},_makeRegExp:function(urlSpaceRegex){return RegExp(urlSpaceRegex)}})})();"use strict";Polymer({is:"iron-query-params",properties:{paramsString:{type:String,notify:true,observer:"paramsStringChanged"},paramsObject:{type:Object,notify:true,value:function(){return{}}},_dontReact:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["paramsObjectChanged(paramsObject.*)"],paramsStringChanged:function(){this._dontReact=true;this.paramsObject=this._decodeParams(this.paramsString);this._dontReact=false},paramsObjectChanged:function(){if(this._dontReact){return}this.paramsString=this._encodeParams(this.paramsObject)},_encodeParams:function(params){var encodedParams=[];for(var key in params){var value=params[key];if(value===""){encodedParams.push(encodeURIComponent(key))}else if(value){encodedParams.push(encodeURIComponent(key)+"="+encodeURIComponent(value.toString()))}}return encodedParams.join("&")},_decodeParams:function(paramString){var params={};paramString=(paramString||"").replace(/\+/g,"%20");var paramList=paramString.split("&");for(var i=0;i<paramList.length;i++){var param=paramList[i].split("=");if(param[0]){params[decodeURIComponent(param[0])]=decodeURIComponent(param[1]||"")}}return params}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'history-router', - properties: { - selectedPage: { - type: String, - observer: 'serializePath_', - notify: true - }, - queryState: { - type: Object, - notify: true - }, - path_: { - type: String, - observer: 'pathChanged_' - }, - queryParams_: Object - }, - observers: [ 'queryParamsChanged_(queryParams_.*)', 'searchTermChanged_(queryState.searchTerm)' ], - attached: function() { - if (window.location.hash) { - window.location.href = window.location.href.split('#')[0] + '?' + window.location.hash.substr(1); - } - }, - serializePath_: function() { - var page = this.selectedPage == 'history' ? '' : this.selectedPage; - this.path_ = '/' + page; - }, - pathChanged_: function() { - var sections = this.path_.substr(1).split('/'); - this.selectedPage = sections[0] || 'history'; - }, - queryParamsChanged_: function() { - this.set('queryState.searchTerm', this.queryParams_.q || ''); - }, - searchTermChanged_: function() { - this.set('queryParams_.q', this.queryState.searchTerm || null); - } -}); - -Polymer.IronMultiSelectableBehaviorImpl = { - properties: { - multi: { - type: Boolean, - value: false, - observer: 'multiChanged' - }, - selectedValues: { - type: Array, - notify: true - }, - selectedItems: { - type: Array, - readOnly: true, - notify: true - } - }, - observers: [ '_updateSelected(selectedValues.splices)' ], - select: function(value) { - if (this.multi) { - if (this.selectedValues) { - this._toggleSelected(value); - } else { - this.selectedValues = [ value ]; - } - } else { - this.selected = value; - } - }, - multiChanged: function(multi) { - this._selection.multi = multi; - }, - get _shouldUpdateSelection() { - return this.selected != null || this.selectedValues != null && this.selectedValues.length; - }, - _updateAttrForSelected: function() { - if (!this.multi) { - Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this); - } else if (this._shouldUpdateSelection) { - this.selectedValues = this.selectedItems.map(function(selectedItem) { - return this._indexToValue(this.indexOf(selectedItem)); - }, this).filter(function(unfilteredValue) { - return unfilteredValue != null; - }, this); - } - }, - _updateSelected: function() { - if (this.multi) { - this._selectMulti(this.selectedValues); - } else { - this._selectSelected(this.selected); - } - }, - _selectMulti: function(values) { - if (values) { - var selectedItems = this._valuesToItems(values); - this._selection.clear(selectedItems); - for (var i = 0; i < selectedItems.length; i++) { - this._selection.setItemSelected(selectedItems[i], true); - } - if (this.fallbackSelection && this.items.length && !this._selection.get().length) { - var fallback = this._valueToItem(this.fallbackSelection); - if (fallback) { - this.selectedValues = [ this.fallbackSelection ]; - } - } - } else { - this._selection.clear(); - } - }, - _selectionChange: function() { - var s = this._selection.get(); - if (this.multi) { - this._setSelectedItems(s); - } else { - this._setSelectedItems([ s ]); - this._setSelectedItem(s); - } - }, - _toggleSelected: function(value) { - var i = this.selectedValues.indexOf(value); - var unselected = i < 0; - if (unselected) { - this.push('selectedValues', value); - } else { - this.splice('selectedValues', i, 1); - } - }, - _valuesToItems: function(values) { - return values == null ? null : values.map(function(value) { - return this._valueToItem(value); - }, this); - } -}; - -Polymer.IronMultiSelectableBehavior = [ Polymer.IronSelectableBehavior, Polymer.IronMultiSelectableBehaviorImpl ]; - -Polymer({ - is: 'iron-selector', - behaviors: [ Polymer.IronMultiSelectableBehavior ] -}); - +Polymer({is:"history-router",properties:{selectedPage:{type:String,observer:"serializePath_",notify:true},queryState:{type:Object,notify:true},path_:{type:String,observer:"pathChanged_"},queryParams_:Object},observers:["queryParamsChanged_(queryParams_.*)","searchTermChanged_(queryState.searchTerm)"],attached:function(){if(window.location.hash){window.location.href=window.location.href.split("#")[0]+"?"+window.location.hash.substr(1)}},serializePath_:function(){var page=this.selectedPage=="history"?"":this.selectedPage;this.path_="/"+page},pathChanged_:function(){var sections=this.path_.substr(1).split("/");this.selectedPage=sections[0]||"history"},queryParamsChanged_:function(){this.set("queryState.searchTerm",this.queryParams_.q||"")},searchTermChanged_:function(){this.set("queryParams_.q",this.queryState.searchTerm||null)}});Polymer.IronMultiSelectableBehaviorImpl={properties:{multi:{type:Boolean,value:false,observer:"multiChanged"},selectedValues:{type:Array,notify:true},selectedItems:{type:Array,readOnly:true,notify:true}},observers:["_updateSelected(selectedValues.splices)"],select:function(value){if(this.multi){if(this.selectedValues){this._toggleSelected(value)}else{this.selectedValues=[value]}}else{this.selected=value}},multiChanged:function(multi){this._selection.multi=multi},get _shouldUpdateSelection(){return this.selected!=null||this.selectedValues!=null&&this.selectedValues.length},_updateAttrForSelected:function(){if(!this.multi){Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this)}else if(this._shouldUpdateSelection){this.selectedValues=this.selectedItems.map(function(selectedItem){return this._indexToValue(this.indexOf(selectedItem))},this).filter(function(unfilteredValue){return unfilteredValue!=null},this)}},_updateSelected:function(){if(this.multi){this._selectMulti(this.selectedValues)}else{this._selectSelected(this.selected)}},_selectMulti:function(values){if(values){var selectedItems=this._valuesToItems(values);this._selection.clear(selectedItems);for(var i=0;i<selectedItems.length;i++){this._selection.setItemSelected(selectedItems[i],true)}if(this.fallbackSelection&&this.items.length&&!this._selection.get().length){var fallback=this._valueToItem(this.fallbackSelection);if(fallback){this.selectedValues=[this.fallbackSelection]}}}else{this._selection.clear()}},_selectionChange:function(){var s=this._selection.get();if(this.multi){this._setSelectedItems(s)}else{this._setSelectedItems([s]);this._setSelectedItem(s)}},_toggleSelected:function(value){var i=this.selectedValues.indexOf(value);var unselected=i<0;if(unselected){this.push("selectedValues",value)}else{this.splice("selectedValues",i,1)}},_valuesToItems:function(values){return values==null?null:values.map(function(value){return this._valueToItem(value)},this)}};Polymer.IronMultiSelectableBehavior=[Polymer.IronSelectableBehavior,Polymer.IronMultiSelectableBehaviorImpl];Polymer({is:"iron-selector",behaviors:[Polymer.IronMultiSelectableBehavior]}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'history-side-bar', - behaviors: [ Polymer.IronA11yKeysBehavior ], - properties: { - selectedPage: { - type: String, - notify: true - }, - showFooter: Boolean, - drawer: { - type: Boolean, - reflectToAttribute: true - } - }, - keyBindings: { - 'space:keydown': 'onSpacePressed_' - }, - onSpacePressed_: function(e) { - e.detail.keyboardEvent.path[0].click(); - }, - onSelectorActivate_: function() { - this.fire('history-close-drawer'); - }, - onClearBrowsingDataTap_: function(e) { - var browserService = md_history.BrowserService.getInstance(); - browserService.recordAction('InitClearBrowsingData'); - browserService.openClearBrowsingData(); - this.$['cbd-ripple'].upAction(); - e.preventDefault(); - }, - onItemClick_: function(e) { - e.preventDefault(); - } -}); - +Polymer({is:"history-side-bar",behaviors:[Polymer.IronA11yKeysBehavior],properties:{selectedPage:{type:String,notify:true},showFooter:Boolean,drawer:{type:Boolean,reflectToAttribute:true}},keyBindings:{"space:keydown":"onSpacePressed_"},onSpacePressed_:function(e){e.detail.keyboardEvent.path[0].click()},onSelectorActivate_:function(){this.fire("history-close-drawer")},onClearBrowsingDataTap_:function(e){var browserService=md_history.BrowserService.getInstance();browserService.recordAction("InitClearBrowsingData");browserService.openClearBrowsingData();this.$["cbd-ripple"].upAction();e.preventDefault()},onItemClick_:function(e){e.preventDefault()}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -cr.define('md_history', function() { - var lazyLoadPromise = null; - function ensureLazyLoaded() { - if (!lazyLoadPromise) { - lazyLoadPromise = new Promise(function(resolve, reject) { - Polymer.Base.importHref('chrome://history/lazy_load.html', resolve, reject, true); - }); - } - return lazyLoadPromise; - } - return { - ensureLazyLoaded: ensureLazyLoaded - }; -}); - -Polymer({ - is: 'history-app', - behaviors: [ Polymer.IronScrollTargetBehavior ], - properties: { - showSidebarFooter: Boolean, - hasSyncedResults: Boolean, - selectedPage_: { - type: String, - observer: 'selectedPageChanged_' - }, - grouped_: { - type: Boolean, - reflectToAttribute: true - }, - queryState_: { - type: Object, - value: function() { - return { - incremental: false, - querying: true, - queryingDisabled: false, - _range: HistoryRange.ALL_TIME, - searchTerm: '', - groupedOffset: 0, - set range(val) { - this._range = Number(val); - }, - get range() { - return this._range; - } - }; - } - }, - queryResult_: { - type: Object, - value: function() { - return { - info: null, - results: null, - sessionList: null - }; - } - }, - hasDrawer_: Boolean, - isUserSignedIn_: { - type: Boolean, - value: loadTimeData.getBoolean('isUserSignedIn') - }, - toolbarShadow_: { - type: Boolean, - reflectToAttribute: true, - notify: true - } - }, - listeners: { - 'cr-menu-tap': 'onMenuTap_', - 'history-checkbox-select': 'checkboxSelected', - 'unselect-all': 'unselectAll', - 'delete-selected': 'deleteSelected', - 'history-close-drawer': 'closeDrawer_', - 'history-view-changed': 'historyViewChanged_' - }, - ready: function() { - this.grouped_ = loadTimeData.getBoolean('groupByDomain'); - cr.ui.decorate('command', cr.ui.Command); - document.addEventListener('canExecute', this.onCanExecute_.bind(this)); - document.addEventListener('command', this.onCommand_.bind(this)); - }, - onFirstRender: function() { - setTimeout(function() { - chrome.send('metricsHandler:recordTime', [ 'History.ResultsRenderedTime', window.performance.now() ]); - }); - var searchField = this.$.toolbar.searchField; - if (!searchField.narrow) { - searchField.getSearchInput().focus(); - } - md_history.ensureLazyLoaded(); - }, - _scrollHandler: function() { - if (this.scrollTarget) this.toolbarShadow_ = this.scrollTarget.scrollTop != 0; - }, - onMenuTap_: function() { - var drawer = this.$$('#drawer'); - if (drawer) drawer.toggle(); - }, - checkboxSelected: function(e) { - var toolbar = this.$.toolbar; - toolbar.count = this.$.history.getSelectedItemCount(); - }, - unselectAll: function() { - var listContainer = this.$.history; - var toolbar = this.$.toolbar; - listContainer.unselectAllItems(toolbar.count); - toolbar.count = 0; - }, - deleteSelected: function() { - this.$.history.deleteSelectedWithPrompt(); - }, - historyResult: function(info, results) { - this.set('queryState_.querying', false); - this.set('queryResult_.info', info); - this.set('queryResult_.results', results); - var listContainer = this.$['history']; - listContainer.historyResult(info, results); - }, - focusToolbarSearchField: function() { - this.$.toolbar.showSearchField(); - }, - onCanExecute_: function(e) { - e = e; - switch (e.command.id) { - case 'find-command': - e.canExecute = true; - break; - - case 'slash-command': - e.canExecute = !this.$.toolbar.searchField.isSearchFocused(); - break; - - case 'delete-command': - e.canExecute = this.$.toolbar.count > 0; - break; - } - }, - onCommand_: function(e) { - if (e.command.id == 'find-command' || e.command.id == 'slash-command') this.focusToolbarSearchField(); - if (e.command.id == 'delete-command') this.deleteSelected(); - }, - setForeignSessions: function(sessionList, isTabSyncEnabled) { - if (!isTabSyncEnabled) { - var syncedDeviceManagerElem = this.$$('history-synced-device-manager'); - if (syncedDeviceManagerElem) syncedDeviceManagerElem.tabSyncDisabled(); - return; - } - this.set('queryResult_.sessionList', sessionList); - }, - historyDeleted: function() { - this.$.history.historyDeleted(); - }, - updateSignInState: function(isUserSignedIn) { - this.isUserSignedIn_ = isUserSignedIn; - }, - syncedTabsSelected_: function(selectedPage) { - return selectedPage == 'syncedTabs'; - }, - shouldShowSpinner_: function(querying, incremental, searchTerm) { - return querying && !incremental && searchTerm != ''; - }, - showSyncNotice_: function(hasSyncedResults, selectedPage) { - return hasSyncedResults && selectedPage != 'syncedTabs'; - }, - selectedPageChanged_: function() { - this.unselectAll(); - this.historyViewChanged_(); - }, - historyViewChanged_: function() { - requestAnimationFrame(function() { - if (!this.$.content.selectedItem) return; - this.scrollTarget = this.$.content.selectedItem.getContentScrollTarget(); - this._scrollHandler(); - }.bind(this)); - this.recordHistoryPageView_(); - }, - getSelectedPage_: function(selectedPage, items) { - return selectedPage; - }, - closeDrawer_: function() { - var drawer = this.$$('#drawer'); - if (drawer) drawer.close(); - }, - recordHistoryPageView_: function() { - var histogramValue = HistoryPageViewHistogram.END; - switch (this.selectedPage_) { - case 'syncedTabs': - histogramValue = this.isUserSignedIn_ ? HistoryPageViewHistogram.SYNCED_TABS : HistoryPageViewHistogram.SIGNIN_PROMO; - break; - - default: - switch (this.queryState_.range) { - case HistoryRange.ALL_TIME: - histogramValue = HistoryPageViewHistogram.HISTORY; - break; - - case HistoryRange.WEEK: - histogramValue = HistoryPageViewHistogram.GROUPED_WEEK; - break; - - case HistoryRange.MONTH: - histogramValue = HistoryPageViewHistogram.GROUPED_MONTH; - break; - } - break; - } - md_history.BrowserService.getInstance().recordHistogram('History.HistoryPageView', histogramValue, HistoryPageViewHistogram.END); - } -}); \ No newline at end of file +cr.define("md_history",function(){var lazyLoadPromise=null;function ensureLazyLoaded(){if(!lazyLoadPromise){lazyLoadPromise=new Promise(function(resolve,reject){Polymer.Base.importHref("chrome://history/lazy_load.html",resolve,reject,true)})}return lazyLoadPromise}return{ensureLazyLoaded:ensureLazyLoaded}});Polymer({is:"history-app",behaviors:[Polymer.IronScrollTargetBehavior],properties:{showSidebarFooter:Boolean,hasSyncedResults:Boolean,selectedPage_:{type:String,observer:"selectedPageChanged_"},grouped_:{type:Boolean,reflectToAttribute:true},queryState_:{type:Object,value:function(){return{incremental:false,querying:true,queryingDisabled:false,_range:HistoryRange.ALL_TIME,searchTerm:"",groupedOffset:0,set range(val){this._range=Number(val)},get range(){return this._range}}}},queryResult_:{type:Object,value:function(){return{info:null,results:null,sessionList:null}}},hasDrawer_:Boolean,isUserSignedIn_:{type:Boolean,value:loadTimeData.getBoolean("isUserSignedIn")},toolbarShadow_:{type:Boolean,reflectToAttribute:true,notify:true}},listeners:{"cr-menu-tap":"onMenuTap_","history-checkbox-select":"checkboxSelected","unselect-all":"unselectAll","delete-selected":"deleteSelected","history-close-drawer":"closeDrawer_","history-view-changed":"historyViewChanged_"},ready:function(){this.grouped_=loadTimeData.getBoolean("groupByDomain");cr.ui.decorate("command",cr.ui.Command);document.addEventListener("canExecute",this.onCanExecute_.bind(this));document.addEventListener("command",this.onCommand_.bind(this))},onFirstRender:function(){setTimeout(function(){chrome.send("metricsHandler:recordTime",["History.ResultsRenderedTime",window.performance.now()])});var searchField=this.$.toolbar.searchField;if(!searchField.narrow){searchField.getSearchInput().focus()}md_history.ensureLazyLoaded()},_scrollHandler:function(){if(this.scrollTarget)this.toolbarShadow_=this.scrollTarget.scrollTop!=0},onMenuTap_:function(){var drawer=this.$$("#drawer");if(drawer)drawer.toggle()},checkboxSelected:function(e){var toolbar=this.$.toolbar;toolbar.count=this.$.history.getSelectedItemCount()},unselectAll:function(){var listContainer=this.$.history;var toolbar=this.$.toolbar;listContainer.unselectAllItems(toolbar.count);toolbar.count=0},deleteSelected:function(){this.$.history.deleteSelectedWithPrompt()},historyResult:function(info,results){this.set("queryState_.querying",false);this.set("queryResult_.info",info);this.set("queryResult_.results",results);var listContainer=this.$["history"];listContainer.historyResult(info,results)},focusToolbarSearchField:function(){this.$.toolbar.showSearchField()},onCanExecute_:function(e){e=e;switch(e.command.id){case"find-command":e.canExecute=true;break;case"slash-command":e.canExecute=!this.$.toolbar.searchField.isSearchFocused();break;case"delete-command":e.canExecute=this.$.toolbar.count>0;break}},onCommand_:function(e){if(e.command.id=="find-command"||e.command.id=="slash-command")this.focusToolbarSearchField();if(e.command.id=="delete-command")this.deleteSelected()},setForeignSessions:function(sessionList,isTabSyncEnabled){if(!isTabSyncEnabled){var syncedDeviceManagerElem=this.$$("history-synced-device-manager");if(syncedDeviceManagerElem)syncedDeviceManagerElem.tabSyncDisabled();return}this.set("queryResult_.sessionList",sessionList)},historyDeleted:function(){this.$.history.historyDeleted()},updateSignInState:function(isUserSignedIn){this.isUserSignedIn_=isUserSignedIn},syncedTabsSelected_:function(selectedPage){return selectedPage=="syncedTabs"},shouldShowSpinner_:function(querying,incremental,searchTerm){return querying&&!incremental&&searchTerm!=""},showSyncNotice_:function(hasSyncedResults,selectedPage){return hasSyncedResults&&selectedPage!="syncedTabs"},selectedPageChanged_:function(){this.unselectAll();this.historyViewChanged_()},historyViewChanged_:function(){requestAnimationFrame(function(){if(!this.$.content.selectedItem)return;this.scrollTarget=this.$.content.selectedItem.getContentScrollTarget();this._scrollHandler()}.bind(this));this.recordHistoryPageView_()},getSelectedPage_:function(selectedPage,items){return selectedPage},closeDrawer_:function(){var drawer=this.$$("#drawer");if(drawer)drawer.close()},recordHistoryPageView_:function(){var histogramValue=HistoryPageViewHistogram.END;switch(this.selectedPage_){case"syncedTabs":histogramValue=this.isUserSignedIn_?HistoryPageViewHistogram.SYNCED_TABS:HistoryPageViewHistogram.SIGNIN_PROMO;break;default:switch(this.queryState_.range){case HistoryRange.ALL_TIME:histogramValue=HistoryPageViewHistogram.HISTORY;break;case HistoryRange.WEEK:histogramValue=HistoryPageViewHistogram.GROUPED_WEEK;break;case HistoryRange.MONTH:histogramValue=HistoryPageViewHistogram.GROUPED_MONTH;break}break}md_history.BrowserService.getInstance().recordHistogram("History.HistoryPageView",histogramValue,HistoryPageViewHistogram.END)}}); \ No newline at end of file
diff --git a/chrome/browser/resources/md_history/app.vulcanized.html b/chrome/browser/resources/md_history/app.vulcanized.html index 43040c9..2500f4a5 100644 --- a/chrome/browser/resources/md_history/app.vulcanized.html +++ b/chrome/browser/resources/md_history/app.vulcanized.html
@@ -2836,6 +2836,12 @@ cursor: var(--cr-actionable_-_cursor); } +.scroll-container { + display: flex; + flex-direction: column; + min-height: 1px; +} + [selectable]:focus, [selectable] > :focus { background-color: var(--cr-selectable-focus_-_background-color); outline: var(--cr-selectable-focus_-_outline); } @@ -3508,6 +3514,12 @@ cursor: var(--cr-actionable_-_cursor); } +.scroll-container { + display: flex; + flex-direction: column; + min-height: 1px; +} + [selectable]:focus, [selectable] > :focus { background-color: var(--cr-selectable-focus_-_background-color); outline: var(--cr-selectable-focus_-_outline); }
diff --git a/chrome/browser/resources/md_history/lazy_load.crisper.js b/chrome/browser/resources/md_history/lazy_load.crisper.js index cc06fa30..7acffe8 100644 --- a/chrome/browser/resources/md_history/lazy_load.crisper.js +++ b/chrome/browser/resources/md_history/lazy_load.crisper.js
@@ -1,3295 +1,23 @@ -Polymer({ - is: 'iron-collapse', - behaviors: [ Polymer.IronResizableBehavior ], - properties: { - horizontal: { - type: Boolean, - value: false, - observer: '_horizontalChanged' - }, - opened: { - type: Boolean, - value: false, - notify: true, - observer: '_openedChanged' - }, - noAnimation: { - type: Boolean - }, - _desiredSize: { - type: String, - value: '' - } - }, - get dimension() { - return this.horizontal ? 'width' : 'height'; - }, - get _dimensionMax() { - return this.horizontal ? 'maxWidth' : 'maxHeight'; - }, - get _dimensionMaxCss() { - return this.horizontal ? 'max-width' : 'max-height'; - }, - hostAttributes: { - role: 'group', - 'aria-hidden': 'true', - 'aria-expanded': 'false' - }, - listeners: { - transitionend: '_transitionEnd' - }, - attached: function() { - this._transitionEnd(); - }, - toggle: function() { - this.opened = !this.opened; - }, - show: function() { - this.opened = true; - }, - hide: function() { - this.opened = false; - }, - updateSize: function(size, animated) { - size = size === 'auto' ? '' : size; - if (this._desiredSize === size) { - return; - } - this._desiredSize = size; - this._updateTransition(false); - var willAnimate = animated && !this.noAnimation && this._isDisplayed; - if (willAnimate) { - var startSize = this._calcSize(); - if (size === '') { - this.style[this._dimensionMax] = ''; - size = this._calcSize(); - } - this.style[this._dimensionMax] = startSize; - this.scrollTop = this.scrollTop; - this._updateTransition(true); - willAnimate = size !== startSize; - } - this.style[this._dimensionMax] = size; - if (!willAnimate) { - this._transitionEnd(); - } - }, - enableTransition: function(enabled) { - Polymer.Base._warn('`enableTransition()` is deprecated, use `noAnimation` instead.'); - this.noAnimation = !enabled; - }, - _updateTransition: function(enabled) { - this.style.transitionDuration = enabled && !this.noAnimation ? '' : '0s'; - }, - _horizontalChanged: function() { - this.style.transitionProperty = this._dimensionMaxCss; - var otherDimension = this._dimensionMax === 'maxWidth' ? 'maxHeight' : 'maxWidth'; - this.style[otherDimension] = ''; - this.updateSize(this.opened ? 'auto' : '0px', false); - }, - _openedChanged: function() { - this.setAttribute('aria-expanded', this.opened); - this.setAttribute('aria-hidden', !this.opened); - this.toggleClass('iron-collapse-closed', false); - this.toggleClass('iron-collapse-opened', false); - this.updateSize(this.opened ? 'auto' : '0px', true); - if (this.opened) { - this.focus(); - } - }, - _transitionEnd: function() { - this.style[this._dimensionMax] = this._desiredSize; - this.toggleClass('iron-collapse-closed', !this.opened); - this.toggleClass('iron-collapse-opened', this.opened); - this._updateTransition(false); - this.notifyResize(); - }, - get _isDisplayed() { - var rect = this.getBoundingClientRect(); - for (var prop in rect) { - if (rect[prop] !== 0) return true; - } - return false; - }, - _calcSize: function() { - return this.getBoundingClientRect()[this.dimension] + 'px'; - } -}); - +Polymer({is:"iron-collapse",behaviors:[Polymer.IronResizableBehavior],properties:{horizontal:{type:Boolean,value:false,observer:"_horizontalChanged"},opened:{type:Boolean,value:false,notify:true,observer:"_openedChanged"},noAnimation:{type:Boolean},_desiredSize:{type:String,value:""}},get dimension(){return this.horizontal?"width":"height"},get _dimensionMax(){return this.horizontal?"maxWidth":"maxHeight"},get _dimensionMaxCss(){return this.horizontal?"max-width":"max-height"},hostAttributes:{role:"group","aria-hidden":"true","aria-expanded":"false"},listeners:{transitionend:"_transitionEnd"},attached:function(){this._transitionEnd()},toggle:function(){this.opened=!this.opened},show:function(){this.opened=true},hide:function(){this.opened=false},updateSize:function(size,animated){size=size==="auto"?"":size;if(this._desiredSize===size){return}this._desiredSize=size;this._updateTransition(false);var willAnimate=animated&&!this.noAnimation&&this._isDisplayed;if(willAnimate){var startSize=this._calcSize();if(size===""){this.style[this._dimensionMax]="";size=this._calcSize()}this.style[this._dimensionMax]=startSize;this.scrollTop=this.scrollTop;this._updateTransition(true);willAnimate=size!==startSize}this.style[this._dimensionMax]=size;if(!willAnimate){this._transitionEnd()}},enableTransition:function(enabled){Polymer.Base._warn("`enableTransition()` is deprecated, use `noAnimation` instead.");this.noAnimation=!enabled},_updateTransition:function(enabled){this.style.transitionDuration=enabled&&!this.noAnimation?"":"0s"},_horizontalChanged:function(){this.style.transitionProperty=this._dimensionMaxCss;var otherDimension=this._dimensionMax==="maxWidth"?"maxHeight":"maxWidth";this.style[otherDimension]="";this.updateSize(this.opened?"auto":"0px",false)},_openedChanged:function(){this.setAttribute("aria-expanded",this.opened);this.setAttribute("aria-hidden",!this.opened);this.toggleClass("iron-collapse-closed",false);this.toggleClass("iron-collapse-opened",false);this.updateSize(this.opened?"auto":"0px",true);if(this.opened){this.focus()}},_transitionEnd:function(){this.style[this._dimensionMax]=this._desiredSize;this.toggleClass("iron-collapse-closed",!this.opened);this.toggleClass("iron-collapse-opened",this.opened);this._updateTransition(false);this.notifyResize()},get _isDisplayed(){var rect=this.getBoundingClientRect();for(var prop in rect){if(rect[prop]!==0)return true}return false},_calcSize:function(){return this.getBoundingClientRect()[this.dimension]+"px"}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var HistoryDomain; +var HistoryDomain;var HistoryGroup;Polymer({is:"history-grouped-list",behaviors:[HistoryListBehavior],properties:{historyData:{type:Array},groupedHistoryData_:{type:Array},searchedTerm:{type:String,value:""},range:{type:Number},queryStartTime:String,queryEndTime:String},observers:["updateGroupedHistoryData_(range, historyData)"],createHistoryDomains_:function(visits){var domainIndexes={};var domains=[];for(var i=0,visit;visit=visits[i];i++){var domain=visit.domain;if(domainIndexes[domain]==undefined){domainIndexes[domain]=domains.length;domains.push({domain:domain,visits:[],expanded:false,rendered:false})}domains[domainIndexes[domain]].visits.push(visit)}var sortByVisits=function(a,b){return b.visits.length-a.visits.length};domains.sort(sortByVisits);return domains},updateGroupedHistoryData_:function(){if(this.historyData.length==0){this.groupedHistoryData_=[];return}if(this.range==HistoryRange.WEEK){var days=[];var currentDayVisits=[this.historyData[0]];var pushCurrentDay=function(){days.push({title:this.searchedTerm?currentDayVisits[0].dateShort:currentDayVisits[0].dateRelativeDay,domains:this.createHistoryDomains_(currentDayVisits)})}.bind(this);var visitsSameDay=function(a,b){if(this.searchedTerm)return a.dateShort==b.dateShort;return a.dateRelativeDay==b.dateRelativeDay}.bind(this);for(var i=1;i<this.historyData.length;i++){var visit=this.historyData[i];if(!visitsSameDay(visit,currentDayVisits[0])){pushCurrentDay();currentDayVisits=[]}currentDayVisits.push(visit)}pushCurrentDay();this.groupedHistoryData_=days}else if(this.range==HistoryRange.MONTH){this.groupedHistoryData_=[{title:this.queryStartTime+" – "+this.queryEndTime,domains:this.createHistoryDomains_(this.historyData)}]}},toggleDomainExpanded_:function(e){var collapse=e.currentTarget.parentNode.querySelector("iron-collapse");e.model.set("domain.rendered",true);setTimeout(function(){collapse.toggle()},0)},needsTimeGap_:function(groupIndex,domainIndex,itemIndex){var visits=this.groupedHistoryData_[groupIndex].domains[domainIndex].visits;return md_history.HistoryItem.needsTimeGap(visits,itemIndex,this.searchedTerm)},pathForItem_:function(groupIndex,domainIndex,itemIndex){return["groupedHistoryData_",groupIndex,"domains",domainIndex,"visits",itemIndex].join(".")},getWebsiteIconStyle_:function(domain){return"background-image: "+cr.icon.getFavicon(domain.visits[0].url)},getDropdownIcon_:function(expanded){return expanded?"cr:expand-less":"cr:expand-more"}});Polymer.PaperButtonBehaviorImpl={properties:{elevation:{type:Number,reflectToAttribute:true,readOnly:true}},observers:["_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)","_computeKeyboardClass(receivedFocusFromKeyboard)"],hostAttributes:{role:"button",tabindex:"0",animated:true},_calculateElevation:function(){var e=1;if(this.disabled){e=0}else if(this.active||this.pressed){e=4}else if(this.receivedFocusFromKeyboard){e=3}this._setElevation(e)},_computeKeyboardClass:function(receivedFocusFromKeyboard){this.toggleClass("keyboard-focus",receivedFocusFromKeyboard)},_spaceKeyDownHandler:function(event){Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this,event);if(this.hasRipple()&&this.getRipple().ripples.length<1){this._ripple.uiDownAction()}},_spaceKeyUpHandler:function(event){Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this,event);if(this.hasRipple()){this._ripple.uiUpAction()}}};Polymer.PaperButtonBehavior=[Polymer.IronButtonState,Polymer.IronControlState,Polymer.PaperRippleBehavior,Polymer.PaperButtonBehaviorImpl];Polymer({is:"paper-button",behaviors:[Polymer.PaperButtonBehavior],properties:{raised:{type:Boolean,reflectToAttribute:true,value:false,observer:"_calculateElevation"}},_calculateElevation:function(){if(!this.raised){this._setElevation(0)}else{Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this)}}});Polymer.PaperItemBehaviorImpl={hostAttributes:{role:"option",tabindex:"0"}};Polymer.PaperItemBehavior=[Polymer.IronButtonState,Polymer.IronControlState,Polymer.PaperItemBehaviorImpl];Polymer({is:"paper-item",behaviors:[Polymer.PaperItemBehavior]});Polymer.IronFitBehavior={properties:{sizingTarget:{type:Object,value:function(){return this}},fitInto:{type:Object,value:window},noOverlap:{type:Boolean},positionTarget:{type:Element},horizontalAlign:{type:String},verticalAlign:{type:String},dynamicAlign:{type:Boolean},horizontalOffset:{type:Number,value:0,notify:true},verticalOffset:{type:Number,value:0,notify:true},autoFitOnAttach:{type:Boolean,value:false},_fitInfo:{type:Object}},get _fitWidth(){var fitWidth;if(this.fitInto===window){fitWidth=this.fitInto.innerWidth}else{fitWidth=this.fitInto.getBoundingClientRect().width}return fitWidth},get _fitHeight(){var fitHeight;if(this.fitInto===window){fitHeight=this.fitInto.innerHeight}else{fitHeight=this.fitInto.getBoundingClientRect().height}return fitHeight},get _fitLeft(){var fitLeft;if(this.fitInto===window){fitLeft=0}else{fitLeft=this.fitInto.getBoundingClientRect().left}return fitLeft},get _fitTop(){var fitTop;if(this.fitInto===window){fitTop=0}else{fitTop=this.fitInto.getBoundingClientRect().top}return fitTop},get _defaultPositionTarget(){var parent=Polymer.dom(this).parentNode;if(parent&&parent.nodeType===Node.DOCUMENT_FRAGMENT_NODE){parent=parent.host}return parent},get _localeHorizontalAlign(){if(this._isRTL){if(this.horizontalAlign==="right"){return"left"}if(this.horizontalAlign==="left"){return"right"}}return this.horizontalAlign},attached:function(){this._isRTL=window.getComputedStyle(this).direction=="rtl";this.positionTarget=this.positionTarget||this._defaultPositionTarget;if(this.autoFitOnAttach){if(window.getComputedStyle(this).display==="none"){setTimeout(function(){this.fit()}.bind(this))}else{this.fit()}}},fit:function(){this.position();this.constrain();this.center()},_discoverInfo:function(){if(this._fitInfo){return}var target=window.getComputedStyle(this);var sizer=window.getComputedStyle(this.sizingTarget);this._fitInfo={inlineStyle:{top:this.style.top||"",left:this.style.left||"",position:this.style.position||""},sizerInlineStyle:{maxWidth:this.sizingTarget.style.maxWidth||"",maxHeight:this.sizingTarget.style.maxHeight||"",boxSizing:this.sizingTarget.style.boxSizing||""},positionedBy:{vertically:target.top!=="auto"?"top":target.bottom!=="auto"?"bottom":null,horizontally:target.left!=="auto"?"left":target.right!=="auto"?"right":null},sizedBy:{height:sizer.maxHeight!=="none",width:sizer.maxWidth!=="none",minWidth:parseInt(sizer.minWidth,10)||0,minHeight:parseInt(sizer.minHeight,10)||0},margin:{top:parseInt(target.marginTop,10)||0,right:parseInt(target.marginRight,10)||0,bottom:parseInt(target.marginBottom,10)||0,left:parseInt(target.marginLeft,10)||0}};if(this.verticalOffset){this._fitInfo.margin.top=this._fitInfo.margin.bottom=this.verticalOffset;this._fitInfo.inlineStyle.marginTop=this.style.marginTop||"";this._fitInfo.inlineStyle.marginBottom=this.style.marginBottom||"";this.style.marginTop=this.style.marginBottom=this.verticalOffset+"px"}if(this.horizontalOffset){this._fitInfo.margin.left=this._fitInfo.margin.right=this.horizontalOffset;this._fitInfo.inlineStyle.marginLeft=this.style.marginLeft||"";this._fitInfo.inlineStyle.marginRight=this.style.marginRight||"";this.style.marginLeft=this.style.marginRight=this.horizontalOffset+"px"}},resetFit:function(){var info=this._fitInfo||{};for(var property in info.sizerInlineStyle){this.sizingTarget.style[property]=info.sizerInlineStyle[property]}for(var property in info.inlineStyle){this.style[property]=info.inlineStyle[property]}this._fitInfo=null},refit:function(){var scrollLeft=this.sizingTarget.scrollLeft;var scrollTop=this.sizingTarget.scrollTop;this.resetFit();this.fit();this.sizingTarget.scrollLeft=scrollLeft;this.sizingTarget.scrollTop=scrollTop},position:function(){if(!this.horizontalAlign&&!this.verticalAlign){return}this._discoverInfo();this.style.position="fixed";this.sizingTarget.style.boxSizing="border-box";this.style.left="0px";this.style.top="0px";var rect=this.getBoundingClientRect();var positionRect=this.__getNormalizedRect(this.positionTarget);var fitRect=this.__getNormalizedRect(this.fitInto);var margin=this._fitInfo.margin;var size={width:rect.width+margin.left+margin.right,height:rect.height+margin.top+margin.bottom};var position=this.__getPosition(this._localeHorizontalAlign,this.verticalAlign,size,positionRect,fitRect);var left=position.left+margin.left;var top=position.top+margin.top;var right=Math.min(fitRect.right-margin.right,left+rect.width);var bottom=Math.min(fitRect.bottom-margin.bottom,top+rect.height);var minWidth=this._fitInfo.sizedBy.minWidth;var minHeight=this._fitInfo.sizedBy.minHeight;if(left<margin.left){left=margin.left;if(right-left<minWidth){left=right-minWidth}}if(top<margin.top){top=margin.top;if(bottom-top<minHeight){top=bottom-minHeight}}this.sizingTarget.style.maxWidth=right-left+"px";this.sizingTarget.style.maxHeight=bottom-top+"px";this.style.left=left-rect.left+"px";this.style.top=top-rect.top+"px"},constrain:function(){if(this.horizontalAlign||this.verticalAlign){return}this._discoverInfo();var info=this._fitInfo;if(!info.positionedBy.vertically){this.style.position="fixed";this.style.top="0px"}if(!info.positionedBy.horizontally){this.style.position="fixed";this.style.left="0px"}this.sizingTarget.style.boxSizing="border-box";var rect=this.getBoundingClientRect();if(!info.sizedBy.height){this.__sizeDimension(rect,info.positionedBy.vertically,"top","bottom","Height")}if(!info.sizedBy.width){this.__sizeDimension(rect,info.positionedBy.horizontally,"left","right","Width")}},_sizeDimension:function(rect,positionedBy,start,end,extent){this.__sizeDimension(rect,positionedBy,start,end,extent)},__sizeDimension:function(rect,positionedBy,start,end,extent){var info=this._fitInfo;var fitRect=this.__getNormalizedRect(this.fitInto);var max=extent==="Width"?fitRect.width:fitRect.height;var flip=positionedBy===end;var offset=flip?max-rect[end]:rect[start];var margin=info.margin[flip?start:end];var offsetExtent="offset"+extent;var sizingOffset=this[offsetExtent]-this.sizingTarget[offsetExtent];this.sizingTarget.style["max"+extent]=max-margin-offset-sizingOffset+"px"},center:function(){if(this.horizontalAlign||this.verticalAlign){return}this._discoverInfo();var positionedBy=this._fitInfo.positionedBy;if(positionedBy.vertically&&positionedBy.horizontally){return}this.style.position="fixed";if(!positionedBy.vertically){this.style.top="0px"}if(!positionedBy.horizontally){this.style.left="0px"}var rect=this.getBoundingClientRect();var fitRect=this.__getNormalizedRect(this.fitInto);if(!positionedBy.vertically){var top=fitRect.top-rect.top+(fitRect.height-rect.height)/2;this.style.top=top+"px"}if(!positionedBy.horizontally){var left=fitRect.left-rect.left+(fitRect.width-rect.width)/2;this.style.left=left+"px"}},__getNormalizedRect:function(target){if(target===document.documentElement||target===window){return{top:0,left:0,width:window.innerWidth,height:window.innerHeight,right:window.innerWidth,bottom:window.innerHeight}}return target.getBoundingClientRect()},__getCroppedArea:function(position,size,fitRect){var verticalCrop=Math.min(0,position.top)+Math.min(0,fitRect.bottom-(position.top+size.height));var horizontalCrop=Math.min(0,position.left)+Math.min(0,fitRect.right-(position.left+size.width));return Math.abs(verticalCrop)*size.width+Math.abs(horizontalCrop)*size.height},__getPosition:function(hAlign,vAlign,size,positionRect,fitRect){var positions=[{verticalAlign:"top",horizontalAlign:"left",top:positionRect.top,left:positionRect.left},{verticalAlign:"top",horizontalAlign:"right",top:positionRect.top,left:positionRect.right-size.width},{verticalAlign:"bottom",horizontalAlign:"left",top:positionRect.bottom-size.height,left:positionRect.left},{verticalAlign:"bottom",horizontalAlign:"right",top:positionRect.bottom-size.height,left:positionRect.right-size.width}];if(this.noOverlap){for(var i=0,l=positions.length;i<l;i++){var copy={};for(var key in positions[i]){copy[key]=positions[i][key]}positions.push(copy)}positions[0].top=positions[1].top+=positionRect.height;positions[2].top=positions[3].top-=positionRect.height;positions[4].left=positions[6].left+=positionRect.width;positions[5].left=positions[7].left-=positionRect.width}vAlign=vAlign==="auto"?null:vAlign;hAlign=hAlign==="auto"?null:hAlign;var position;for(var i=0;i<positions.length;i++){var pos=positions[i];if(!this.dynamicAlign&&!this.noOverlap&&pos.verticalAlign===vAlign&&pos.horizontalAlign===hAlign){position=pos;break}var alignOk=(!vAlign||pos.verticalAlign===vAlign)&&(!hAlign||pos.horizontalAlign===hAlign);if(!this.dynamicAlign&&!alignOk){continue}position=position||pos;pos.croppedArea=this.__getCroppedArea(pos,size,fitRect);var diff=pos.croppedArea-position.croppedArea;if(diff<0||diff===0&&alignOk){position=pos}if(position.croppedArea===0&&alignOk){break}}return position}};(function(){"use strict";Polymer({is:"iron-overlay-backdrop",properties:{opened:{reflectToAttribute:true,type:Boolean,value:false,observer:"_openedChanged"}},listeners:{transitionend:"_onTransitionend"},created:function(){this.__openedRaf=null},attached:function(){this.opened&&this._openedChanged(this.opened)},prepare:function(){if(this.opened&&!this.parentNode){Polymer.dom(document.body).appendChild(this)}},open:function(){this.opened=true},close:function(){this.opened=false},complete:function(){if(!this.opened&&this.parentNode===document.body){Polymer.dom(this.parentNode).removeChild(this)}},_onTransitionend:function(event){if(event&&event.target===this){this.complete()}},_openedChanged:function(opened){if(opened){this.prepare()}else{var cs=window.getComputedStyle(this);if(cs.transitionDuration==="0s"||cs.opacity==0){this.complete()}}if(!this.isAttached){return}if(this.__openedRaf){window.cancelAnimationFrame(this.__openedRaf);this.__openedRaf=null}this.scrollTop=this.scrollTop;this.__openedRaf=window.requestAnimationFrame(function(){this.__openedRaf=null;this.toggleClass("opened",this.opened)}.bind(this))}})})();Polymer.IronOverlayManagerClass=function(){this._overlays=[];this._minimumZ=101;this._backdropElement=null;Polymer.Gestures.add(document,"tap",this._onCaptureClick.bind(this));document.addEventListener("focus",this._onCaptureFocus.bind(this),true);document.addEventListener("keydown",this._onCaptureKeyDown.bind(this),true)};Polymer.IronOverlayManagerClass.prototype={constructor:Polymer.IronOverlayManagerClass,get backdropElement(){if(!this._backdropElement){this._backdropElement=document.createElement("iron-overlay-backdrop")}return this._backdropElement},get deepActiveElement(){var active=document.activeElement||document.body;while(active.root&&Polymer.dom(active.root).activeElement){active=Polymer.dom(active.root).activeElement}return active},_bringOverlayAtIndexToFront:function(i){var overlay=this._overlays[i];if(!overlay){return}var lastI=this._overlays.length-1;var currentOverlay=this._overlays[lastI];if(currentOverlay&&this._shouldBeBehindOverlay(overlay,currentOverlay)){lastI--}if(i>=lastI){return}var minimumZ=Math.max(this.currentOverlayZ(),this._minimumZ);if(this._getZ(overlay)<=minimumZ){this._applyOverlayZ(overlay,minimumZ)}while(i<lastI){this._overlays[i]=this._overlays[i+1];i++}this._overlays[lastI]=overlay},addOrRemoveOverlay:function(overlay){if(overlay.opened){this.addOverlay(overlay)}else{this.removeOverlay(overlay)}},addOverlay:function(overlay){var i=this._overlays.indexOf(overlay);if(i>=0){this._bringOverlayAtIndexToFront(i);this.trackBackdrop();return}var insertionIndex=this._overlays.length;var currentOverlay=this._overlays[insertionIndex-1];var minimumZ=Math.max(this._getZ(currentOverlay),this._minimumZ);var newZ=this._getZ(overlay);if(currentOverlay&&this._shouldBeBehindOverlay(overlay,currentOverlay)){this._applyOverlayZ(currentOverlay,minimumZ);insertionIndex--;var previousOverlay=this._overlays[insertionIndex-1];minimumZ=Math.max(this._getZ(previousOverlay),this._minimumZ)}if(newZ<=minimumZ){this._applyOverlayZ(overlay,minimumZ)}this._overlays.splice(insertionIndex,0,overlay);this.trackBackdrop()},removeOverlay:function(overlay){var i=this._overlays.indexOf(overlay);if(i===-1){return}this._overlays.splice(i,1);this.trackBackdrop()},currentOverlay:function(){var i=this._overlays.length-1;return this._overlays[i]},currentOverlayZ:function(){return this._getZ(this.currentOverlay())},ensureMinimumZ:function(minimumZ){this._minimumZ=Math.max(this._minimumZ,minimumZ)},focusOverlay:function(){var current=this.currentOverlay();if(current){current._applyFocus()}},trackBackdrop:function(){var overlay=this._overlayWithBackdrop();if(!overlay&&!this._backdropElement){return}this.backdropElement.style.zIndex=this._getZ(overlay)-1;this.backdropElement.opened=!!overlay},getBackdrops:function(){var backdrops=[];for(var i=0;i<this._overlays.length;i++){if(this._overlays[i].withBackdrop){backdrops.push(this._overlays[i])}}return backdrops},backdropZ:function(){return this._getZ(this._overlayWithBackdrop())-1},_overlayWithBackdrop:function(){for(var i=0;i<this._overlays.length;i++){if(this._overlays[i].withBackdrop){return this._overlays[i]}}},_getZ:function(overlay){var z=this._minimumZ;if(overlay){var z1=Number(overlay.style.zIndex||window.getComputedStyle(overlay).zIndex);if(z1===z1){z=z1}}return z},_setZ:function(element,z){element.style.zIndex=z},_applyOverlayZ:function(overlay,aboveZ){this._setZ(overlay,aboveZ+2)},_overlayInPath:function(path){path=path||[];for(var i=0;i<path.length;i++){if(path[i]._manager===this){return path[i]}}},_onCaptureClick:function(event){var overlay=this.currentOverlay();if(overlay&&this._overlayInPath(Polymer.dom(event).path)!==overlay){overlay._onCaptureClick(event)}},_onCaptureFocus:function(event){var overlay=this.currentOverlay();if(overlay){overlay._onCaptureFocus(event)}},_onCaptureKeyDown:function(event){var overlay=this.currentOverlay();if(overlay){if(Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event,"esc")){overlay._onCaptureEsc(event)}else if(Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event,"tab")){overlay._onCaptureTab(event)}}},_shouldBeBehindOverlay:function(overlay1,overlay2){return!overlay1.alwaysOnTop&&overlay2.alwaysOnTop}};Polymer.IronOverlayManager=new Polymer.IronOverlayManagerClass;(function(){"use strict";Polymer.IronOverlayBehaviorImpl={properties:{opened:{observer:"_openedChanged",type:Boolean,value:false,notify:true},canceled:{observer:"_canceledChanged",readOnly:true,type:Boolean,value:false},withBackdrop:{observer:"_withBackdropChanged",type:Boolean},noAutoFocus:{type:Boolean,value:false},noCancelOnEscKey:{type:Boolean,value:false},noCancelOnOutsideClick:{type:Boolean,value:false},closingReason:{type:Object},restoreFocusOnClose:{type:Boolean,value:false},alwaysOnTop:{type:Boolean},_manager:{type:Object,value:Polymer.IronOverlayManager},_focusedChild:{type:Object}},listeners:{"iron-resize":"_onIronResize"},get backdropElement(){return this._manager.backdropElement},get _focusNode(){return this._focusedChild||Polymer.dom(this).querySelector("[autofocus]")||this},get _focusableNodes(){var FOCUSABLE_WITH_DISABLED=["a[href]","area[href]","iframe","[tabindex]","[contentEditable=true]"];var FOCUSABLE_WITHOUT_DISABLED=["input","select","textarea","button"];var selector=FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),')+':not([tabindex="-1"]),'+FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),')+':not([disabled]):not([tabindex="-1"])';var focusables=Polymer.dom(this).querySelectorAll(selector);if(this.tabIndex>=0){focusables.splice(0,0,this)}return focusables.sort(function(a,b){if(a.tabIndex===b.tabIndex){return 0}if(a.tabIndex===0||a.tabIndex>b.tabIndex){return 1}return-1})},ready:function(){this.__isAnimating=false;this.__shouldRemoveTabIndex=false;this.__firstFocusableNode=this.__lastFocusableNode=null;this.__raf=null;this.__restoreFocusNode=null;this._ensureSetup()},attached:function(){if(this.opened){this._openedChanged(this.opened)}this._observer=Polymer.dom(this).observeNodes(this._onNodesChange)},detached:function(){Polymer.dom(this).unobserveNodes(this._observer);this._observer=null;if(this.__raf){window.cancelAnimationFrame(this.__raf);this.__raf=null}this._manager.removeOverlay(this)},toggle:function(){this._setCanceled(false);this.opened=!this.opened},open:function(){this._setCanceled(false);this.opened=true},close:function(){this._setCanceled(false);this.opened=false},cancel:function(event){var cancelEvent=this.fire("iron-overlay-canceled",event,{cancelable:true});if(cancelEvent.defaultPrevented){return}this._setCanceled(true);this.opened=false},_ensureSetup:function(){if(this._overlaySetup){return}this._overlaySetup=true;this.style.outline="none";this.style.display="none"},_openedChanged:function(opened){if(opened){this.removeAttribute("aria-hidden")}else{this.setAttribute("aria-hidden","true")}if(!this.isAttached){return}this.__isAnimating=true;this.__onNextAnimationFrame(this.__openedChanged)},_canceledChanged:function(){this.closingReason=this.closingReason||{};this.closingReason.canceled=this.canceled},_withBackdropChanged:function(){if(this.withBackdrop&&!this.hasAttribute("tabindex")){this.setAttribute("tabindex","-1");this.__shouldRemoveTabIndex=true}else if(this.__shouldRemoveTabIndex){this.removeAttribute("tabindex");this.__shouldRemoveTabIndex=false}if(this.opened&&this.isAttached){this._manager.trackBackdrop()}},_prepareRenderOpened:function(){this.__restoreFocusNode=this._manager.deepActiveElement;this._preparePositioning();this.refit();this._finishPositioning();if(this.noAutoFocus&&document.activeElement===this._focusNode){this._focusNode.blur();this.__restoreFocusNode.focus()}},_renderOpened:function(){this._finishRenderOpened()},_renderClosed:function(){this._finishRenderClosed()},_finishRenderOpened:function(){this.notifyResize();this.__isAnimating=false;var focusableNodes=this._focusableNodes;this.__firstFocusableNode=focusableNodes[0];this.__lastFocusableNode=focusableNodes[focusableNodes.length-1];this.fire("iron-overlay-opened")},_finishRenderClosed:function(){this.style.display="none";this.style.zIndex="";this.notifyResize();this.__isAnimating=false;this.fire("iron-overlay-closed",this.closingReason)},_preparePositioning:function(){this.style.transition=this.style.webkitTransition="none";this.style.transform=this.style.webkitTransform="none";this.style.display=""},_finishPositioning:function(){this.style.display="none";this.scrollTop=this.scrollTop;this.style.transition=this.style.webkitTransition="";this.style.transform=this.style.webkitTransform="";this.style.display="";this.scrollTop=this.scrollTop},_applyFocus:function(){if(this.opened){if(!this.noAutoFocus){this._focusNode.focus()}}else{this._focusNode.blur();this._focusedChild=null;if(this.restoreFocusOnClose&&this.__restoreFocusNode){this.__restoreFocusNode.focus()}this.__restoreFocusNode=null;var currentOverlay=this._manager.currentOverlay();if(currentOverlay&&this!==currentOverlay){currentOverlay._applyFocus()}}},_onCaptureClick:function(event){if(!this.noCancelOnOutsideClick){this.cancel(event)}},_onCaptureFocus:function(event){if(!this.withBackdrop){return}var path=Polymer.dom(event).path;if(path.indexOf(this)===-1){event.stopPropagation();this._applyFocus()}else{this._focusedChild=path[0]}},_onCaptureEsc:function(event){if(!this.noCancelOnEscKey){this.cancel(event)}},_onCaptureTab:function(event){if(!this.withBackdrop){return}var shift=event.shiftKey;var nodeToCheck=shift?this.__firstFocusableNode:this.__lastFocusableNode;var nodeToSet=shift?this.__lastFocusableNode:this.__firstFocusableNode;var shouldWrap=false;if(nodeToCheck===nodeToSet){shouldWrap=true}else{var focusedNode=this._manager.deepActiveElement;shouldWrap=focusedNode===nodeToCheck||focusedNode===this}if(shouldWrap){event.preventDefault();this._focusedChild=nodeToSet;this._applyFocus()}},_onIronResize:function(){if(this.opened&&!this.__isAnimating){this.__onNextAnimationFrame(this.refit)}},_onNodesChange:function(){if(this.opened&&!this.__isAnimating){this.notifyResize()}},__openedChanged:function(){if(this.opened){this._prepareRenderOpened();this._manager.addOverlay(this);this._applyFocus();this._renderOpened()}else{this._manager.removeOverlay(this);this._applyFocus();this._renderClosed()}},__onNextAnimationFrame:function(callback){if(this.__raf){window.cancelAnimationFrame(this.__raf)}var self=this;this.__raf=window.requestAnimationFrame(function nextAnimationFrame(){self.__raf=null;callback.call(self)})}};Polymer.IronOverlayBehavior=[Polymer.IronFitBehavior,Polymer.IronResizableBehavior,Polymer.IronOverlayBehaviorImpl]})();Polymer.NeonAnimatableBehavior={properties:{animationConfig:{type:Object},entryAnimation:{observer:"_entryAnimationChanged",type:String},exitAnimation:{observer:"_exitAnimationChanged",type:String}},_entryAnimationChanged:function(){this.animationConfig=this.animationConfig||{};this.animationConfig["entry"]=[{name:this.entryAnimation,node:this}]},_exitAnimationChanged:function(){this.animationConfig=this.animationConfig||{};this.animationConfig["exit"]=[{name:this.exitAnimation,node:this}]},_copyProperties:function(config1,config2){for(var property in config2){config1[property]=config2[property]}},_cloneConfig:function(config){var clone={isClone:true};this._copyProperties(clone,config);return clone},_getAnimationConfigRecursive:function(type,map,allConfigs){if(!this.animationConfig){return}if(this.animationConfig.value&&typeof this.animationConfig.value==="function"){this._warn(this._logf("playAnimation","Please put 'animationConfig' inside of your components 'properties' object instead of outside of it."));return}var thisConfig;if(type){thisConfig=this.animationConfig[type]}else{thisConfig=this.animationConfig}if(!Array.isArray(thisConfig)){thisConfig=[thisConfig]}if(thisConfig){for(var config,index=0;config=thisConfig[index];index++){if(config.animatable){config.animatable._getAnimationConfigRecursive(config.type||type,map,allConfigs)}else{if(config.id){var cachedConfig=map[config.id];if(cachedConfig){if(!cachedConfig.isClone){map[config.id]=this._cloneConfig(cachedConfig);cachedConfig=map[config.id]}this._copyProperties(cachedConfig,config)}else{map[config.id]=config}}else{allConfigs.push(config)}}}}},getAnimationConfig:function(type){var map={};var allConfigs=[];this._getAnimationConfigRecursive(type,map,allConfigs);for(var key in map){allConfigs.push(map[key])}return allConfigs}};Polymer.NeonAnimationRunnerBehaviorImpl={_configureAnimations:function(configs){var results=[];if(configs.length>0){for(var config,index=0;config=configs[index];index++){var neonAnimation=document.createElement(config.name);if(neonAnimation.isNeonAnimation){var result=null;try{result=neonAnimation.configure(config);if(typeof result.cancel!="function"){result=document.timeline.play(result)}}catch(e){result=null;console.warn("Couldnt play","(",config.name,").",e)}if(result){results.push({neonAnimation:neonAnimation,config:config,animation:result})}}else{console.warn(this.is+":",config.name,"not found!")}}}return results},_shouldComplete:function(activeEntries){var finished=true;for(var i=0;i<activeEntries.length;i++){if(activeEntries[i].animation.playState!="finished"){finished=false;break}}return finished},_complete:function(activeEntries){for(var i=0;i<activeEntries.length;i++){activeEntries[i].neonAnimation.complete(activeEntries[i].config)}for(var i=0;i<activeEntries.length;i++){activeEntries[i].animation.cancel()}},playAnimation:function(type,cookie){var configs=this.getAnimationConfig(type);if(!configs){return}this._active=this._active||{};if(this._active[type]){this._complete(this._active[type]);delete this._active[type]}var activeEntries=this._configureAnimations(configs);if(activeEntries.length==0){this.fire("neon-animation-finish",cookie,{bubbles:false});return}this._active[type]=activeEntries;for(var i=0;i<activeEntries.length;i++){activeEntries[i].animation.onfinish=function(){if(this._shouldComplete(activeEntries)){this._complete(activeEntries);delete this._active[type];this.fire("neon-animation-finish",cookie,{bubbles:false})}}.bind(this)}},cancelAnimation:function(){for(var k in this._animations){this._animations[k].cancel()}this._animations={}}};Polymer.NeonAnimationRunnerBehavior=[Polymer.NeonAnimatableBehavior,Polymer.NeonAnimationRunnerBehaviorImpl];Polymer.NeonAnimationBehavior={properties:{animationTiming:{type:Object,value:function(){return{duration:500,easing:"cubic-bezier(0.4, 0, 0.2, 1)",fill:"both"}}}},isNeonAnimation:true,timingFromConfig:function(config){if(config.timing){for(var property in config.timing){this.animationTiming[property]=config.timing[property]}}return this.animationTiming},setPrefixedProperty:function(node,property,value){var map={transform:["webkitTransform"],transformOrigin:["mozTransformOrigin","webkitTransformOrigin"]};var prefixes=map[property];for(var prefix,index=0;prefix=prefixes[index];index++){node.style[prefix]=value}node.style[property]=value},complete:function(){}};Polymer({is:"opaque-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;this._effect=new KeyframeEffect(node,[{opacity:"1"},{opacity:"1"}],this.timingFromConfig(config));node.style.opacity="0";return this._effect},complete:function(config){config.node.style.opacity=""}});(function(){"use strict";var LAST_TOUCH_POSITION={pageX:0,pageY:0};var ROOT_TARGET=null;var SCROLLABLE_NODES=[];Polymer.IronDropdownScrollManager={get currentLockingElement(){return this._lockingElements[this._lockingElements.length-1]},elementIsScrollLocked:function(element){var currentLockingElement=this.currentLockingElement;if(currentLockingElement===undefined)return false;var scrollLocked;if(this._hasCachedLockedElement(element)){return true}if(this._hasCachedUnlockedElement(element)){return false}scrollLocked=!!currentLockingElement&¤tLockingElement!==element&&!this._composedTreeContains(currentLockingElement,element);if(scrollLocked){this._lockedElementCache.push(element)}else{this._unlockedElementCache.push(element)}return scrollLocked},pushScrollLock:function(element){if(this._lockingElements.indexOf(element)>=0){return}if(this._lockingElements.length===0){this._lockScrollInteractions()}this._lockingElements.push(element);this._lockedElementCache=[];this._unlockedElementCache=[]},removeScrollLock:function(element){var index=this._lockingElements.indexOf(element);if(index===-1){return}this._lockingElements.splice(index,1);this._lockedElementCache=[];this._unlockedElementCache=[];if(this._lockingElements.length===0){this._unlockScrollInteractions()}},_lockingElements:[],_lockedElementCache:null,_unlockedElementCache:null,_hasCachedLockedElement:function(element){return this._lockedElementCache.indexOf(element)>-1},_hasCachedUnlockedElement:function(element){return this._unlockedElementCache.indexOf(element)>-1},_composedTreeContains:function(element,child){var contentElements;var distributedNodes;var contentIndex;var nodeIndex;if(element.contains(child)){return true}contentElements=Polymer.dom(element).querySelectorAll("content");for(contentIndex=0;contentIndex<contentElements.length;++contentIndex){distributedNodes=Polymer.dom(contentElements[contentIndex]).getDistributedNodes();for(nodeIndex=0;nodeIndex<distributedNodes.length;++nodeIndex){if(this._composedTreeContains(distributedNodes[nodeIndex],child)){return true}}}return false},_scrollInteractionHandler:function(event){if(event.cancelable&&this._shouldPreventScrolling(event)){event.preventDefault()}if(event.targetTouches){var touch=event.targetTouches[0];LAST_TOUCH_POSITION.pageX=touch.pageX;LAST_TOUCH_POSITION.pageY=touch.pageY}},_lockScrollInteractions:function(){this._boundScrollHandler=this._boundScrollHandler||this._scrollInteractionHandler.bind(this);document.addEventListener("wheel",this._boundScrollHandler,true);document.addEventListener("mousewheel",this._boundScrollHandler,true);document.addEventListener("DOMMouseScroll",this._boundScrollHandler,true);document.addEventListener("touchstart",this._boundScrollHandler,true); -var HistoryGroup; - -Polymer({ - is: 'history-grouped-list', - behaviors: [ HistoryListBehavior ], - properties: { - historyData: { - type: Array - }, - groupedHistoryData_: { - type: Array - }, - searchedTerm: { - type: String, - value: '' - }, - range: { - type: Number - }, - queryStartTime: String, - queryEndTime: String - }, - observers: [ 'updateGroupedHistoryData_(range, historyData)' ], - createHistoryDomains_: function(visits) { - var domainIndexes = {}; - var domains = []; - for (var i = 0, visit; visit = visits[i]; i++) { - var domain = visit.domain; - if (domainIndexes[domain] == undefined) { - domainIndexes[domain] = domains.length; - domains.push({ - domain: domain, - visits: [], - expanded: false, - rendered: false - }); - } - domains[domainIndexes[domain]].visits.push(visit); - } - var sortByVisits = function(a, b) { - return b.visits.length - a.visits.length; - }; - domains.sort(sortByVisits); - return domains; - }, - updateGroupedHistoryData_: function() { - if (this.historyData.length == 0) { - this.groupedHistoryData_ = []; - return; - } - if (this.range == HistoryRange.WEEK) { - var days = []; - var currentDayVisits = [ this.historyData[0] ]; - var pushCurrentDay = function() { - days.push({ - title: this.searchedTerm ? currentDayVisits[0].dateShort : currentDayVisits[0].dateRelativeDay, - domains: this.createHistoryDomains_(currentDayVisits) - }); - }.bind(this); - var visitsSameDay = function(a, b) { - if (this.searchedTerm) return a.dateShort == b.dateShort; - return a.dateRelativeDay == b.dateRelativeDay; - }.bind(this); - for (var i = 1; i < this.historyData.length; i++) { - var visit = this.historyData[i]; - if (!visitsSameDay(visit, currentDayVisits[0])) { - pushCurrentDay(); - currentDayVisits = []; - } - currentDayVisits.push(visit); - } - pushCurrentDay(); - this.groupedHistoryData_ = days; - } else if (this.range == HistoryRange.MONTH) { - this.groupedHistoryData_ = [ { - title: this.queryStartTime + ' – ' + this.queryEndTime, - domains: this.createHistoryDomains_(this.historyData) - } ]; - } - }, - toggleDomainExpanded_: function(e) { - var collapse = e.currentTarget.parentNode.querySelector('iron-collapse'); - e.model.set('domain.rendered', true); - setTimeout(function() { - collapse.toggle(); - }, 0); - }, - needsTimeGap_: function(groupIndex, domainIndex, itemIndex) { - var visits = this.groupedHistoryData_[groupIndex].domains[domainIndex].visits; - return md_history.HistoryItem.needsTimeGap(visits, itemIndex, this.searchedTerm); - }, - pathForItem_: function(groupIndex, domainIndex, itemIndex) { - return [ 'groupedHistoryData_', groupIndex, 'domains', domainIndex, 'visits', itemIndex ].join('.'); - }, - getWebsiteIconStyle_: function(domain) { - return 'background-image: ' + cr.icon.getFavicon(domain.visits[0].url); - }, - getDropdownIcon_: function(expanded) { - return expanded ? 'cr:expand-less' : 'cr:expand-more'; - } -}); - -Polymer.PaperButtonBehaviorImpl = { - properties: { - elevation: { - type: Number, - reflectToAttribute: true, - readOnly: true - } - }, - observers: [ '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)', '_computeKeyboardClass(receivedFocusFromKeyboard)' ], - hostAttributes: { - role: 'button', - tabindex: '0', - animated: true - }, - _calculateElevation: function() { - var e = 1; - if (this.disabled) { - e = 0; - } else if (this.active || this.pressed) { - e = 4; - } else if (this.receivedFocusFromKeyboard) { - e = 3; - } - this._setElevation(e); - }, - _computeKeyboardClass: function(receivedFocusFromKeyboard) { - this.toggleClass('keyboard-focus', receivedFocusFromKeyboard); - }, - _spaceKeyDownHandler: function(event) { - Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event); - if (this.hasRipple() && this.getRipple().ripples.length < 1) { - this._ripple.uiDownAction(); - } - }, - _spaceKeyUpHandler: function(event) { - Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event); - if (this.hasRipple()) { - this._ripple.uiUpAction(); - } - } -}; - -Polymer.PaperButtonBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, Polymer.PaperRippleBehavior, Polymer.PaperButtonBehaviorImpl ]; - -Polymer({ - is: 'paper-button', - behaviors: [ Polymer.PaperButtonBehavior ], - properties: { - raised: { - type: Boolean, - reflectToAttribute: true, - value: false, - observer: '_calculateElevation' - } - }, - _calculateElevation: function() { - if (!this.raised) { - this._setElevation(0); - } else { - Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this); - } - } -}); - -Polymer.PaperItemBehaviorImpl = { - hostAttributes: { - role: 'option', - tabindex: '0' - } -}; - -Polymer.PaperItemBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, Polymer.PaperItemBehaviorImpl ]; - -Polymer({ - is: 'paper-item', - behaviors: [ Polymer.PaperItemBehavior ] -}); - -Polymer.IronFitBehavior = { - properties: { - sizingTarget: { - type: Object, - value: function() { - return this; - } - }, - fitInto: { - type: Object, - value: window - }, - noOverlap: { - type: Boolean - }, - positionTarget: { - type: Element - }, - horizontalAlign: { - type: String - }, - verticalAlign: { - type: String - }, - dynamicAlign: { - type: Boolean - }, - horizontalOffset: { - type: Number, - value: 0, - notify: true - }, - verticalOffset: { - type: Number, - value: 0, - notify: true - }, - autoFitOnAttach: { - type: Boolean, - value: false - }, - _fitInfo: { - type: Object - } - }, - get _fitWidth() { - var fitWidth; - if (this.fitInto === window) { - fitWidth = this.fitInto.innerWidth; - } else { - fitWidth = this.fitInto.getBoundingClientRect().width; - } - return fitWidth; - }, - get _fitHeight() { - var fitHeight; - if (this.fitInto === window) { - fitHeight = this.fitInto.innerHeight; - } else { - fitHeight = this.fitInto.getBoundingClientRect().height; - } - return fitHeight; - }, - get _fitLeft() { - var fitLeft; - if (this.fitInto === window) { - fitLeft = 0; - } else { - fitLeft = this.fitInto.getBoundingClientRect().left; - } - return fitLeft; - }, - get _fitTop() { - var fitTop; - if (this.fitInto === window) { - fitTop = 0; - } else { - fitTop = this.fitInto.getBoundingClientRect().top; - } - return fitTop; - }, - get _defaultPositionTarget() { - var parent = Polymer.dom(this).parentNode; - if (parent && parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { - parent = parent.host; - } - return parent; - }, - get _localeHorizontalAlign() { - if (this._isRTL) { - if (this.horizontalAlign === 'right') { - return 'left'; - } - if (this.horizontalAlign === 'left') { - return 'right'; - } - } - return this.horizontalAlign; - }, - attached: function() { - this._isRTL = window.getComputedStyle(this).direction == 'rtl'; - this.positionTarget = this.positionTarget || this._defaultPositionTarget; - if (this.autoFitOnAttach) { - if (window.getComputedStyle(this).display === 'none') { - setTimeout(function() { - this.fit(); - }.bind(this)); - } else { - this.fit(); - } - } - }, - fit: function() { - this.position(); - this.constrain(); - this.center(); - }, - _discoverInfo: function() { - if (this._fitInfo) { - return; - } - var target = window.getComputedStyle(this); - var sizer = window.getComputedStyle(this.sizingTarget); - this._fitInfo = { - inlineStyle: { - top: this.style.top || '', - left: this.style.left || '', - position: this.style.position || '' - }, - sizerInlineStyle: { - maxWidth: this.sizingTarget.style.maxWidth || '', - maxHeight: this.sizingTarget.style.maxHeight || '', - boxSizing: this.sizingTarget.style.boxSizing || '' - }, - positionedBy: { - vertically: target.top !== 'auto' ? 'top' : target.bottom !== 'auto' ? 'bottom' : null, - horizontally: target.left !== 'auto' ? 'left' : target.right !== 'auto' ? 'right' : null - }, - sizedBy: { - height: sizer.maxHeight !== 'none', - width: sizer.maxWidth !== 'none', - minWidth: parseInt(sizer.minWidth, 10) || 0, - minHeight: parseInt(sizer.minHeight, 10) || 0 - }, - margin: { - top: parseInt(target.marginTop, 10) || 0, - right: parseInt(target.marginRight, 10) || 0, - bottom: parseInt(target.marginBottom, 10) || 0, - left: parseInt(target.marginLeft, 10) || 0 - } - }; - if (this.verticalOffset) { - this._fitInfo.margin.top = this._fitInfo.margin.bottom = this.verticalOffset; - this._fitInfo.inlineStyle.marginTop = this.style.marginTop || ''; - this._fitInfo.inlineStyle.marginBottom = this.style.marginBottom || ''; - this.style.marginTop = this.style.marginBottom = this.verticalOffset + 'px'; - } - if (this.horizontalOffset) { - this._fitInfo.margin.left = this._fitInfo.margin.right = this.horizontalOffset; - this._fitInfo.inlineStyle.marginLeft = this.style.marginLeft || ''; - this._fitInfo.inlineStyle.marginRight = this.style.marginRight || ''; - this.style.marginLeft = this.style.marginRight = this.horizontalOffset + 'px'; - } - }, - resetFit: function() { - var info = this._fitInfo || {}; - for (var property in info.sizerInlineStyle) { - this.sizingTarget.style[property] = info.sizerInlineStyle[property]; - } - for (var property in info.inlineStyle) { - this.style[property] = info.inlineStyle[property]; - } - this._fitInfo = null; - }, - refit: function() { - var scrollLeft = this.sizingTarget.scrollLeft; - var scrollTop = this.sizingTarget.scrollTop; - this.resetFit(); - this.fit(); - this.sizingTarget.scrollLeft = scrollLeft; - this.sizingTarget.scrollTop = scrollTop; - }, - position: function() { - if (!this.horizontalAlign && !this.verticalAlign) { - return; - } - this._discoverInfo(); - this.style.position = 'fixed'; - this.sizingTarget.style.boxSizing = 'border-box'; - this.style.left = '0px'; - this.style.top = '0px'; - var rect = this.getBoundingClientRect(); - var positionRect = this.__getNormalizedRect(this.positionTarget); - var fitRect = this.__getNormalizedRect(this.fitInto); - var margin = this._fitInfo.margin; - var size = { - width: rect.width + margin.left + margin.right, - height: rect.height + margin.top + margin.bottom - }; - var position = this.__getPosition(this._localeHorizontalAlign, this.verticalAlign, size, positionRect, fitRect); - var left = position.left + margin.left; - var top = position.top + margin.top; - var right = Math.min(fitRect.right - margin.right, left + rect.width); - var bottom = Math.min(fitRect.bottom - margin.bottom, top + rect.height); - var minWidth = this._fitInfo.sizedBy.minWidth; - var minHeight = this._fitInfo.sizedBy.minHeight; - if (left < margin.left) { - left = margin.left; - if (right - left < minWidth) { - left = right - minWidth; - } - } - if (top < margin.top) { - top = margin.top; - if (bottom - top < minHeight) { - top = bottom - minHeight; - } - } - this.sizingTarget.style.maxWidth = right - left + 'px'; - this.sizingTarget.style.maxHeight = bottom - top + 'px'; - this.style.left = left - rect.left + 'px'; - this.style.top = top - rect.top + 'px'; - }, - constrain: function() { - if (this.horizontalAlign || this.verticalAlign) { - return; - } - this._discoverInfo(); - var info = this._fitInfo; - if (!info.positionedBy.vertically) { - this.style.position = 'fixed'; - this.style.top = '0px'; - } - if (!info.positionedBy.horizontally) { - this.style.position = 'fixed'; - this.style.left = '0px'; - } - this.sizingTarget.style.boxSizing = 'border-box'; - var rect = this.getBoundingClientRect(); - if (!info.sizedBy.height) { - this.__sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height'); - } - if (!info.sizedBy.width) { - this.__sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right', 'Width'); - } - }, - _sizeDimension: function(rect, positionedBy, start, end, extent) { - this.__sizeDimension(rect, positionedBy, start, end, extent); - }, - __sizeDimension: function(rect, positionedBy, start, end, extent) { - var info = this._fitInfo; - var fitRect = this.__getNormalizedRect(this.fitInto); - var max = extent === 'Width' ? fitRect.width : fitRect.height; - var flip = positionedBy === end; - var offset = flip ? max - rect[end] : rect[start]; - var margin = info.margin[flip ? start : end]; - var offsetExtent = 'offset' + extent; - var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent]; - this.sizingTarget.style['max' + extent] = max - margin - offset - sizingOffset + 'px'; - }, - center: function() { - if (this.horizontalAlign || this.verticalAlign) { - return; - } - this._discoverInfo(); - var positionedBy = this._fitInfo.positionedBy; - if (positionedBy.vertically && positionedBy.horizontally) { - return; - } - this.style.position = 'fixed'; - if (!positionedBy.vertically) { - this.style.top = '0px'; - } - if (!positionedBy.horizontally) { - this.style.left = '0px'; - } - var rect = this.getBoundingClientRect(); - var fitRect = this.__getNormalizedRect(this.fitInto); - if (!positionedBy.vertically) { - var top = fitRect.top - rect.top + (fitRect.height - rect.height) / 2; - this.style.top = top + 'px'; - } - if (!positionedBy.horizontally) { - var left = fitRect.left - rect.left + (fitRect.width - rect.width) / 2; - this.style.left = left + 'px'; - } - }, - __getNormalizedRect: function(target) { - if (target === document.documentElement || target === window) { - return { - top: 0, - left: 0, - width: window.innerWidth, - height: window.innerHeight, - right: window.innerWidth, - bottom: window.innerHeight - }; - } - return target.getBoundingClientRect(); - }, - __getCroppedArea: function(position, size, fitRect) { - var verticalCrop = Math.min(0, position.top) + Math.min(0, fitRect.bottom - (position.top + size.height)); - var horizontalCrop = Math.min(0, position.left) + Math.min(0, fitRect.right - (position.left + size.width)); - return Math.abs(verticalCrop) * size.width + Math.abs(horizontalCrop) * size.height; - }, - __getPosition: function(hAlign, vAlign, size, positionRect, fitRect) { - var positions = [ { - verticalAlign: 'top', - horizontalAlign: 'left', - top: positionRect.top, - left: positionRect.left - }, { - verticalAlign: 'top', - horizontalAlign: 'right', - top: positionRect.top, - left: positionRect.right - size.width - }, { - verticalAlign: 'bottom', - horizontalAlign: 'left', - top: positionRect.bottom - size.height, - left: positionRect.left - }, { - verticalAlign: 'bottom', - horizontalAlign: 'right', - top: positionRect.bottom - size.height, - left: positionRect.right - size.width - } ]; - if (this.noOverlap) { - for (var i = 0, l = positions.length; i < l; i++) { - var copy = {}; - for (var key in positions[i]) { - copy[key] = positions[i][key]; - } - positions.push(copy); - } - positions[0].top = positions[1].top += positionRect.height; - positions[2].top = positions[3].top -= positionRect.height; - positions[4].left = positions[6].left += positionRect.width; - positions[5].left = positions[7].left -= positionRect.width; - } - vAlign = vAlign === 'auto' ? null : vAlign; - hAlign = hAlign === 'auto' ? null : hAlign; - var position; - for (var i = 0; i < positions.length; i++) { - var pos = positions[i]; - if (!this.dynamicAlign && !this.noOverlap && pos.verticalAlign === vAlign && pos.horizontalAlign === hAlign) { - position = pos; - break; - } - var alignOk = (!vAlign || pos.verticalAlign === vAlign) && (!hAlign || pos.horizontalAlign === hAlign); - if (!this.dynamicAlign && !alignOk) { - continue; - } - position = position || pos; - pos.croppedArea = this.__getCroppedArea(pos, size, fitRect); - var diff = pos.croppedArea - position.croppedArea; - if (diff < 0 || diff === 0 && alignOk) { - position = pos; - } - if (position.croppedArea === 0 && alignOk) { - break; - } - } - return position; - } -}; - -(function() { - 'use strict'; - Polymer({ - is: 'iron-overlay-backdrop', - properties: { - opened: { - reflectToAttribute: true, - type: Boolean, - value: false, - observer: '_openedChanged' - } - }, - listeners: { - transitionend: '_onTransitionend' - }, - created: function() { - this.__openedRaf = null; - }, - attached: function() { - this.opened && this._openedChanged(this.opened); - }, - prepare: function() { - if (this.opened && !this.parentNode) { - Polymer.dom(document.body).appendChild(this); - } - }, - open: function() { - this.opened = true; - }, - close: function() { - this.opened = false; - }, - complete: function() { - if (!this.opened && this.parentNode === document.body) { - Polymer.dom(this.parentNode).removeChild(this); - } - }, - _onTransitionend: function(event) { - if (event && event.target === this) { - this.complete(); - } - }, - _openedChanged: function(opened) { - if (opened) { - this.prepare(); - } else { - var cs = window.getComputedStyle(this); - if (cs.transitionDuration === '0s' || cs.opacity == 0) { - this.complete(); - } - } - if (!this.isAttached) { - return; - } - if (this.__openedRaf) { - window.cancelAnimationFrame(this.__openedRaf); - this.__openedRaf = null; - } - this.scrollTop = this.scrollTop; - this.__openedRaf = window.requestAnimationFrame(function() { - this.__openedRaf = null; - this.toggleClass('opened', this.opened); - }.bind(this)); - } - }); -})(); - -Polymer.IronOverlayManagerClass = function() { - this._overlays = []; - this._minimumZ = 101; - this._backdropElement = null; - Polymer.Gestures.add(document, 'tap', this._onCaptureClick.bind(this)); - document.addEventListener('focus', this._onCaptureFocus.bind(this), true); - document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true); -}; - -Polymer.IronOverlayManagerClass.prototype = { - constructor: Polymer.IronOverlayManagerClass, - get backdropElement() { - if (!this._backdropElement) { - this._backdropElement = document.createElement('iron-overlay-backdrop'); - } - return this._backdropElement; - }, - get deepActiveElement() { - var active = document.activeElement || document.body; - while (active.root && Polymer.dom(active.root).activeElement) { - active = Polymer.dom(active.root).activeElement; - } - return active; - }, - _bringOverlayAtIndexToFront: function(i) { - var overlay = this._overlays[i]; - if (!overlay) { - return; - } - var lastI = this._overlays.length - 1; - var currentOverlay = this._overlays[lastI]; - if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) { - lastI--; - } - if (i >= lastI) { - return; - } - var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); - if (this._getZ(overlay) <= minimumZ) { - this._applyOverlayZ(overlay, minimumZ); - } - while (i < lastI) { - this._overlays[i] = this._overlays[i + 1]; - i++; - } - this._overlays[lastI] = overlay; - }, - addOrRemoveOverlay: function(overlay) { - if (overlay.opened) { - this.addOverlay(overlay); - } else { - this.removeOverlay(overlay); - } - }, - addOverlay: function(overlay) { - var i = this._overlays.indexOf(overlay); - if (i >= 0) { - this._bringOverlayAtIndexToFront(i); - this.trackBackdrop(); - return; - } - var insertionIndex = this._overlays.length; - var currentOverlay = this._overlays[insertionIndex - 1]; - var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ); - var newZ = this._getZ(overlay); - if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) { - this._applyOverlayZ(currentOverlay, minimumZ); - insertionIndex--; - var previousOverlay = this._overlays[insertionIndex - 1]; - minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ); - } - if (newZ <= minimumZ) { - this._applyOverlayZ(overlay, minimumZ); - } - this._overlays.splice(insertionIndex, 0, overlay); - this.trackBackdrop(); - }, - removeOverlay: function(overlay) { - var i = this._overlays.indexOf(overlay); - if (i === -1) { - return; - } - this._overlays.splice(i, 1); - this.trackBackdrop(); - }, - currentOverlay: function() { - var i = this._overlays.length - 1; - return this._overlays[i]; - }, - currentOverlayZ: function() { - return this._getZ(this.currentOverlay()); - }, - ensureMinimumZ: function(minimumZ) { - this._minimumZ = Math.max(this._minimumZ, minimumZ); - }, - focusOverlay: function() { - var current = this.currentOverlay(); - if (current) { - current._applyFocus(); - } - }, - trackBackdrop: function() { - var overlay = this._overlayWithBackdrop(); - if (!overlay && !this._backdropElement) { - return; - } - this.backdropElement.style.zIndex = this._getZ(overlay) - 1; - this.backdropElement.opened = !!overlay; - }, - getBackdrops: function() { - var backdrops = []; - for (var i = 0; i < this._overlays.length; i++) { - if (this._overlays[i].withBackdrop) { - backdrops.push(this._overlays[i]); - } - } - return backdrops; - }, - backdropZ: function() { - return this._getZ(this._overlayWithBackdrop()) - 1; - }, - _overlayWithBackdrop: function() { - for (var i = 0; i < this._overlays.length; i++) { - if (this._overlays[i].withBackdrop) { - return this._overlays[i]; - } - } - }, - _getZ: function(overlay) { - var z = this._minimumZ; - if (overlay) { - var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay).zIndex); - if (z1 === z1) { - z = z1; - } - } - return z; - }, - _setZ: function(element, z) { - element.style.zIndex = z; - }, - _applyOverlayZ: function(overlay, aboveZ) { - this._setZ(overlay, aboveZ + 2); - }, - _overlayInPath: function(path) { - path = path || []; - for (var i = 0; i < path.length; i++) { - if (path[i]._manager === this) { - return path[i]; - } - } - }, - _onCaptureClick: function(event) { - var overlay = this.currentOverlay(); - if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) { - overlay._onCaptureClick(event); - } - }, - _onCaptureFocus: function(event) { - var overlay = this.currentOverlay(); - if (overlay) { - overlay._onCaptureFocus(event); - } - }, - _onCaptureKeyDown: function(event) { - var overlay = this.currentOverlay(); - if (overlay) { - if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc')) { - overlay._onCaptureEsc(event); - } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'tab')) { - overlay._onCaptureTab(event); - } - } - }, - _shouldBeBehindOverlay: function(overlay1, overlay2) { - return !overlay1.alwaysOnTop && overlay2.alwaysOnTop; - } -}; - -Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass(); - -(function() { - 'use strict'; - Polymer.IronOverlayBehaviorImpl = { - properties: { - opened: { - observer: '_openedChanged', - type: Boolean, - value: false, - notify: true - }, - canceled: { - observer: '_canceledChanged', - readOnly: true, - type: Boolean, - value: false - }, - withBackdrop: { - observer: '_withBackdropChanged', - type: Boolean - }, - noAutoFocus: { - type: Boolean, - value: false - }, - noCancelOnEscKey: { - type: Boolean, - value: false - }, - noCancelOnOutsideClick: { - type: Boolean, - value: false - }, - closingReason: { - type: Object - }, - restoreFocusOnClose: { - type: Boolean, - value: false - }, - alwaysOnTop: { - type: Boolean - }, - _manager: { - type: Object, - value: Polymer.IronOverlayManager - }, - _focusedChild: { - type: Object - } - }, - listeners: { - 'iron-resize': '_onIronResize' - }, - get backdropElement() { - return this._manager.backdropElement; - }, - get _focusNode() { - return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]') || this; - }, - get _focusableNodes() { - var FOCUSABLE_WITH_DISABLED = [ 'a[href]', 'area[href]', 'iframe', '[tabindex]', '[contentEditable=true]' ]; - var FOCUSABLE_WITHOUT_DISABLED = [ 'input', 'select', 'textarea', 'button' ]; - var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') + ':not([tabindex="-1"]),' + FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),') + ':not([disabled]):not([tabindex="-1"])'; - var focusables = Polymer.dom(this).querySelectorAll(selector); - if (this.tabIndex >= 0) { - focusables.splice(0, 0, this); - } - return focusables.sort(function(a, b) { - if (a.tabIndex === b.tabIndex) { - return 0; - } - if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { - return 1; - } - return -1; - }); - }, - ready: function() { - this.__isAnimating = false; - this.__shouldRemoveTabIndex = false; - this.__firstFocusableNode = this.__lastFocusableNode = null; - this.__raf = null; - this.__restoreFocusNode = null; - this._ensureSetup(); - }, - attached: function() { - if (this.opened) { - this._openedChanged(this.opened); - } - this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); - }, - detached: function() { - Polymer.dom(this).unobserveNodes(this._observer); - this._observer = null; - if (this.__raf) { - window.cancelAnimationFrame(this.__raf); - this.__raf = null; - } - this._manager.removeOverlay(this); - }, - toggle: function() { - this._setCanceled(false); - this.opened = !this.opened; - }, - open: function() { - this._setCanceled(false); - this.opened = true; - }, - close: function() { - this._setCanceled(false); - this.opened = false; - }, - cancel: function(event) { - var cancelEvent = this.fire('iron-overlay-canceled', event, { - cancelable: true - }); - if (cancelEvent.defaultPrevented) { - return; - } - this._setCanceled(true); - this.opened = false; - }, - _ensureSetup: function() { - if (this._overlaySetup) { - return; - } - this._overlaySetup = true; - this.style.outline = 'none'; - this.style.display = 'none'; - }, - _openedChanged: function(opened) { - if (opened) { - this.removeAttribute('aria-hidden'); - } else { - this.setAttribute('aria-hidden', 'true'); - } - if (!this.isAttached) { - return; - } - this.__isAnimating = true; - this.__onNextAnimationFrame(this.__openedChanged); - }, - _canceledChanged: function() { - this.closingReason = this.closingReason || {}; - this.closingReason.canceled = this.canceled; - }, - _withBackdropChanged: function() { - if (this.withBackdrop && !this.hasAttribute('tabindex')) { - this.setAttribute('tabindex', '-1'); - this.__shouldRemoveTabIndex = true; - } else if (this.__shouldRemoveTabIndex) { - this.removeAttribute('tabindex'); - this.__shouldRemoveTabIndex = false; - } - if (this.opened && this.isAttached) { - this._manager.trackBackdrop(); - } - }, - _prepareRenderOpened: function() { - this.__restoreFocusNode = this._manager.deepActiveElement; - this._preparePositioning(); - this.refit(); - this._finishPositioning(); - if (this.noAutoFocus && document.activeElement === this._focusNode) { - this._focusNode.blur(); - this.__restoreFocusNode.focus(); - } - }, - _renderOpened: function() { - this._finishRenderOpened(); - }, - _renderClosed: function() { - this._finishRenderClosed(); - }, - _finishRenderOpened: function() { - this.notifyResize(); - this.__isAnimating = false; - var focusableNodes = this._focusableNodes; - this.__firstFocusableNode = focusableNodes[0]; - this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; - this.fire('iron-overlay-opened'); - }, - _finishRenderClosed: function() { - this.style.display = 'none'; - this.style.zIndex = ''; - this.notifyResize(); - this.__isAnimating = false; - this.fire('iron-overlay-closed', this.closingReason); - }, - _preparePositioning: function() { - this.style.transition = this.style.webkitTransition = 'none'; - this.style.transform = this.style.webkitTransform = 'none'; - this.style.display = ''; - }, - _finishPositioning: function() { - this.style.display = 'none'; - this.scrollTop = this.scrollTop; - this.style.transition = this.style.webkitTransition = ''; - this.style.transform = this.style.webkitTransform = ''; - this.style.display = ''; - this.scrollTop = this.scrollTop; - }, - _applyFocus: function() { - if (this.opened) { - if (!this.noAutoFocus) { - this._focusNode.focus(); - } - } else { - this._focusNode.blur(); - this._focusedChild = null; - if (this.restoreFocusOnClose && this.__restoreFocusNode) { - this.__restoreFocusNode.focus(); - } - this.__restoreFocusNode = null; - var currentOverlay = this._manager.currentOverlay(); - if (currentOverlay && this !== currentOverlay) { - currentOverlay._applyFocus(); - } - } - }, - _onCaptureClick: function(event) { - if (!this.noCancelOnOutsideClick) { - this.cancel(event); - } - }, - _onCaptureFocus: function(event) { - if (!this.withBackdrop) { - return; - } - var path = Polymer.dom(event).path; - if (path.indexOf(this) === -1) { - event.stopPropagation(); - this._applyFocus(); - } else { - this._focusedChild = path[0]; - } - }, - _onCaptureEsc: function(event) { - if (!this.noCancelOnEscKey) { - this.cancel(event); - } - }, - _onCaptureTab: function(event) { - if (!this.withBackdrop) { - return; - } - var shift = event.shiftKey; - var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode; - var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode; - var shouldWrap = false; - if (nodeToCheck === nodeToSet) { - shouldWrap = true; - } else { - var focusedNode = this._manager.deepActiveElement; - shouldWrap = focusedNode === nodeToCheck || focusedNode === this; - } - if (shouldWrap) { - event.preventDefault(); - this._focusedChild = nodeToSet; - this._applyFocus(); - } - }, - _onIronResize: function() { - if (this.opened && !this.__isAnimating) { - this.__onNextAnimationFrame(this.refit); - } - }, - _onNodesChange: function() { - if (this.opened && !this.__isAnimating) { - this.notifyResize(); - } - }, - __openedChanged: function() { - if (this.opened) { - this._prepareRenderOpened(); - this._manager.addOverlay(this); - this._applyFocus(); - this._renderOpened(); - } else { - this._manager.removeOverlay(this); - this._applyFocus(); - this._renderClosed(); - } - }, - __onNextAnimationFrame: function(callback) { - if (this.__raf) { - window.cancelAnimationFrame(this.__raf); - } - var self = this; - this.__raf = window.requestAnimationFrame(function nextAnimationFrame() { - self.__raf = null; - callback.call(self); - }); - } - }; - Polymer.IronOverlayBehavior = [ Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl ]; -})(); - -Polymer.NeonAnimatableBehavior = { - properties: { - animationConfig: { - type: Object - }, - entryAnimation: { - observer: '_entryAnimationChanged', - type: String - }, - exitAnimation: { - observer: '_exitAnimationChanged', - type: String - } - }, - _entryAnimationChanged: function() { - this.animationConfig = this.animationConfig || {}; - this.animationConfig['entry'] = [ { - name: this.entryAnimation, - node: this - } ]; - }, - _exitAnimationChanged: function() { - this.animationConfig = this.animationConfig || {}; - this.animationConfig['exit'] = [ { - name: this.exitAnimation, - node: this - } ]; - }, - _copyProperties: function(config1, config2) { - for (var property in config2) { - config1[property] = config2[property]; - } - }, - _cloneConfig: function(config) { - var clone = { - isClone: true - }; - this._copyProperties(clone, config); - return clone; - }, - _getAnimationConfigRecursive: function(type, map, allConfigs) { - if (!this.animationConfig) { - return; - } - if (this.animationConfig.value && typeof this.animationConfig.value === 'function') { - this._warn(this._logf('playAnimation', "Please put 'animationConfig' inside of your components 'properties' object instead of outside of it.")); - return; - } - var thisConfig; - if (type) { - thisConfig = this.animationConfig[type]; - } else { - thisConfig = this.animationConfig; - } - if (!Array.isArray(thisConfig)) { - thisConfig = [ thisConfig ]; - } - if (thisConfig) { - for (var config, index = 0; config = thisConfig[index]; index++) { - if (config.animatable) { - config.animatable._getAnimationConfigRecursive(config.type || type, map, allConfigs); - } else { - if (config.id) { - var cachedConfig = map[config.id]; - if (cachedConfig) { - if (!cachedConfig.isClone) { - map[config.id] = this._cloneConfig(cachedConfig); - cachedConfig = map[config.id]; - } - this._copyProperties(cachedConfig, config); - } else { - map[config.id] = config; - } - } else { - allConfigs.push(config); - } - } - } - } - }, - getAnimationConfig: function(type) { - var map = {}; - var allConfigs = []; - this._getAnimationConfigRecursive(type, map, allConfigs); - for (var key in map) { - allConfigs.push(map[key]); - } - return allConfigs; - } -}; - -Polymer.NeonAnimationRunnerBehaviorImpl = { - _configureAnimations: function(configs) { - var results = []; - if (configs.length > 0) { - for (var config, index = 0; config = configs[index]; index++) { - var neonAnimation = document.createElement(config.name); - if (neonAnimation.isNeonAnimation) { - var result = null; - try { - result = neonAnimation.configure(config); - if (typeof result.cancel != 'function') { - result = document.timeline.play(result); - } - } catch (e) { - result = null; - console.warn('Couldnt play', '(', config.name, ').', e); - } - if (result) { - results.push({ - neonAnimation: neonAnimation, - config: config, - animation: result - }); - } - } else { - console.warn(this.is + ':', config.name, 'not found!'); - } - } - } - return results; - }, - _shouldComplete: function(activeEntries) { - var finished = true; - for (var i = 0; i < activeEntries.length; i++) { - if (activeEntries[i].animation.playState != 'finished') { - finished = false; - break; - } - } - return finished; - }, - _complete: function(activeEntries) { - for (var i = 0; i < activeEntries.length; i++) { - activeEntries[i].neonAnimation.complete(activeEntries[i].config); - } - for (var i = 0; i < activeEntries.length; i++) { - activeEntries[i].animation.cancel(); - } - }, - playAnimation: function(type, cookie) { - var configs = this.getAnimationConfig(type); - if (!configs) { - return; - } - this._active = this._active || {}; - if (this._active[type]) { - this._complete(this._active[type]); - delete this._active[type]; - } - var activeEntries = this._configureAnimations(configs); - if (activeEntries.length == 0) { - this.fire('neon-animation-finish', cookie, { - bubbles: false - }); - return; - } - this._active[type] = activeEntries; - for (var i = 0; i < activeEntries.length; i++) { - activeEntries[i].animation.onfinish = function() { - if (this._shouldComplete(activeEntries)) { - this._complete(activeEntries); - delete this._active[type]; - this.fire('neon-animation-finish', cookie, { - bubbles: false - }); - } - }.bind(this); - } - }, - cancelAnimation: function() { - for (var k in this._animations) { - this._animations[k].cancel(); - } - this._animations = {}; - } -}; - -Polymer.NeonAnimationRunnerBehavior = [ Polymer.NeonAnimatableBehavior, Polymer.NeonAnimationRunnerBehaviorImpl ]; - -Polymer.NeonAnimationBehavior = { - properties: { - animationTiming: { - type: Object, - value: function() { - return { - duration: 500, - easing: 'cubic-bezier(0.4, 0, 0.2, 1)', - fill: 'both' - }; - } - } - }, - isNeonAnimation: true, - timingFromConfig: function(config) { - if (config.timing) { - for (var property in config.timing) { - this.animationTiming[property] = config.timing[property]; - } - } - return this.animationTiming; - }, - setPrefixedProperty: function(node, property, value) { - var map = { - transform: [ 'webkitTransform' ], - transformOrigin: [ 'mozTransformOrigin', 'webkitTransformOrigin' ] - }; - var prefixes = map[property]; - for (var prefix, index = 0; prefix = prefixes[index]; index++) { - node.style[prefix] = value; - } - node.style[property] = value; - }, - complete: function() {} -}; - -Polymer({ - is: 'opaque-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - this._effect = new KeyframeEffect(node, [ { - opacity: '1' - }, { - opacity: '1' - } ], this.timingFromConfig(config)); - node.style.opacity = '0'; - return this._effect; - }, - complete: function(config) { - config.node.style.opacity = ''; - } -}); - -(function() { - 'use strict'; - var LAST_TOUCH_POSITION = { - pageX: 0, - pageY: 0 - }; - var ROOT_TARGET = null; - var SCROLLABLE_NODES = []; - Polymer.IronDropdownScrollManager = { - get currentLockingElement() { - return this._lockingElements[this._lockingElements.length - 1]; - }, - elementIsScrollLocked: function(element) { - var currentLockingElement = this.currentLockingElement; - if (currentLockingElement === undefined) return false; - var scrollLocked; - if (this._hasCachedLockedElement(element)) { - return true; - } - if (this._hasCachedUnlockedElement(element)) { - return false; - } - scrollLocked = !!currentLockingElement && currentLockingElement !== element && !this._composedTreeContains(currentLockingElement, element); - if (scrollLocked) { - this._lockedElementCache.push(element); - } else { - this._unlockedElementCache.push(element); - } - return scrollLocked; - }, - pushScrollLock: function(element) { - if (this._lockingElements.indexOf(element) >= 0) { - return; - } - if (this._lockingElements.length === 0) { - this._lockScrollInteractions(); - } - this._lockingElements.push(element); - this._lockedElementCache = []; - this._unlockedElementCache = []; - }, - removeScrollLock: function(element) { - var index = this._lockingElements.indexOf(element); - if (index === -1) { - return; - } - this._lockingElements.splice(index, 1); - this._lockedElementCache = []; - this._unlockedElementCache = []; - if (this._lockingElements.length === 0) { - this._unlockScrollInteractions(); - } - }, - _lockingElements: [], - _lockedElementCache: null, - _unlockedElementCache: null, - _hasCachedLockedElement: function(element) { - return this._lockedElementCache.indexOf(element) > -1; - }, - _hasCachedUnlockedElement: function(element) { - return this._unlockedElementCache.indexOf(element) > -1; - }, - _composedTreeContains: function(element, child) { - var contentElements; - var distributedNodes; - var contentIndex; - var nodeIndex; - if (element.contains(child)) { - return true; - } - contentElements = Polymer.dom(element).querySelectorAll('content'); - for (contentIndex = 0; contentIndex < contentElements.length; ++contentIndex) { - distributedNodes = Polymer.dom(contentElements[contentIndex]).getDistributedNodes(); - for (nodeIndex = 0; nodeIndex < distributedNodes.length; ++nodeIndex) { - if (this._composedTreeContains(distributedNodes[nodeIndex], child)) { - return true; - } - } - } - return false; - }, - _scrollInteractionHandler: function(event) { - if (event.cancelable && this._shouldPreventScrolling(event)) { - event.preventDefault(); - } - if (event.targetTouches) { - var touch = event.targetTouches[0]; - LAST_TOUCH_POSITION.pageX = touch.pageX; - LAST_TOUCH_POSITION.pageY = touch.pageY; - } - }, - _lockScrollInteractions: function() { - this._boundScrollHandler = this._boundScrollHandler || this._scrollInteractionHandler.bind(this); - document.addEventListener('wheel', this._boundScrollHandler, true); - document.addEventListener('mousewheel', this._boundScrollHandler, true); - document.addEventListener('DOMMouseScroll', this._boundScrollHandler, true); - document.addEventListener('touchstart', this._boundScrollHandler, true); - document.addEventListener('touchmove', this._boundScrollHandler, true); - }, - _unlockScrollInteractions: function() { - document.removeEventListener('wheel', this._boundScrollHandler, true); - document.removeEventListener('mousewheel', this._boundScrollHandler, true); - document.removeEventListener('DOMMouseScroll', this._boundScrollHandler, true); - document.removeEventListener('touchstart', this._boundScrollHandler, true); - document.removeEventListener('touchmove', this._boundScrollHandler, true); - }, - _shouldPreventScrolling: function(event) { - var target = Polymer.dom(event).rootTarget; - if (event.type !== 'touchmove' && ROOT_TARGET !== target) { - ROOT_TARGET = target; - SCROLLABLE_NODES = this._getScrollableNodes(Polymer.dom(event).path); - } - if (!SCROLLABLE_NODES.length) { - return true; - } - if (event.type === 'touchstart') { - return false; - } - var info = this._getScrollInfo(event); - return !this._getScrollingNode(SCROLLABLE_NODES, info.deltaX, info.deltaY); - }, - _getScrollableNodes: function(nodes) { - var scrollables = []; - var lockingIndex = nodes.indexOf(this.currentLockingElement); - for (var i = 0; i <= lockingIndex; i++) { - var node = nodes[i]; - if (node.nodeType === 11) { - continue; - } - var style = node.style; - if (style.overflow !== 'scroll' && style.overflow !== 'auto') { - style = window.getComputedStyle(node); - } - if (style.overflow === 'scroll' || style.overflow === 'auto') { - scrollables.push(node); - } - } - return scrollables; - }, - _getScrollingNode: function(nodes, deltaX, deltaY) { - if (!deltaX && !deltaY) { - return; - } - var verticalScroll = Math.abs(deltaY) >= Math.abs(deltaX); - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - var canScroll = false; - if (verticalScroll) { - canScroll = deltaY < 0 ? node.scrollTop > 0 : node.scrollTop < node.scrollHeight - node.clientHeight; - } else { - canScroll = deltaX < 0 ? node.scrollLeft > 0 : node.scrollLeft < node.scrollWidth - node.clientWidth; - } - if (canScroll) { - return node; - } - } - }, - _getScrollInfo: function(event) { - var info = { - deltaX: event.deltaX, - deltaY: event.deltaY - }; - if ('deltaX' in event) {} else if ('wheelDeltaX' in event) { - info.deltaX = -event.wheelDeltaX; - info.deltaY = -event.wheelDeltaY; - } else if ('axis' in event) { - info.deltaX = event.axis === 1 ? event.detail : 0; - info.deltaY = event.axis === 2 ? event.detail : 0; - } else if (event.targetTouches) { - var touch = event.targetTouches[0]; - info.deltaX = LAST_TOUCH_POSITION.pageX - touch.pageX; - info.deltaY = LAST_TOUCH_POSITION.pageY - touch.pageY; - } - return info; - } - }; -})(); - -(function() { - 'use strict'; - Polymer({ - is: 'iron-dropdown', - behaviors: [ Polymer.IronControlState, Polymer.IronA11yKeysBehavior, Polymer.IronOverlayBehavior, Polymer.NeonAnimationRunnerBehavior ], - properties: { - horizontalAlign: { - type: String, - value: 'left', - reflectToAttribute: true - }, - verticalAlign: { - type: String, - value: 'top', - reflectToAttribute: true - }, - openAnimationConfig: { - type: Object - }, - closeAnimationConfig: { - type: Object - }, - focusTarget: { - type: Object - }, - noAnimations: { - type: Boolean, - value: false - }, - allowOutsideScroll: { - type: Boolean, - value: false - }, - _boundOnCaptureScroll: { - type: Function, - value: function() { - return this._onCaptureScroll.bind(this); - } - } - }, - listeners: { - 'neon-animation-finish': '_onNeonAnimationFinish' - }, - observers: [ '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)' ], - get containedElement() { - return Polymer.dom(this.$.content).getDistributedNodes()[0]; - }, - get _focusTarget() { - return this.focusTarget || this.containedElement; - }, - ready: function() { - this._scrollTop = 0; - this._scrollLeft = 0; - this._refitOnScrollRAF = null; - }, - attached: function() { - if (!this.sizingTarget || this.sizingTarget === this) { - this.sizingTarget = this.containedElement; - } - }, - detached: function() { - this.cancelAnimation(); - document.removeEventListener('scroll', this._boundOnCaptureScroll); - Polymer.IronDropdownScrollManager.removeScrollLock(this); - }, - _openedChanged: function() { - if (this.opened && this.disabled) { - this.cancel(); - } else { - this.cancelAnimation(); - this._updateAnimationConfig(); - this._saveScrollPosition(); - if (this.opened) { - document.addEventListener('scroll', this._boundOnCaptureScroll); - !this.allowOutsideScroll && Polymer.IronDropdownScrollManager.pushScrollLock(this); - } else { - document.removeEventListener('scroll', this._boundOnCaptureScroll); - Polymer.IronDropdownScrollManager.removeScrollLock(this); - } - Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments); - } - }, - _renderOpened: function() { - if (!this.noAnimations && this.animationConfig.open) { - this.$.contentWrapper.classList.add('animating'); - this.playAnimation('open'); - } else { - Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments); - } - }, - _renderClosed: function() { - if (!this.noAnimations && this.animationConfig.close) { - this.$.contentWrapper.classList.add('animating'); - this.playAnimation('close'); - } else { - Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments); - } - }, - _onNeonAnimationFinish: function() { - this.$.contentWrapper.classList.remove('animating'); - if (this.opened) { - this._finishRenderOpened(); - } else { - this._finishRenderClosed(); - } - }, - _onCaptureScroll: function() { - if (!this.allowOutsideScroll) { - this._restoreScrollPosition(); - } else { - this._refitOnScrollRAF && window.cancelAnimationFrame(this._refitOnScrollRAF); - this._refitOnScrollRAF = window.requestAnimationFrame(this.refit.bind(this)); - } - }, - _saveScrollPosition: function() { - if (document.scrollingElement) { - this._scrollTop = document.scrollingElement.scrollTop; - this._scrollLeft = document.scrollingElement.scrollLeft; - } else { - this._scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop); - this._scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); - } - }, - _restoreScrollPosition: function() { - if (document.scrollingElement) { - document.scrollingElement.scrollTop = this._scrollTop; - document.scrollingElement.scrollLeft = this._scrollLeft; - } else { - document.documentElement.scrollTop = this._scrollTop; - document.documentElement.scrollLeft = this._scrollLeft; - document.body.scrollTop = this._scrollTop; - document.body.scrollLeft = this._scrollLeft; - } - }, - _updateAnimationConfig: function() { - var animations = (this.openAnimationConfig || []).concat(this.closeAnimationConfig || []); - for (var i = 0; i < animations.length; i++) { - animations[i].node = this.containedElement; - } - this.animationConfig = { - open: this.openAnimationConfig, - close: this.closeAnimationConfig - }; - }, - _updateOverlayPosition: function() { - if (this.isAttached) { - this.notifyResize(); - } - }, - _applyFocus: function() { - var focusTarget = this.focusTarget || this.containedElement; - if (focusTarget && this.opened && !this.noAutoFocus) { - focusTarget.focus(); - } else { - Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); - } - } - }); -})(); - -Polymer({ - is: 'fade-in-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - this._effect = new KeyframeEffect(node, [ { - opacity: '0' - }, { - opacity: '1' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'fade-out-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - this._effect = new KeyframeEffect(node, [ { - opacity: '1' - }, { - opacity: '0' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'paper-menu-grow-height-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - var rect = node.getBoundingClientRect(); - var height = rect.height; - this._effect = new KeyframeEffect(node, [ { - height: height / 2 + 'px' - }, { - height: height + 'px' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'paper-menu-grow-width-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - var rect = node.getBoundingClientRect(); - var width = rect.width; - this._effect = new KeyframeEffect(node, [ { - width: width / 2 + 'px' - }, { - width: width + 'px' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'paper-menu-shrink-width-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - var rect = node.getBoundingClientRect(); - var width = rect.width; - this._effect = new KeyframeEffect(node, [ { - width: width + 'px' - }, { - width: width - width / 20 + 'px' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - -Polymer({ - is: 'paper-menu-shrink-height-animation', - behaviors: [ Polymer.NeonAnimationBehavior ], - configure: function(config) { - var node = config.node; - var rect = node.getBoundingClientRect(); - var height = rect.height; - var top = rect.top; - this.setPrefixedProperty(node, 'transformOrigin', '0 0'); - this._effect = new KeyframeEffect(node, [ { - height: height + 'px', - transform: 'translateY(0)' - }, { - height: height / 2 + 'px', - transform: 'translateY(-20px)' - } ], this.timingFromConfig(config)); - return this._effect; - } -}); - +document.addEventListener("touchmove",this._boundScrollHandler,true)},_unlockScrollInteractions:function(){document.removeEventListener("wheel",this._boundScrollHandler,true);document.removeEventListener("mousewheel",this._boundScrollHandler,true);document.removeEventListener("DOMMouseScroll",this._boundScrollHandler,true);document.removeEventListener("touchstart",this._boundScrollHandler,true);document.removeEventListener("touchmove",this._boundScrollHandler,true)},_shouldPreventScrolling:function(event){var target=Polymer.dom(event).rootTarget;if(event.type!=="touchmove"&&ROOT_TARGET!==target){ROOT_TARGET=target;SCROLLABLE_NODES=this._getScrollableNodes(Polymer.dom(event).path)}if(!SCROLLABLE_NODES.length){return true}if(event.type==="touchstart"){return false}var info=this._getScrollInfo(event);return!this._getScrollingNode(SCROLLABLE_NODES,info.deltaX,info.deltaY)},_getScrollableNodes:function(nodes){var scrollables=[];var lockingIndex=nodes.indexOf(this.currentLockingElement);for(var i=0;i<=lockingIndex;i++){var node=nodes[i];if(node.nodeType===11){continue}var style=node.style;if(style.overflow!=="scroll"&&style.overflow!=="auto"){style=window.getComputedStyle(node)}if(style.overflow==="scroll"||style.overflow==="auto"){scrollables.push(node)}}return scrollables},_getScrollingNode:function(nodes,deltaX,deltaY){if(!deltaX&&!deltaY){return}var verticalScroll=Math.abs(deltaY)>=Math.abs(deltaX);for(var i=0;i<nodes.length;i++){var node=nodes[i];var canScroll=false;if(verticalScroll){canScroll=deltaY<0?node.scrollTop>0:node.scrollTop<node.scrollHeight-node.clientHeight}else{canScroll=deltaX<0?node.scrollLeft>0:node.scrollLeft<node.scrollWidth-node.clientWidth}if(canScroll){return node}}},_getScrollInfo:function(event){var info={deltaX:event.deltaX,deltaY:event.deltaY};if("deltaX"in event){}else if("wheelDeltaX"in event){info.deltaX=-event.wheelDeltaX;info.deltaY=-event.wheelDeltaY}else if("axis"in event){info.deltaX=event.axis===1?event.detail:0;info.deltaY=event.axis===2?event.detail:0}else if(event.targetTouches){var touch=event.targetTouches[0];info.deltaX=LAST_TOUCH_POSITION.pageX-touch.pageX;info.deltaY=LAST_TOUCH_POSITION.pageY-touch.pageY}return info}}})();(function(){"use strict";Polymer({is:"iron-dropdown",behaviors:[Polymer.IronControlState,Polymer.IronA11yKeysBehavior,Polymer.IronOverlayBehavior,Polymer.NeonAnimationRunnerBehavior],properties:{horizontalAlign:{type:String,value:"left",reflectToAttribute:true},verticalAlign:{type:String,value:"top",reflectToAttribute:true},openAnimationConfig:{type:Object},closeAnimationConfig:{type:Object},focusTarget:{type:Object},noAnimations:{type:Boolean,value:false},allowOutsideScroll:{type:Boolean,value:false},_boundOnCaptureScroll:{type:Function,value:function(){return this._onCaptureScroll.bind(this)}}},listeners:{"neon-animation-finish":"_onNeonAnimationFinish"},observers:["_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)"],get containedElement(){return Polymer.dom(this.$.content).getDistributedNodes()[0]},get _focusTarget(){return this.focusTarget||this.containedElement},ready:function(){this._scrollTop=0;this._scrollLeft=0;this._refitOnScrollRAF=null},attached:function(){if(!this.sizingTarget||this.sizingTarget===this){this.sizingTarget=this.containedElement}},detached:function(){this.cancelAnimation();document.removeEventListener("scroll",this._boundOnCaptureScroll);Polymer.IronDropdownScrollManager.removeScrollLock(this)},_openedChanged:function(){if(this.opened&&this.disabled){this.cancel()}else{this.cancelAnimation();this._updateAnimationConfig();this._saveScrollPosition();if(this.opened){document.addEventListener("scroll",this._boundOnCaptureScroll);!this.allowOutsideScroll&&Polymer.IronDropdownScrollManager.pushScrollLock(this)}else{document.removeEventListener("scroll",this._boundOnCaptureScroll);Polymer.IronDropdownScrollManager.removeScrollLock(this)}Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this,arguments)}},_renderOpened:function(){if(!this.noAnimations&&this.animationConfig.open){this.$.contentWrapper.classList.add("animating");this.playAnimation("open")}else{Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this,arguments)}},_renderClosed:function(){if(!this.noAnimations&&this.animationConfig.close){this.$.contentWrapper.classList.add("animating");this.playAnimation("close")}else{Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this,arguments)}},_onNeonAnimationFinish:function(){this.$.contentWrapper.classList.remove("animating");if(this.opened){this._finishRenderOpened()}else{this._finishRenderClosed()}},_onCaptureScroll:function(){if(!this.allowOutsideScroll){this._restoreScrollPosition()}else{this._refitOnScrollRAF&&window.cancelAnimationFrame(this._refitOnScrollRAF);this._refitOnScrollRAF=window.requestAnimationFrame(this.refit.bind(this))}},_saveScrollPosition:function(){if(document.scrollingElement){this._scrollTop=document.scrollingElement.scrollTop;this._scrollLeft=document.scrollingElement.scrollLeft}else{this._scrollTop=Math.max(document.documentElement.scrollTop,document.body.scrollTop);this._scrollLeft=Math.max(document.documentElement.scrollLeft,document.body.scrollLeft)}},_restoreScrollPosition:function(){if(document.scrollingElement){document.scrollingElement.scrollTop=this._scrollTop;document.scrollingElement.scrollLeft=this._scrollLeft}else{document.documentElement.scrollTop=this._scrollTop;document.documentElement.scrollLeft=this._scrollLeft;document.body.scrollTop=this._scrollTop;document.body.scrollLeft=this._scrollLeft}},_updateAnimationConfig:function(){var animations=(this.openAnimationConfig||[]).concat(this.closeAnimationConfig||[]);for(var i=0;i<animations.length;i++){animations[i].node=this.containedElement}this.animationConfig={open:this.openAnimationConfig,close:this.closeAnimationConfig}},_updateOverlayPosition:function(){if(this.isAttached){this.notifyResize()}},_applyFocus:function(){var focusTarget=this.focusTarget||this.containedElement;if(focusTarget&&this.opened&&!this.noAutoFocus){focusTarget.focus()}else{Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this,arguments)}}})})();Polymer({is:"fade-in-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;this._effect=new KeyframeEffect(node,[{opacity:"0"},{opacity:"1"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"fade-out-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;this._effect=new KeyframeEffect(node,[{opacity:"1"},{opacity:"0"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"paper-menu-grow-height-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;var rect=node.getBoundingClientRect();var height=rect.height;this._effect=new KeyframeEffect(node,[{height:height/2+"px"},{height:height+"px"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"paper-menu-grow-width-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;var rect=node.getBoundingClientRect();var width=rect.width;this._effect=new KeyframeEffect(node,[{width:width/2+"px"},{width:width+"px"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"paper-menu-shrink-width-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;var rect=node.getBoundingClientRect();var width=rect.width;this._effect=new KeyframeEffect(node,[{width:width+"px"},{width:width-width/20+"px"}],this.timingFromConfig(config));return this._effect}});Polymer({is:"paper-menu-shrink-height-animation",behaviors:[Polymer.NeonAnimationBehavior],configure:function(config){var node=config.node;var rect=node.getBoundingClientRect();var height=rect.height;var top=rect.top;this.setPrefixedProperty(node,"transformOrigin","0 0");this._effect=new KeyframeEffect(node,[{height:height+"px",transform:"translateY(0)"},{height:height/2+"px",transform:"translateY(-20px)"}],this.timingFromConfig(config));return this._effect}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var SLIDE_CUBIC_BEZIER = 'cubic-bezier(0.3, 0.95, 0.5, 1)'; - -Polymer({ - is: 'cr-shared-menu', - behaviors: [ Polymer.IronA11yKeysBehavior ], - properties: { - menuOpen: { - type: Boolean, - observer: 'menuOpenChanged_', - value: false, - notify: true - }, - itemData: { - type: Object, - value: null - }, - keyEventTarget: { - type: Object, - value: function() { - return this.$.menu; - } - }, - openAnimationConfig: { - type: Object, - value: function() { - return [ { - name: 'fade-in-animation', - timing: { - delay: 50, - duration: 200 - } - }, { - name: 'paper-menu-grow-width-animation', - timing: { - delay: 50, - duration: 150, - easing: SLIDE_CUBIC_BEZIER - } - }, { - name: 'paper-menu-grow-height-animation', - timing: { - delay: 100, - duration: 275, - easing: SLIDE_CUBIC_BEZIER - } - } ]; - } - }, - closeAnimationConfig: { - type: Object, - value: function() { - return [ { - name: 'fade-out-animation', - timing: { - duration: 150 - } - } ]; - } - } - }, - keyBindings: { - tab: 'onTabPressed_' - }, - listeners: { - 'dropdown.iron-overlay-canceled': 'onOverlayCanceled_' - }, - lastAnchor_: null, - firstFocus_: null, - lastFocus_: null, - attached: function() { - window.addEventListener('resize', this.closeMenu.bind(this)); - }, - closeMenu: function() { - if (this.root.activeElement == null) { - this.$.dropdown.restoreFocusOnClose = false; - } - this.menuOpen = false; - }, - openMenu: function(anchor, opt_itemData) { - if (this.lastAnchor_ == anchor && this.menuOpen) return; - if (this.menuOpen) this.closeMenu(); - this.itemData = opt_itemData || null; - this.lastAnchor_ = anchor; - this.$.dropdown.restoreFocusOnClose = true; - var focusableChildren = Polymer.dom(this).querySelectorAll('[tabindex]:not([disabled]):not([hidden]),' + 'button:not([disabled]):not([hidden])'); - if (focusableChildren.length > 0) { - this.$.dropdown.focusTarget = focusableChildren[0]; - this.firstFocus_ = focusableChildren[0]; - this.lastFocus_ = focusableChildren[focusableChildren.length - 1]; - } - this.$.dropdown.positionTarget = anchor; - this.menuOpen = true; - }, - toggleMenu: function(anchor, opt_itemData) { - if (anchor == this.lastAnchor_ && this.menuOpen) this.closeMenu(); else this.openMenu(anchor, opt_itemData); - }, - onTabPressed_: function(e) { - if (!this.firstFocus_ || !this.lastFocus_) return; - var toFocus; - var keyEvent = e.detail.keyboardEvent; - if (keyEvent.shiftKey && keyEvent.target == this.firstFocus_) toFocus = this.lastFocus_; else if (!keyEvent.shiftKey && keyEvent.target == this.lastFocus_) toFocus = this.firstFocus_; - if (!toFocus) return; - e.preventDefault(); - toFocus.focus(); - }, - menuOpenChanged_: function() { - if (!this.menuOpen) { - this.itemData = null; - this.lastAnchor_ = null; - } - }, - onOverlayCanceled_: function(e) { - if (e.detail.type == 'tap') this.$.dropdown.restoreFocusOnClose = false; - } -}); - -Polymer({ - is: 'paper-icon-button-light', - "extends": 'button', - behaviors: [ Polymer.PaperRippleBehavior ], - listeners: { - down: '_rippleDown', - up: '_rippleUp', - focus: '_rippleDown', - blur: '_rippleUp' - }, - _rippleDown: function() { - this.getRipple().downAction(); - }, - _rippleUp: function() { - this.getRipple().upAction(); - }, - ensureRipple: function(var_args) { - var lastRipple = this._ripple; - Polymer.PaperRippleBehavior.ensureRipple.apply(this, arguments); - if (this._ripple && this._ripple !== lastRipple) { - this._ripple.center = true; - this._ripple.classList.add('circle'); - } - } -}); - +var SLIDE_CUBIC_BEZIER="cubic-bezier(0.3, 0.95, 0.5, 1)";Polymer({is:"cr-shared-menu",behaviors:[Polymer.IronA11yKeysBehavior],properties:{menuOpen:{type:Boolean,observer:"menuOpenChanged_",value:false,notify:true},itemData:{type:Object,value:null},keyEventTarget:{type:Object,value:function(){return this.$.menu}},openAnimationConfig:{type:Object,value:function(){return[{name:"fade-in-animation",timing:{delay:50,duration:200}},{name:"paper-menu-grow-width-animation",timing:{delay:50,duration:150,easing:SLIDE_CUBIC_BEZIER}},{name:"paper-menu-grow-height-animation",timing:{delay:100,duration:275,easing:SLIDE_CUBIC_BEZIER}}]}},closeAnimationConfig:{type:Object,value:function(){return[{name:"fade-out-animation",timing:{duration:150}}]}}},keyBindings:{tab:"onTabPressed_"},listeners:{"dropdown.iron-overlay-canceled":"onOverlayCanceled_"},lastAnchor_:null,firstFocus_:null,lastFocus_:null,attached:function(){window.addEventListener("resize",this.closeMenu.bind(this))},closeMenu:function(){if(this.root.activeElement==null){this.$.dropdown.restoreFocusOnClose=false}this.menuOpen=false},openMenu:function(anchor,opt_itemData){if(this.lastAnchor_==anchor&&this.menuOpen)return;if(this.menuOpen)this.closeMenu();this.itemData=opt_itemData||null;this.lastAnchor_=anchor;this.$.dropdown.restoreFocusOnClose=true;var focusableChildren=Polymer.dom(this).querySelectorAll("[tabindex]:not([disabled]):not([hidden]),"+"button:not([disabled]):not([hidden])");if(focusableChildren.length>0){this.$.dropdown.focusTarget=focusableChildren[0];this.firstFocus_=focusableChildren[0];this.lastFocus_=focusableChildren[focusableChildren.length-1]}this.$.dropdown.positionTarget=anchor;this.menuOpen=true},toggleMenu:function(anchor,opt_itemData){if(anchor==this.lastAnchor_&&this.menuOpen)this.closeMenu();else this.openMenu(anchor,opt_itemData)},onTabPressed_:function(e){if(!this.firstFocus_||!this.lastFocus_)return;var toFocus;var keyEvent=e.detail.keyboardEvent;if(keyEvent.shiftKey&&keyEvent.target==this.firstFocus_)toFocus=this.lastFocus_;else if(!keyEvent.shiftKey&&keyEvent.target==this.lastFocus_)toFocus=this.firstFocus_;if(!toFocus)return;e.preventDefault();toFocus.focus()},menuOpenChanged_:function(){if(!this.menuOpen){this.itemData=null;this.lastAnchor_=null}},onOverlayCanceled_:function(e){if(e.detail.type=="tap")this.$.dropdown.restoreFocusOnClose=false}});Polymer({is:"paper-icon-button-light","extends":"button",behaviors:[Polymer.PaperRippleBehavior],listeners:{down:"_rippleDown",up:"_rippleUp",focus:"_rippleDown",blur:"_rippleUp"},_rippleDown:function(){this.getRipple().downAction()},_rippleUp:function(){this.getRipple().upAction()},ensureRipple:function(var_args){var lastRipple=this._ripple;Polymer.PaperRippleBehavior.ensureRipple.apply(this,arguments);if(this._ripple&&this._ripple!==lastRipple){this._ripple.center=true;this._ripple.classList.add("circle")}}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'history-synced-device-card', - properties: { - device: String, - lastUpdateTime: String, - tabs: { - type: Array, - value: function() { - return []; - }, - observer: 'updateIcons_' - }, - separatorIndexes: Array, - opened: Boolean, - searchTerm: String, - sessionTag: String - }, - openTab_: function(e) { - var tab = e.model.tab; - var browserService = md_history.BrowserService.getInstance(); - browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.LINK_CLICKED, SyncedTabsHistogram.LIMIT); - browserService.openForeignSessionTab(this.sessionTag, tab.windowId, tab.sessionId, e); - e.preventDefault(); - }, - toggleTabCard: function() { - var histogramValue = this.$.collapse.opened ? SyncedTabsHistogram.COLLAPSE_SESSION : SyncedTabsHistogram.EXPAND_SESSION; - md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, histogramValue, SyncedTabsHistogram.LIMIT); - this.$.collapse.toggle(); - this.$['dropdown-indicator'].icon = this.$.collapse.opened ? 'cr:expand-less' : 'cr:expand-more'; - }, - updateIcons_: function() { - this.async(function() { - var icons = Polymer.dom(this.root).querySelectorAll('.website-icon'); - for (var i = 0; i < this.tabs.length; i++) { - icons[i].style.backgroundImage = cr.icon.getFavicon(this.tabs[i].url); - } - }); - }, - isWindowSeparatorIndex_: function(index, separatorIndexes) { - return this.separatorIndexes.indexOf(index) != -1; - }, - getCollapseIcon_: function(opened) { - return opened ? 'cr:expand-less' : 'cr:expand-more'; - }, - getCollapseTitle_: function(opened) { - return opened ? loadTimeData.getString('collapseSessionButton') : loadTimeData.getString('expandSessionButton'); - }, - onMenuButtonTap_: function(e) { - this.fire('toggle-menu', { - target: Polymer.dom(e).localTarget, - tag: this.sessionTag - }); - e.stopPropagation(); - }, - onLinkRightClick_: function() { - md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.LINK_RIGHT_CLICKED, SyncedTabsHistogram.LIMIT); - } -}); - +Polymer({is:"history-synced-device-card",properties:{device:String,lastUpdateTime:String,tabs:{type:Array,value:function(){return[]},observer:"updateIcons_"},separatorIndexes:Array,opened:Boolean,searchTerm:String,sessionTag:String},openTab_:function(e){var tab=e.model.tab;var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.LINK_CLICKED,SyncedTabsHistogram.LIMIT);browserService.openForeignSessionTab(this.sessionTag,tab.windowId,tab.sessionId,e);e.preventDefault()},toggleTabCard:function(){var histogramValue=this.$.collapse.opened?SyncedTabsHistogram.COLLAPSE_SESSION:SyncedTabsHistogram.EXPAND_SESSION;md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,histogramValue,SyncedTabsHistogram.LIMIT);this.$.collapse.toggle();this.$["dropdown-indicator"].icon=this.$.collapse.opened?"cr:expand-less":"cr:expand-more"},updateIcons_:function(){this.async(function(){var icons=Polymer.dom(this.root).querySelectorAll(".website-icon");for(var i=0;i<this.tabs.length;i++){icons[i].style.backgroundImage=cr.icon.getFavicon(this.tabs[i].url)}})},isWindowSeparatorIndex_:function(index,separatorIndexes){return this.separatorIndexes.indexOf(index)!=-1},getCollapseIcon_:function(opened){return opened?"cr:expand-less":"cr:expand-more"},getCollapseTitle_:function(opened){return opened?loadTimeData.getString("collapseSessionButton"):loadTimeData.getString("expandSessionButton")},onMenuButtonTap_:function(e){this.fire("toggle-menu",{target:Polymer.dom(e).localTarget,tag:this.sessionTag});e.stopPropagation()},onLinkRightClick_:function(){md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.LINK_RIGHT_CLICKED,SyncedTabsHistogram.LIMIT)}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var ForeignDeviceInternal; - -Polymer({ - is: 'history-synced-device-manager', - properties: { - sessionList: { - type: Array, - observer: 'updateSyncedDevices' - }, - searchTerm: { - type: String, - observer: 'searchTermChanged' - }, - syncedDevices_: { - type: Array, - value: function() { - return []; - } - }, - signInState: { - type: Boolean, - observer: 'signInStateChanged_' - }, - guestSession_: { - type: Boolean, - value: loadTimeData.getBoolean('isGuestSession') - }, - fetchingSyncedTabs_: { - type: Boolean, - value: false - }, - hasSeenForeignData_: Boolean - }, - listeners: { - 'toggle-menu': 'onToggleMenu_', - scroll: 'onListScroll_' - }, - attached: function() { - chrome.send('otherDevicesInitialized'); - md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.INITIALIZED, SyncedTabsHistogram.LIMIT); - }, - getContentScrollTarget: function() { - return this; - }, - createInternalDevice_: function(session) { - var tabs = []; - var separatorIndexes = []; - for (var i = 0; i < session.windows.length; i++) { - var windowId = session.windows[i].sessionId; - var newTabs = session.windows[i].tabs; - if (newTabs.length == 0) continue; - newTabs.forEach(function(tab) { - tab.windowId = windowId; - }); - var windowAdded = false; - if (!this.searchTerm) { - tabs = tabs.concat(newTabs); - windowAdded = true; - } else { - var searchText = this.searchTerm.toLowerCase(); - for (var j = 0; j < newTabs.length; j++) { - var tab = newTabs[j]; - if (tab.title.toLowerCase().indexOf(searchText) != -1) { - tabs.push(tab); - windowAdded = true; - } - } - } - if (windowAdded && i != session.windows.length - 1) separatorIndexes.push(tabs.length - 1); - } - return { - device: session.name, - lastUpdateTime: '– ' + session.modifiedTime, - opened: true, - separatorIndexes: separatorIndexes, - timestamp: session.timestamp, - tabs: tabs, - tag: session.tag - }; - }, - onSignInTap_: function() { - chrome.send('startSignInFlow'); - }, - onListScroll_: function() { - var menu = this.$.menu.getIfExists(); - if (menu) menu.closeMenu(); - }, - onToggleMenu_: function(e) { - var menu = this.$.menu.get(); - menu.toggleMenu(e.detail.target, e.detail.tag); - if (menu.menuOpen) { - md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.SHOW_SESSION_MENU, SyncedTabsHistogram.LIMIT); - } - }, - onOpenAllTap_: function() { - var menu = assert(this.$.menu.getIfExists()); - var browserService = md_history.BrowserService.getInstance(); - browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.OPEN_ALL, SyncedTabsHistogram.LIMIT); - browserService.openForeignSessionAllTabs(menu.itemData); - menu.closeMenu(); - }, - onDeleteSessionTap_: function() { - var menu = assert(this.$.menu.getIfExists()); - var browserService = md_history.BrowserService.getInstance(); - browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.HIDE_FOR_NOW, SyncedTabsHistogram.LIMIT); - browserService.deleteForeignSession(menu.itemData); - menu.closeMenu(); - }, - clearDisplayedSyncedDevices_: function() { - this.syncedDevices_ = []; - }, - showNoSyncedMessage: function(signInState, syncedDevicesLength, guestSession) { - if (guestSession) return true; - return signInState && syncedDevicesLength == 0; - }, - showSignInGuide: function(signInState, guestSession) { - var show = !signInState && !guestSession; - if (show) { - md_history.BrowserService.getInstance().recordAction('Signin_Impression_FromRecentTabs'); - } - return show; - }, - noSyncedTabsMessage: function() { - var stringName = this.fetchingSyncedTabs_ ? 'loading' : 'noSyncedResults'; - if (this.searchTerm !== '') stringName = 'noSearchResults'; - return loadTimeData.getString(stringName); - }, - updateSyncedDevices: function(sessionList) { - this.fetchingSyncedTabs_ = false; - if (!sessionList) return; - if (sessionList.length > 0 && !this.hasSeenForeignData_) { - this.hasSeenForeignData_ = true; - md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.HAS_FOREIGN_DATA, SyncedTabsHistogram.LIMIT); - } - var updateCount = Math.min(sessionList.length, this.syncedDevices_.length); - for (var i = 0; i < updateCount; i++) { - var oldDevice = this.syncedDevices_[i]; - if (oldDevice.tag != sessionList[i].tag || oldDevice.timestamp != sessionList[i].timestamp) { - var device = this.createInternalDevice_(sessionList[i]); - if (device.tabs.length != 0) this.splice('syncedDevices_', i, 1, device); - } - } - if (sessionList.length >= this.syncedDevices_.length) { - for (var i = updateCount; i < sessionList.length; i++) { - var device = this.createInternalDevice_(sessionList[i]); - if (device.tabs.length != 0) this.push('syncedDevices_', device); - } - } else { - this.splice('syncedDevices_', updateCount, this.syncedDevices_.length - updateCount); - } - }, - tabSyncDisabled: function() { - this.fetchingSyncedTabs_ = false; - this.clearDisplayedSyncedDevices_(); - }, - signInStateChanged_: function() { - this.fire('history-view-changed'); - if (!this.signInState) { - this.clearDisplayedSyncedDevices_(); - return; - } - this.fetchingSyncedTabs_ = true; - }, - searchTermChanged: function(searchTerm) { - this.clearDisplayedSyncedDevices_(); - this.updateSyncedDevices(this.sessionList); - } -}); - +var ForeignDeviceInternal;Polymer({is:"history-synced-device-manager",properties:{sessionList:{type:Array,observer:"updateSyncedDevices"},searchTerm:{type:String,observer:"searchTermChanged"},syncedDevices_:{type:Array,value:function(){return[]}},signInState:{type:Boolean,observer:"signInStateChanged_"},guestSession_:{type:Boolean,value:loadTimeData.getBoolean("isGuestSession")},fetchingSyncedTabs_:{type:Boolean,value:false},hasSeenForeignData_:Boolean},listeners:{"toggle-menu":"onToggleMenu_",scroll:"onListScroll_"},attached:function(){chrome.send("otherDevicesInitialized");md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.INITIALIZED,SyncedTabsHistogram.LIMIT)},getContentScrollTarget:function(){return this},createInternalDevice_:function(session){var tabs=[];var separatorIndexes=[];for(var i=0;i<session.windows.length;i++){var windowId=session.windows[i].sessionId;var newTabs=session.windows[i].tabs;if(newTabs.length==0)continue;newTabs.forEach(function(tab){tab.windowId=windowId});var windowAdded=false;if(!this.searchTerm){tabs=tabs.concat(newTabs);windowAdded=true}else{var searchText=this.searchTerm.toLowerCase();for(var j=0;j<newTabs.length;j++){var tab=newTabs[j];if(tab.title.toLowerCase().indexOf(searchText)!=-1){tabs.push(tab);windowAdded=true}}}if(windowAdded&&i!=session.windows.length-1)separatorIndexes.push(tabs.length-1)}return{device:session.name,lastUpdateTime:"– "+session.modifiedTime,opened:true,separatorIndexes:separatorIndexes,timestamp:session.timestamp,tabs:tabs,tag:session.tag}},onSignInTap_:function(){chrome.send("startSignInFlow")},onListScroll_:function(){var menu=this.$.menu.getIfExists();if(menu)menu.closeMenu()},onToggleMenu_:function(e){var menu=this.$.menu.get();menu.toggleMenu(e.detail.target,e.detail.tag);if(menu.menuOpen){md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.SHOW_SESSION_MENU,SyncedTabsHistogram.LIMIT)}},onOpenAllTap_:function(){var menu=assert(this.$.menu.getIfExists());var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.OPEN_ALL,SyncedTabsHistogram.LIMIT);browserService.openForeignSessionAllTabs(menu.itemData);menu.closeMenu()},onDeleteSessionTap_:function(){var menu=assert(this.$.menu.getIfExists());var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.HIDE_FOR_NOW,SyncedTabsHistogram.LIMIT);browserService.deleteForeignSession(menu.itemData);menu.closeMenu()},clearDisplayedSyncedDevices_:function(){this.syncedDevices_=[]},showNoSyncedMessage:function(signInState,syncedDevicesLength,guestSession){if(guestSession)return true;return signInState&&syncedDevicesLength==0},showSignInGuide:function(signInState,guestSession){var show=!signInState&&!guestSession;if(show){md_history.BrowserService.getInstance().recordAction("Signin_Impression_FromRecentTabs")}return show},noSyncedTabsMessage:function(){var stringName=this.fetchingSyncedTabs_?"loading":"noSyncedResults";if(this.searchTerm!=="")stringName="noSearchResults";return loadTimeData.getString(stringName)},updateSyncedDevices:function(sessionList){this.fetchingSyncedTabs_=false;if(!sessionList)return;if(sessionList.length>0&&!this.hasSeenForeignData_){this.hasSeenForeignData_=true;md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME,SyncedTabsHistogram.HAS_FOREIGN_DATA,SyncedTabsHistogram.LIMIT)}var updateCount=Math.min(sessionList.length,this.syncedDevices_.length);for(var i=0;i<updateCount;i++){var oldDevice=this.syncedDevices_[i];if(oldDevice.tag!=sessionList[i].tag||oldDevice.timestamp!=sessionList[i].timestamp){var device=this.createInternalDevice_(sessionList[i]);if(device.tabs.length!=0)this.splice("syncedDevices_",i,1,device)}}if(sessionList.length>=this.syncedDevices_.length){for(var i=updateCount;i<sessionList.length;i++){var device=this.createInternalDevice_(sessionList[i]);if(device.tabs.length!=0)this.push("syncedDevices_",device)}}else{this.splice("syncedDevices_",updateCount,this.syncedDevices_.length-updateCount)}},tabSyncDisabled:function(){this.fetchingSyncedTabs_=false;this.clearDisplayedSyncedDevices_()},signInStateChanged_:function(){this.fire("history-view-changed");if(!this.signInState){this.clearDisplayedSyncedDevices_();return}this.fetchingSyncedTabs_=true},searchTermChanged:function(searchTerm){this.clearDisplayedSyncedDevices_();this.updateSyncedDevices(this.sessionList)}}); // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Polymer({ - is: 'cr-dialog', - "extends": 'dialog', - created: function() { - window.addEventListener('popstate', function() { - if (this.open) this.cancel(); - }.bind(this)); - }, - cancel: function() { - this.fire('cancel'); - HTMLDialogElement.prototype.close.call(this, ''); - }, - close: function(opt_returnValue) { - HTMLDialogElement.prototype.close.call(this, 'success'); - }, - getCloseButton: function() { - return this.$.close; - } -}); - -Polymer({ - is: 'app-drawer', - properties: { - opened: { - type: Boolean, - value: false, - notify: true, - reflectToAttribute: true - }, - persistent: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - align: { - type: String, - value: 'left' - }, - position: { - type: String, - readOnly: true, - value: 'left', - reflectToAttribute: true - }, - swipeOpen: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - noFocusTrap: { - type: Boolean, - value: false - } - }, - observers: [ 'resetLayout(position)', '_resetPosition(align, isAttached)' ], - _translateOffset: 0, - _trackDetails: null, - _drawerState: 0, - _boundEscKeydownHandler: null, - _firstTabStop: null, - _lastTabStop: null, - ready: function() { - this.setScrollDirection('y'); - this._setTransitionDuration('0s'); - }, - attached: function() { - Polymer.RenderStatus.afterNextRender(this, function() { - this._setTransitionDuration(''); - this._boundEscKeydownHandler = this._escKeydownHandler.bind(this); - this._resetDrawerState(); - this.listen(this, 'track', '_track'); - this.addEventListener('transitionend', this._transitionend.bind(this)); - this.addEventListener('keydown', this._tabKeydownHandler.bind(this)); - }); - }, - detached: function() { - document.removeEventListener('keydown', this._boundEscKeydownHandler); - }, - open: function() { - this.opened = true; - }, - close: function() { - this.opened = false; - }, - toggle: function() { - this.opened = !this.opened; - }, - getWidth: function() { - return this.$.contentContainer.offsetWidth; - }, - resetLayout: function() { - this.debounce('_resetLayout', function() { - this.fire('app-drawer-reset-layout'); - }, 1); - }, - _isRTL: function() { - return window.getComputedStyle(this).direction === 'rtl'; - }, - _resetPosition: function() { - switch (this.align) { - case 'start': - this._setPosition(this._isRTL() ? 'right' : 'left'); - return; - - case 'end': - this._setPosition(this._isRTL() ? 'left' : 'right'); - return; - } - this._setPosition(this.align); - }, - _escKeydownHandler: function(event) { - var ESC_KEYCODE = 27; - if (event.keyCode === ESC_KEYCODE) { - event.preventDefault(); - this.close(); - } - }, - _track: function(event) { - if (this.persistent) { - return; - } - event.preventDefault(); - switch (event.detail.state) { - case 'start': - this._trackStart(event); - break; - - case 'track': - this._trackMove(event); - break; - - case 'end': - this._trackEnd(event); - break; - } - }, - _trackStart: function(event) { - this._drawerState = this._DRAWER_STATE.TRACKING; - this._setTransitionDuration('0s'); - this.style.visibility = 'visible'; - var rect = this.$.contentContainer.getBoundingClientRect(); - if (this.position === 'left') { - this._translateOffset = rect.left; - } else { - this._translateOffset = rect.right - window.innerWidth; - } - this._trackDetails = []; - }, - _trackMove: function(event) { - this._translateDrawer(event.detail.dx + this._translateOffset); - this._trackDetails.push({ - dx: event.detail.dx, - timeStamp: Date.now() - }); - }, - _trackEnd: function(event) { - var x = event.detail.dx + this._translateOffset; - var drawerWidth = this.getWidth(); - var isPositionLeft = this.position === 'left'; - var isInEndState = isPositionLeft ? x >= 0 || x <= -drawerWidth : x <= 0 || x >= drawerWidth; - if (!isInEndState) { - var trackDetails = this._trackDetails; - this._trackDetails = null; - this._flingDrawer(event, trackDetails); - if (this._drawerState === this._DRAWER_STATE.FLINGING) { - return; - } - } - var halfWidth = drawerWidth / 2; - if (event.detail.dx < -halfWidth) { - this.opened = this.position === 'right'; - } else if (event.detail.dx > halfWidth) { - this.opened = this.position === 'left'; - } - if (isInEndState) { - this._resetDrawerState(); - } - this._setTransitionDuration(''); - this._resetDrawerTranslate(); - this.style.visibility = ''; - }, - _calculateVelocity: function(event, trackDetails) { - var now = Date.now(); - var timeLowerBound = now - 100; - var trackDetail; - var min = 0; - var max = trackDetails.length - 1; - while (min <= max) { - var mid = min + max >> 1; - var d = trackDetails[mid]; - if (d.timeStamp >= timeLowerBound) { - trackDetail = d; - max = mid - 1; - } else { - min = mid + 1; - } - } - if (trackDetail) { - var dx = event.detail.dx - trackDetail.dx; - var dt = now - trackDetail.timeStamp || 1; - return dx / dt; - } - return 0; - }, - _flingDrawer: function(event, trackDetails) { - var velocity = this._calculateVelocity(event, trackDetails); - if (Math.abs(velocity) < this._MIN_FLING_THRESHOLD) { - return; - } - this._drawerState = this._DRAWER_STATE.FLINGING; - var x = event.detail.dx + this._translateOffset; - var drawerWidth = this.getWidth(); - var isPositionLeft = this.position === 'left'; - var isVelocityPositive = velocity > 0; - var isClosingLeft = !isVelocityPositive && isPositionLeft; - var isClosingRight = isVelocityPositive && !isPositionLeft; - var dx; - if (isClosingLeft) { - dx = -(x + drawerWidth); - } else if (isClosingRight) { - dx = drawerWidth - x; - } else { - dx = -x; - } - if (isVelocityPositive) { - velocity = Math.max(velocity, this._MIN_TRANSITION_VELOCITY); - this.opened = this.position === 'left'; - } else { - velocity = Math.min(velocity, -this._MIN_TRANSITION_VELOCITY); - this.opened = this.position === 'right'; - } - this._setTransitionDuration(this._FLING_INITIAL_SLOPE * dx / velocity + 'ms'); - this._setTransitionTimingFunction(this._FLING_TIMING_FUNCTION); - this._resetDrawerTranslate(); - }, - _transitionend: function(event) { - var target = Polymer.dom(event).rootTarget; - if (target === this.$.contentContainer || target === this.$.scrim) { - if (this._drawerState === this._DRAWER_STATE.FLINGING) { - this._setTransitionDuration(''); - this._setTransitionTimingFunction(''); - this.style.visibility = ''; - } - this._resetDrawerState(); - } - }, - _setTransitionDuration: function(duration) { - this.$.contentContainer.style.transitionDuration = duration; - this.$.scrim.style.transitionDuration = duration; - }, - _setTransitionTimingFunction: function(timingFunction) { - this.$.contentContainer.style.transitionTimingFunction = timingFunction; - this.$.scrim.style.transitionTimingFunction = timingFunction; - }, - _translateDrawer: function(x) { - var drawerWidth = this.getWidth(); - if (this.position === 'left') { - x = Math.max(-drawerWidth, Math.min(x, 0)); - this.$.scrim.style.opacity = 1 + x / drawerWidth; - } else { - x = Math.max(0, Math.min(x, drawerWidth)); - this.$.scrim.style.opacity = 1 - x / drawerWidth; - } - this.translate3d(x + 'px', '0', '0', this.$.contentContainer); - }, - _resetDrawerTranslate: function() { - this.$.scrim.style.opacity = ''; - this.transform('', this.$.contentContainer); - }, - _resetDrawerState: function() { - var oldState = this._drawerState; - if (this.opened) { - this._drawerState = this.persistent ? this._DRAWER_STATE.OPENED_PERSISTENT : this._DRAWER_STATE.OPENED; - } else { - this._drawerState = this._DRAWER_STATE.CLOSED; - } - if (oldState !== this._drawerState) { - if (this._drawerState === this._DRAWER_STATE.OPENED) { - this._setKeyboardFocusTrap(); - document.addEventListener('keydown', this._boundEscKeydownHandler); - document.body.style.overflow = 'hidden'; - } else { - document.removeEventListener('keydown', this._boundEscKeydownHandler); - document.body.style.overflow = ''; - } - if (oldState !== this._DRAWER_STATE.INIT) { - this.fire('app-drawer-transitioned'); - } - } - }, - _setKeyboardFocusTrap: function() { - if (this.noFocusTrap) { - return; - } - var focusableElementsSelector = [ 'a[href]:not([tabindex="-1"])', 'area[href]:not([tabindex="-1"])', 'input:not([disabled]):not([tabindex="-1"])', 'select:not([disabled]):not([tabindex="-1"])', 'textarea:not([disabled]):not([tabindex="-1"])', 'button:not([disabled]):not([tabindex="-1"])', 'iframe:not([tabindex="-1"])', '[tabindex]:not([tabindex="-1"])', '[contentEditable=true]:not([tabindex="-1"])' ].join(','); - var focusableElements = Polymer.dom(this).querySelectorAll(focusableElementsSelector); - if (focusableElements.length > 0) { - this._firstTabStop = focusableElements[0]; - this._lastTabStop = focusableElements[focusableElements.length - 1]; - } else { - this._firstTabStop = null; - this._lastTabStop = null; - } - var tabindex = this.getAttribute('tabindex'); - if (tabindex && parseInt(tabindex, 10) > -1) { - this.focus(); - } else if (this._firstTabStop) { - this._firstTabStop.focus(); - } - }, - _tabKeydownHandler: function(event) { - if (this.noFocusTrap) { - return; - } - var TAB_KEYCODE = 9; - if (this._drawerState === this._DRAWER_STATE.OPENED && event.keyCode === TAB_KEYCODE) { - if (event.shiftKey) { - if (this._firstTabStop && Polymer.dom(event).localTarget === this._firstTabStop) { - event.preventDefault(); - this._lastTabStop.focus(); - } - } else { - if (this._lastTabStop && Polymer.dom(event).localTarget === this._lastTabStop) { - event.preventDefault(); - this._firstTabStop.focus(); - } - } - } - }, - _MIN_FLING_THRESHOLD: .2, - _MIN_TRANSITION_VELOCITY: 1.2, - _FLING_TIMING_FUNCTION: 'cubic-bezier(0.667, 1, 0.667, 1)', - _FLING_INITIAL_SLOPE: 1.5, - _DRAWER_STATE: { - INIT: 0, - OPENED: 1, - OPENED_PERSISTENT: 2, - CLOSED: 3, - TRACKING: 4, - FLINGING: 5 - } -}); - -Polymer.IronFormElementBehavior = { - properties: { - name: { - type: String - }, - value: { - notify: true, - type: String - }, - required: { - type: Boolean, - value: false - }, - _parentForm: { - type: Object - } - }, - attached: function() { - this.fire('iron-form-element-register'); - }, - detached: function() { - if (this._parentForm) { - this._parentForm.fire('iron-form-element-unregister', { - target: this - }); - } - } -}; - -Polymer.IronCheckedElementBehaviorImpl = { - properties: { - checked: { - type: Boolean, - value: false, - reflectToAttribute: true, - notify: true, - observer: '_checkedChanged' - }, - toggles: { - type: Boolean, - value: true, - reflectToAttribute: true - }, - value: { - type: String, - value: 'on', - observer: '_valueChanged' - } - }, - observers: [ '_requiredChanged(required)' ], - created: function() { - this._hasIronCheckedElementBehavior = true; - }, - _getValidity: function(_value) { - return this.disabled || !this.required || this.checked; - }, - _requiredChanged: function() { - if (this.required) { - this.setAttribute('aria-required', 'true'); - } else { - this.removeAttribute('aria-required'); - } - }, - _checkedChanged: function() { - this.active = this.checked; - this.fire('iron-change'); - }, - _valueChanged: function() { - if (this.value === undefined || this.value === null) { - this.value = 'on'; - } - } -}; - -Polymer.IronCheckedElementBehavior = [ Polymer.IronFormElementBehavior, Polymer.IronValidatableBehavior, Polymer.IronCheckedElementBehaviorImpl ]; - -Polymer.PaperCheckedElementBehaviorImpl = { - _checkedChanged: function() { - Polymer.IronCheckedElementBehaviorImpl._checkedChanged.call(this); - if (this.hasRipple()) { - if (this.checked) { - this._ripple.setAttribute('checked', ''); - } else { - this._ripple.removeAttribute('checked'); - } - } - }, - _buttonStateChanged: function() { - Polymer.PaperRippleBehavior._buttonStateChanged.call(this); - if (this.disabled) { - return; - } - if (this.isAttached) { - this.checked = this.active; - } - } -}; - -Polymer.PaperCheckedElementBehavior = [ Polymer.PaperInkyFocusBehavior, Polymer.IronCheckedElementBehavior, Polymer.PaperCheckedElementBehaviorImpl ]; - -Polymer({ - is: 'paper-checkbox', - behaviors: [ Polymer.PaperCheckedElementBehavior ], - hostAttributes: { - role: 'checkbox', - 'aria-checked': false, - tabindex: 0 - }, - properties: { - ariaActiveAttribute: { - type: String, - value: 'aria-checked' - } - }, - attached: function() { - var inkSize = this.getComputedStyleValue('--calculated-paper-checkbox-ink-size'); - if (inkSize === '-1px') { - var checkboxSize = parseFloat(this.getComputedStyleValue('--calculated-paper-checkbox-size')); - var defaultInkSize = Math.floor(8 / 3 * checkboxSize); - if (defaultInkSize % 2 !== checkboxSize % 2) { - defaultInkSize++; - } - this.customStyle['--paper-checkbox-ink-size'] = defaultInkSize + 'px'; - this.updateStyles(); - } - }, - _computeCheckboxClass: function(checked, invalid) { - var className = ''; - if (checked) { - className += 'checked '; - } - if (invalid) { - className += 'invalid'; - } - return className; - }, - _computeCheckmarkClass: function(checked) { - return checked ? '' : 'hidden'; - }, - _createRipple: function() { - this._rippleContainer = this.$.checkboxContainer; - return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this); - } -}); - -Polymer({ - is: 'paper-tab', - behaviors: [ Polymer.IronControlState, Polymer.IronButtonState, Polymer.PaperRippleBehavior ], - properties: { - link: { - type: Boolean, - value: false, - reflectToAttribute: true - } - }, - hostAttributes: { - role: 'tab' - }, - listeners: { - down: '_updateNoink', - tap: '_onTap' - }, - attached: function() { - this._updateNoink(); - }, - get _parentNoink() { - var parent = Polymer.dom(this).parentNode; - return !!parent && !!parent.noink; - }, - _updateNoink: function() { - this.noink = !!this.noink || !!this._parentNoink; - }, - _onTap: function(event) { - if (this.link) { - var anchor = this.queryEffectiveChildren('a'); - if (!anchor) { - return; - } - if (event.target === anchor) { - return; - } - anchor.click(); - } - } -}); - -Polymer.IronMenuBehaviorImpl = { - properties: { - focusedItem: { - observer: '_focusedItemChanged', - readOnly: true, - type: Object - }, - attrForItemTitle: { - type: String - } - }, - hostAttributes: { - role: 'menu', - tabindex: '0' - }, - observers: [ '_updateMultiselectable(multi)' ], - listeners: { - focus: '_onFocus', - keydown: '_onKeydown', - 'iron-items-changed': '_onIronItemsChanged' - }, - keyBindings: { - up: '_onUpKey', - down: '_onDownKey', - esc: '_onEscKey', - 'shift+tab:keydown': '_onShiftTabDown' - }, - attached: function() { - this._resetTabindices(); - }, - select: function(value) { - if (this._defaultFocusAsync) { - this.cancelAsync(this._defaultFocusAsync); - this._defaultFocusAsync = null; - } - var item = this._valueToItem(value); - if (item && item.hasAttribute('disabled')) return; - this._setFocusedItem(item); - Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments); - }, - _resetTabindices: function() { - var selectedItem = this.multi ? this.selectedItems && this.selectedItems[0] : this.selectedItem; - this.items.forEach(function(item) { - item.setAttribute('tabindex', item === selectedItem ? '0' : '-1'); - }, this); - }, - _updateMultiselectable: function(multi) { - if (multi) { - this.setAttribute('aria-multiselectable', 'true'); - } else { - this.removeAttribute('aria-multiselectable'); - } - }, - _focusWithKeyboardEvent: function(event) { - for (var i = 0, item; item = this.items[i]; i++) { - var attr = this.attrForItemTitle || 'textContent'; - var title = item[attr] || item.getAttribute(attr); - if (!item.hasAttribute('disabled') && title && title.trim().charAt(0).toLowerCase() === String.fromCharCode(event.keyCode).toLowerCase()) { - this._setFocusedItem(item); - break; - } - } - }, - _focusPrevious: function() { - var length = this.items.length; - var curFocusIndex = Number(this.indexOf(this.focusedItem)); - for (var i = 1; i < length + 1; i++) { - var item = this.items[(curFocusIndex - i + length) % length]; - if (!item.hasAttribute('disabled')) { - var owner = Polymer.dom(item).getOwnerRoot() || document; - this._setFocusedItem(item); - if (Polymer.dom(owner).activeElement == item) { - return; - } - } - } - }, - _focusNext: function() { - var length = this.items.length; - var curFocusIndex = Number(this.indexOf(this.focusedItem)); - for (var i = 1; i < length + 1; i++) { - var item = this.items[(curFocusIndex + i) % length]; - if (!item.hasAttribute('disabled')) { - var owner = Polymer.dom(item).getOwnerRoot() || document; - this._setFocusedItem(item); - if (Polymer.dom(owner).activeElement == item) { - return; - } - } - } - }, - _applySelection: function(item, isSelected) { - if (isSelected) { - item.setAttribute('aria-selected', 'true'); - } else { - item.removeAttribute('aria-selected'); - } - Polymer.IronSelectableBehavior._applySelection.apply(this, arguments); - }, - _focusedItemChanged: function(focusedItem, old) { - old && old.setAttribute('tabindex', '-1'); - if (focusedItem) { - focusedItem.setAttribute('tabindex', '0'); - focusedItem.focus(); - } - }, - _onIronItemsChanged: function(event) { - if (event.detail.addedNodes.length) { - this._resetTabindices(); - } - }, - _onShiftTabDown: function(event) { - var oldTabIndex = this.getAttribute('tabindex'); - Polymer.IronMenuBehaviorImpl._shiftTabPressed = true; - this._setFocusedItem(null); - this.setAttribute('tabindex', '-1'); - this.async(function() { - this.setAttribute('tabindex', oldTabIndex); - Polymer.IronMenuBehaviorImpl._shiftTabPressed = false; - }, 1); - }, - _onFocus: function(event) { - if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) { - return; - } - var rootTarget = Polymer.dom(event).rootTarget; - if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !this.isLightDescendant(rootTarget)) { - return; - } - this._defaultFocusAsync = this.async(function() { - var selectedItem = this.multi ? this.selectedItems && this.selectedItems[0] : this.selectedItem; - this._setFocusedItem(null); - if (selectedItem) { - this._setFocusedItem(selectedItem); - } else if (this.items[0]) { - this._focusNext(); - } - }); - }, - _onUpKey: function(event) { - this._focusPrevious(); - event.detail.keyboardEvent.preventDefault(); - }, - _onDownKey: function(event) { - this._focusNext(); - event.detail.keyboardEvent.preventDefault(); - }, - _onEscKey: function(event) { - this.focusedItem.blur(); - }, - _onKeydown: function(event) { - if (!this.keyboardEventMatchesKeys(event, 'up down esc')) { - this._focusWithKeyboardEvent(event); - } - event.stopPropagation(); - }, - _activateHandler: function(event) { - Polymer.IronSelectableBehavior._activateHandler.call(this, event); - event.stopPropagation(); - } -}; - -Polymer.IronMenuBehaviorImpl._shiftTabPressed = false; - -Polymer.IronMenuBehavior = [ Polymer.IronMultiSelectableBehavior, Polymer.IronA11yKeysBehavior, Polymer.IronMenuBehaviorImpl ]; - -Polymer.IronMenubarBehaviorImpl = { - hostAttributes: { - role: 'menubar' - }, - keyBindings: { - left: '_onLeftKey', - right: '_onRightKey' - }, - _onUpKey: function(event) { - this.focusedItem.click(); - event.detail.keyboardEvent.preventDefault(); - }, - _onDownKey: function(event) { - this.focusedItem.click(); - event.detail.keyboardEvent.preventDefault(); - }, - get _isRTL() { - return window.getComputedStyle(this)['direction'] === 'rtl'; - }, - _onLeftKey: function(event) { - if (this._isRTL) { - this._focusNext(); - } else { - this._focusPrevious(); - } - event.detail.keyboardEvent.preventDefault(); - }, - _onRightKey: function(event) { - if (this._isRTL) { - this._focusPrevious(); - } else { - this._focusNext(); - } - event.detail.keyboardEvent.preventDefault(); - }, - _onKeydown: function(event) { - if (this.keyboardEventMatchesKeys(event, 'up down left right esc')) { - return; - } - this._focusWithKeyboardEvent(event); - } -}; - -Polymer.IronMenubarBehavior = [ Polymer.IronMenuBehavior, Polymer.IronMenubarBehaviorImpl ]; - -Polymer({ - is: 'paper-tabs', - behaviors: [ Polymer.IronResizableBehavior, Polymer.IronMenubarBehavior ], - properties: { - noink: { - type: Boolean, - value: false, - observer: '_noinkChanged' - }, - noBar: { - type: Boolean, - value: false - }, - noSlide: { - type: Boolean, - value: false - }, - scrollable: { - type: Boolean, - value: false - }, - fitContainer: { - type: Boolean, - value: false - }, - disableDrag: { - type: Boolean, - value: false - }, - hideScrollButtons: { - type: Boolean, - value: false - }, - alignBottom: { - type: Boolean, - value: false - }, - selectable: { - type: String, - value: 'paper-tab' - }, - autoselect: { - type: Boolean, - value: false - }, - autoselectDelay: { - type: Number, - value: 0 - }, - _step: { - type: Number, - value: 10 - }, - _holdDelay: { - type: Number, - value: 1 - }, - _leftHidden: { - type: Boolean, - value: false - }, - _rightHidden: { - type: Boolean, - value: false - }, - _previousTab: { - type: Object - } - }, - hostAttributes: { - role: 'tablist' - }, - listeners: { - 'iron-resize': '_onTabSizingChanged', - 'iron-items-changed': '_onTabSizingChanged', - 'iron-select': '_onIronSelect', - 'iron-deselect': '_onIronDeselect' - }, - keyBindings: { - 'left:keyup right:keyup': '_onArrowKeyup' - }, - created: function() { - this._holdJob = null; - this._pendingActivationItem = undefined; - this._pendingActivationTimeout = undefined; - this._bindDelayedActivationHandler = this._delayedActivationHandler.bind(this); - this.addEventListener('blur', this._onBlurCapture.bind(this), true); - }, - ready: function() { - this.setScrollDirection('y', this.$.tabsContainer); - }, - detached: function() { - this._cancelPendingActivation(); - }, - _noinkChanged: function(noink) { - var childTabs = Polymer.dom(this).querySelectorAll('paper-tab'); - childTabs.forEach(noink ? this._setNoinkAttribute : this._removeNoinkAttribute); - }, - _setNoinkAttribute: function(element) { - element.setAttribute('noink', ''); - }, - _removeNoinkAttribute: function(element) { - element.removeAttribute('noink'); - }, - _computeScrollButtonClass: function(hideThisButton, scrollable, hideScrollButtons) { - if (!scrollable || hideScrollButtons) { - return 'hidden'; - } - if (hideThisButton) { - return 'not-visible'; - } - return ''; - }, - _computeTabsContentClass: function(scrollable, fitContainer) { - return scrollable ? 'scrollable' + (fitContainer ? ' fit-container' : '') : ' fit-container'; - }, - _computeSelectionBarClass: function(noBar, alignBottom) { - if (noBar) { - return 'hidden'; - } else if (alignBottom) { - return 'align-bottom'; - } - return ''; - }, - _onTabSizingChanged: function() { - this.debounce('_onTabSizingChanged', function() { - this._scroll(); - this._tabChanged(this.selectedItem); - }, 10); - }, - _onIronSelect: function(event) { - this._tabChanged(event.detail.item, this._previousTab); - this._previousTab = event.detail.item; - this.cancelDebouncer('tab-changed'); - }, - _onIronDeselect: function(event) { - this.debounce('tab-changed', function() { - this._tabChanged(null, this._previousTab); - this._previousTab = null; - }, 1); - }, - _activateHandler: function() { - this._cancelPendingActivation(); - Polymer.IronMenuBehaviorImpl._activateHandler.apply(this, arguments); - }, - _scheduleActivation: function(item, delay) { - this._pendingActivationItem = item; - this._pendingActivationTimeout = this.async(this._bindDelayedActivationHandler, delay); - }, - _delayedActivationHandler: function() { - var item = this._pendingActivationItem; - this._pendingActivationItem = undefined; - this._pendingActivationTimeout = undefined; - item.fire(this.activateEvent, null, { - bubbles: true, - cancelable: true - }); - }, - _cancelPendingActivation: function() { - if (this._pendingActivationTimeout !== undefined) { - this.cancelAsync(this._pendingActivationTimeout); - this._pendingActivationItem = undefined; - this._pendingActivationTimeout = undefined; - } - }, - _onArrowKeyup: function(event) { - if (this.autoselect) { - this._scheduleActivation(this.focusedItem, this.autoselectDelay); - } - }, - _onBlurCapture: function(event) { - if (event.target === this._pendingActivationItem) { - this._cancelPendingActivation(); - } - }, - get _tabContainerScrollSize() { - return Math.max(0, this.$.tabsContainer.scrollWidth - this.$.tabsContainer.offsetWidth); - }, - _scroll: function(e, detail) { - if (!this.scrollable) { - return; - } - var ddx = detail && -detail.ddx || 0; - this._affectScroll(ddx); - }, - _down: function(e) { - this.async(function() { - if (this._defaultFocusAsync) { - this.cancelAsync(this._defaultFocusAsync); - this._defaultFocusAsync = null; - } - }, 1); - }, - _affectScroll: function(dx) { - this.$.tabsContainer.scrollLeft += dx; - var scrollLeft = this.$.tabsContainer.scrollLeft; - this._leftHidden = scrollLeft === 0; - this._rightHidden = scrollLeft === this._tabContainerScrollSize; - }, - _onLeftScrollButtonDown: function() { - this._scrollToLeft(); - this._holdJob = setInterval(this._scrollToLeft.bind(this), this._holdDelay); - }, - _onRightScrollButtonDown: function() { - this._scrollToRight(); - this._holdJob = setInterval(this._scrollToRight.bind(this), this._holdDelay); - }, - _onScrollButtonUp: function() { - clearInterval(this._holdJob); - this._holdJob = null; - }, - _scrollToLeft: function() { - this._affectScroll(-this._step); - }, - _scrollToRight: function() { - this._affectScroll(this._step); - }, - _tabChanged: function(tab, old) { - if (!tab) { - this.$.selectionBar.classList.remove('expand'); - this.$.selectionBar.classList.remove('contract'); - this._positionBar(0, 0); - return; - } - var r = this.$.tabsContent.getBoundingClientRect(); - var w = r.width; - var tabRect = tab.getBoundingClientRect(); - var tabOffsetLeft = tabRect.left - r.left; - this._pos = { - width: this._calcPercent(tabRect.width, w), - left: this._calcPercent(tabOffsetLeft, w) - }; - if (this.noSlide || old == null) { - this.$.selectionBar.classList.remove('expand'); - this.$.selectionBar.classList.remove('contract'); - this._positionBar(this._pos.width, this._pos.left); - return; - } - var oldRect = old.getBoundingClientRect(); - var oldIndex = this.items.indexOf(old); - var index = this.items.indexOf(tab); - var m = 5; - this.$.selectionBar.classList.add('expand'); - var moveRight = oldIndex < index; - var isRTL = this._isRTL; - if (isRTL) { - moveRight = !moveRight; - } - if (moveRight) { - this._positionBar(this._calcPercent(tabRect.left + tabRect.width - oldRect.left, w) - m, this._left); - } else { - this._positionBar(this._calcPercent(oldRect.left + oldRect.width - tabRect.left, w) - m, this._calcPercent(tabOffsetLeft, w) + m); - } - if (this.scrollable) { - this._scrollToSelectedIfNeeded(tabRect.width, tabOffsetLeft); - } - }, - _scrollToSelectedIfNeeded: function(tabWidth, tabOffsetLeft) { - var l = tabOffsetLeft - this.$.tabsContainer.scrollLeft; - if (l < 0) { - this.$.tabsContainer.scrollLeft += l; - } else { - l += tabWidth - this.$.tabsContainer.offsetWidth; - if (l > 0) { - this.$.tabsContainer.scrollLeft += l; - } - } - }, - _calcPercent: function(w, w0) { - return 100 * w / w0; - }, - _positionBar: function(width, left) { - width = width || 0; - left = left || 0; - this._width = width; - this._left = left; - this.transform('translateX(' + left + '%) scaleX(' + width / 100 + ')', this.$.selectionBar); - }, - _onBarTransitionEnd: function(e) { - var cl = this.$.selectionBar.classList; - if (cl.contains('expand')) { - cl.remove('expand'); - cl.add('contract'); - this._positionBar(this._pos.width, this._pos.left); - } else if (cl.contains('contract')) { - cl.remove('contract'); - } - } -}); \ No newline at end of file +Polymer({is:"cr-dialog","extends":"dialog",created:function(){window.addEventListener("popstate",function(){if(this.open)this.cancel()}.bind(this))},cancel:function(){this.fire("cancel");HTMLDialogElement.prototype.close.call(this,"")},close:function(opt_returnValue){HTMLDialogElement.prototype.close.call(this,"success")},getCloseButton:function(){return this.$.close}});Polymer({is:"app-drawer",properties:{opened:{type:Boolean,value:false,notify:true,reflectToAttribute:true},persistent:{type:Boolean,value:false,reflectToAttribute:true},align:{type:String,value:"left"},position:{type:String,readOnly:true,value:"left",reflectToAttribute:true},swipeOpen:{type:Boolean,value:false,reflectToAttribute:true},noFocusTrap:{type:Boolean,value:false}},observers:["resetLayout(position)","_resetPosition(align, isAttached)"],_translateOffset:0,_trackDetails:null,_drawerState:0,_boundEscKeydownHandler:null,_firstTabStop:null,_lastTabStop:null,ready:function(){this.setScrollDirection("y");this._setTransitionDuration("0s")},attached:function(){Polymer.RenderStatus.afterNextRender(this,function(){this._setTransitionDuration("");this._boundEscKeydownHandler=this._escKeydownHandler.bind(this);this._resetDrawerState();this.listen(this,"track","_track");this.addEventListener("transitionend",this._transitionend.bind(this));this.addEventListener("keydown",this._tabKeydownHandler.bind(this))})},detached:function(){document.removeEventListener("keydown",this._boundEscKeydownHandler)},open:function(){this.opened=true},close:function(){this.opened=false},toggle:function(){this.opened=!this.opened},getWidth:function(){return this.$.contentContainer.offsetWidth},resetLayout:function(){this.debounce("_resetLayout",function(){this.fire("app-drawer-reset-layout")},1)},_isRTL:function(){return window.getComputedStyle(this).direction==="rtl"},_resetPosition:function(){switch(this.align){case"start":this._setPosition(this._isRTL()?"right":"left");return;case"end":this._setPosition(this._isRTL()?"left":"right");return}this._setPosition(this.align)},_escKeydownHandler:function(event){var ESC_KEYCODE=27;if(event.keyCode===ESC_KEYCODE){event.preventDefault();this.close()}},_track:function(event){if(this.persistent){return}event.preventDefault();switch(event.detail.state){case"start":this._trackStart(event);break;case"track":this._trackMove(event);break;case"end":this._trackEnd(event);break}},_trackStart:function(event){this._drawerState=this._DRAWER_STATE.TRACKING;this._setTransitionDuration("0s");this.style.visibility="visible";var rect=this.$.contentContainer.getBoundingClientRect();if(this.position==="left"){this._translateOffset=rect.left}else{this._translateOffset=rect.right-window.innerWidth}this._trackDetails=[]},_trackMove:function(event){this._translateDrawer(event.detail.dx+this._translateOffset);this._trackDetails.push({dx:event.detail.dx,timeStamp:Date.now()})},_trackEnd:function(event){var x=event.detail.dx+this._translateOffset;var drawerWidth=this.getWidth();var isPositionLeft=this.position==="left";var isInEndState=isPositionLeft?x>=0||x<=-drawerWidth:x<=0||x>=drawerWidth;if(!isInEndState){var trackDetails=this._trackDetails;this._trackDetails=null;this._flingDrawer(event,trackDetails);if(this._drawerState===this._DRAWER_STATE.FLINGING){return}}var halfWidth=drawerWidth/2;if(event.detail.dx<-halfWidth){this.opened=this.position==="right"}else if(event.detail.dx>halfWidth){this.opened=this.position==="left"}if(isInEndState){this._resetDrawerState()}this._setTransitionDuration("");this._resetDrawerTranslate();this.style.visibility=""},_calculateVelocity:function(event,trackDetails){var now=Date.now();var timeLowerBound=now-100;var trackDetail;var min=0;var max=trackDetails.length-1;while(min<=max){var mid=min+max>>1;var d=trackDetails[mid];if(d.timeStamp>=timeLowerBound){trackDetail=d;max=mid-1}else{min=mid+1}}if(trackDetail){var dx=event.detail.dx-trackDetail.dx;var dt=now-trackDetail.timeStamp||1;return dx/dt}return 0},_flingDrawer:function(event,trackDetails){var velocity=this._calculateVelocity(event,trackDetails);if(Math.abs(velocity)<this._MIN_FLING_THRESHOLD){return}this._drawerState=this._DRAWER_STATE.FLINGING;var x=event.detail.dx+this._translateOffset;var drawerWidth=this.getWidth();var isPositionLeft=this.position==="left";var isVelocityPositive=velocity>0;var isClosingLeft=!isVelocityPositive&&isPositionLeft;var isClosingRight=isVelocityPositive&&!isPositionLeft;var dx;if(isClosingLeft){dx=-(x+drawerWidth)}else if(isClosingRight){dx=drawerWidth-x}else{dx=-x}if(isVelocityPositive){velocity=Math.max(velocity,this._MIN_TRANSITION_VELOCITY);this.opened=this.position==="left"}else{velocity=Math.min(velocity,-this._MIN_TRANSITION_VELOCITY);this.opened=this.position==="right"}this._setTransitionDuration(this._FLING_INITIAL_SLOPE*dx/velocity+"ms");this._setTransitionTimingFunction(this._FLING_TIMING_FUNCTION);this._resetDrawerTranslate()},_transitionend:function(event){var target=Polymer.dom(event).rootTarget;if(target===this.$.contentContainer||target===this.$.scrim){if(this._drawerState===this._DRAWER_STATE.FLINGING){this._setTransitionDuration("");this._setTransitionTimingFunction("");this.style.visibility=""}this._resetDrawerState()}},_setTransitionDuration:function(duration){this.$.contentContainer.style.transitionDuration=duration;this.$.scrim.style.transitionDuration=duration},_setTransitionTimingFunction:function(timingFunction){this.$.contentContainer.style.transitionTimingFunction=timingFunction;this.$.scrim.style.transitionTimingFunction=timingFunction},_translateDrawer:function(x){var drawerWidth=this.getWidth();if(this.position==="left"){x=Math.max(-drawerWidth,Math.min(x,0));this.$.scrim.style.opacity=1+x/drawerWidth}else{x=Math.max(0,Math.min(x,drawerWidth));this.$.scrim.style.opacity=1-x/drawerWidth}this.translate3d(x+"px","0","0",this.$.contentContainer)},_resetDrawerTranslate:function(){this.$.scrim.style.opacity="";this.transform("",this.$.contentContainer)},_resetDrawerState:function(){var oldState=this._drawerState;if(this.opened){this._drawerState=this.persistent?this._DRAWER_STATE.OPENED_PERSISTENT:this._DRAWER_STATE.OPENED}else{this._drawerState=this._DRAWER_STATE.CLOSED}if(oldState!==this._drawerState){if(this._drawerState===this._DRAWER_STATE.OPENED){this._setKeyboardFocusTrap();document.addEventListener("keydown",this._boundEscKeydownHandler);document.body.style.overflow="hidden"}else{document.removeEventListener("keydown",this._boundEscKeydownHandler);document.body.style.overflow=""}if(oldState!==this._DRAWER_STATE.INIT){this.fire("app-drawer-transitioned")}}},_setKeyboardFocusTrap:function(){if(this.noFocusTrap){return}var focusableElementsSelector=['a[href]:not([tabindex="-1"])','area[href]:not([tabindex="-1"])','input:not([disabled]):not([tabindex="-1"])','select:not([disabled]):not([tabindex="-1"])','textarea:not([disabled]):not([tabindex="-1"])','button:not([disabled]):not([tabindex="-1"])','iframe:not([tabindex="-1"])','[tabindex]:not([tabindex="-1"])','[contentEditable=true]:not([tabindex="-1"])'].join(",");var focusableElements=Polymer.dom(this).querySelectorAll(focusableElementsSelector);if(focusableElements.length>0){this._firstTabStop=focusableElements[0];this._lastTabStop=focusableElements[focusableElements.length-1]}else{this._firstTabStop=null;this._lastTabStop=null}var tabindex=this.getAttribute("tabindex");if(tabindex&&parseInt(tabindex,10)>-1){this.focus()}else if(this._firstTabStop){this._firstTabStop.focus()}},_tabKeydownHandler:function(event){if(this.noFocusTrap){return}var TAB_KEYCODE=9;if(this._drawerState===this._DRAWER_STATE.OPENED&&event.keyCode===TAB_KEYCODE){if(event.shiftKey){if(this._firstTabStop&&Polymer.dom(event).localTarget===this._firstTabStop){event.preventDefault();this._lastTabStop.focus()}}else{if(this._lastTabStop&&Polymer.dom(event).localTarget===this._lastTabStop){event.preventDefault();this._firstTabStop.focus()}}}},_MIN_FLING_THRESHOLD:.2,_MIN_TRANSITION_VELOCITY:1.2,_FLING_TIMING_FUNCTION:"cubic-bezier(0.667, 1, 0.667, 1)",_FLING_INITIAL_SLOPE:1.5,_DRAWER_STATE:{INIT:0,OPENED:1,OPENED_PERSISTENT:2,CLOSED:3,TRACKING:4,FLINGING:5}});Polymer.IronFormElementBehavior={properties:{name:{type:String},value:{notify:true,type:String},required:{type:Boolean,value:false},_parentForm:{type:Object}},attached:function(){this.fire("iron-form-element-register")},detached:function(){if(this._parentForm){this._parentForm.fire("iron-form-element-unregister",{target:this})}}};Polymer.IronCheckedElementBehaviorImpl={properties:{checked:{type:Boolean,value:false,reflectToAttribute:true,notify:true,observer:"_checkedChanged"},toggles:{type:Boolean,value:true,reflectToAttribute:true},value:{type:String,value:"on",observer:"_valueChanged"}},observers:["_requiredChanged(required)"],created:function(){this._hasIronCheckedElementBehavior=true},_getValidity:function(_value){return this.disabled||!this.required||this.checked},_requiredChanged:function(){if(this.required){this.setAttribute("aria-required","true")}else{this.removeAttribute("aria-required")}},_checkedChanged:function(){this.active=this.checked;this.fire("iron-change")},_valueChanged:function(){if(this.value===undefined||this.value===null){this.value="on"}}};Polymer.IronCheckedElementBehavior=[Polymer.IronFormElementBehavior,Polymer.IronValidatableBehavior,Polymer.IronCheckedElementBehaviorImpl];Polymer.PaperCheckedElementBehaviorImpl={_checkedChanged:function(){Polymer.IronCheckedElementBehaviorImpl._checkedChanged.call(this);if(this.hasRipple()){if(this.checked){this._ripple.setAttribute("checked","")}else{this._ripple.removeAttribute("checked")}}},_buttonStateChanged:function(){Polymer.PaperRippleBehavior._buttonStateChanged.call(this);if(this.disabled){return}if(this.isAttached){this.checked=this.active}}};Polymer.PaperCheckedElementBehavior=[Polymer.PaperInkyFocusBehavior,Polymer.IronCheckedElementBehavior,Polymer.PaperCheckedElementBehaviorImpl];Polymer({is:"paper-checkbox",behaviors:[Polymer.PaperCheckedElementBehavior],hostAttributes:{role:"checkbox","aria-checked":false,tabindex:0},properties:{ariaActiveAttribute:{type:String,value:"aria-checked"}},attached:function(){var inkSize=this.getComputedStyleValue("--calculated-paper-checkbox-ink-size");if(inkSize==="-1px"){var checkboxSize=parseFloat(this.getComputedStyleValue("--calculated-paper-checkbox-size"));var defaultInkSize=Math.floor(8/3*checkboxSize);if(defaultInkSize%2!==checkboxSize%2){defaultInkSize++}this.customStyle["--paper-checkbox-ink-size"]=defaultInkSize+"px";this.updateStyles()}},_computeCheckboxClass:function(checked,invalid){var className="";if(checked){className+="checked "}if(invalid){className+="invalid"}return className},_computeCheckmarkClass:function(checked){return checked?"":"hidden"},_createRipple:function(){this._rippleContainer=this.$.checkboxContainer;return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this)}});Polymer({is:"paper-tab",behaviors:[Polymer.IronControlState,Polymer.IronButtonState,Polymer.PaperRippleBehavior],properties:{link:{type:Boolean,value:false,reflectToAttribute:true}},hostAttributes:{role:"tab"},listeners:{down:"_updateNoink",tap:"_onTap"},attached:function(){this._updateNoink()},get _parentNoink(){var parent=Polymer.dom(this).parentNode;return!!parent&&!!parent.noink},_updateNoink:function(){this.noink=!!this.noink||!!this._parentNoink},_onTap:function(event){if(this.link){var anchor=this.queryEffectiveChildren("a");if(!anchor){return}if(event.target===anchor){return}anchor.click()}}});Polymer.IronMenuBehaviorImpl={properties:{focusedItem:{observer:"_focusedItemChanged",readOnly:true,type:Object},attrForItemTitle:{type:String}},hostAttributes:{role:"menu",tabindex:"0"},observers:["_updateMultiselectable(multi)"],listeners:{focus:"_onFocus",keydown:"_onKeydown","iron-items-changed":"_onIronItemsChanged"},keyBindings:{up:"_onUpKey",down:"_onDownKey",esc:"_onEscKey","shift+tab:keydown":"_onShiftTabDown"},attached:function(){this._resetTabindices()},select:function(value){if(this._defaultFocusAsync){this.cancelAsync(this._defaultFocusAsync);this._defaultFocusAsync=null}var item=this._valueToItem(value);if(item&&item.hasAttribute("disabled"))return;this._setFocusedItem(item);Polymer.IronMultiSelectableBehaviorImpl.select.apply(this,arguments)},_resetTabindices:function(){var selectedItem=this.multi?this.selectedItems&&this.selectedItems[0]:this.selectedItem;this.items.forEach(function(item){item.setAttribute("tabindex",item===selectedItem?"0":"-1")},this)},_updateMultiselectable:function(multi){if(multi){this.setAttribute("aria-multiselectable","true")}else{this.removeAttribute("aria-multiselectable")}},_focusWithKeyboardEvent:function(event){for(var i=0,item;item=this.items[i];i++){var attr=this.attrForItemTitle||"textContent";var title=item[attr]||item.getAttribute(attr);if(!item.hasAttribute("disabled")&&title&&title.trim().charAt(0).toLowerCase()===String.fromCharCode(event.keyCode).toLowerCase()){this._setFocusedItem(item);break}}},_focusPrevious:function(){var length=this.items.length;var curFocusIndex=Number(this.indexOf(this.focusedItem));for(var i=1;i<length+1;i++){var item=this.items[(curFocusIndex-i+length)%length];if(!item.hasAttribute("disabled")){var owner=Polymer.dom(item).getOwnerRoot()||document;this._setFocusedItem(item);if(Polymer.dom(owner).activeElement==item){return}}}},_focusNext:function(){var length=this.items.length;var curFocusIndex=Number(this.indexOf(this.focusedItem));for(var i=1;i<length+1;i++){var item=this.items[(curFocusIndex+i)%length];if(!item.hasAttribute("disabled")){var owner=Polymer.dom(item).getOwnerRoot()||document;this._setFocusedItem(item);if(Polymer.dom(owner).activeElement==item){return}}}},_applySelection:function(item,isSelected){if(isSelected){item.setAttribute("aria-selected","true")}else{item.removeAttribute("aria-selected")}Polymer.IronSelectableBehavior._applySelection.apply(this,arguments)},_focusedItemChanged:function(focusedItem,old){old&&old.setAttribute("tabindex","-1");if(focusedItem){focusedItem.setAttribute("tabindex","0");focusedItem.focus()}},_onIronItemsChanged:function(event){if(event.detail.addedNodes.length){this._resetTabindices()}},_onShiftTabDown:function(event){var oldTabIndex=this.getAttribute("tabindex");Polymer.IronMenuBehaviorImpl._shiftTabPressed=true;this._setFocusedItem(null);this.setAttribute("tabindex","-1");this.async(function(){this.setAttribute("tabindex",oldTabIndex);Polymer.IronMenuBehaviorImpl._shiftTabPressed=false},1)},_onFocus:function(event){if(Polymer.IronMenuBehaviorImpl._shiftTabPressed){return}var rootTarget=Polymer.dom(event).rootTarget;if(rootTarget!==this&&typeof rootTarget.tabIndex!=="undefined"&&!this.isLightDescendant(rootTarget)){return}this._defaultFocusAsync=this.async(function(){var selectedItem=this.multi?this.selectedItems&&this.selectedItems[0]:this.selectedItem;this._setFocusedItem(null);if(selectedItem){this._setFocusedItem(selectedItem)}else if(this.items[0]){this._focusNext()}})},_onUpKey:function(event){this._focusPrevious();event.detail.keyboardEvent.preventDefault()},_onDownKey:function(event){this._focusNext();event.detail.keyboardEvent.preventDefault()},_onEscKey:function(event){this.focusedItem.blur()},_onKeydown:function(event){if(!this.keyboardEventMatchesKeys(event,"up down esc")){this._focusWithKeyboardEvent(event)}event.stopPropagation()},_activateHandler:function(event){Polymer.IronSelectableBehavior._activateHandler.call(this,event);event.stopPropagation()}};Polymer.IronMenuBehaviorImpl._shiftTabPressed=false;Polymer.IronMenuBehavior=[Polymer.IronMultiSelectableBehavior,Polymer.IronA11yKeysBehavior,Polymer.IronMenuBehaviorImpl];Polymer.IronMenubarBehaviorImpl={hostAttributes:{role:"menubar"},keyBindings:{left:"_onLeftKey",right:"_onRightKey"},_onUpKey:function(event){this.focusedItem.click();event.detail.keyboardEvent.preventDefault()},_onDownKey:function(event){this.focusedItem.click();event.detail.keyboardEvent.preventDefault()},get _isRTL(){return window.getComputedStyle(this)["direction"]==="rtl"},_onLeftKey:function(event){if(this._isRTL){this._focusNext()}else{this._focusPrevious()}event.detail.keyboardEvent.preventDefault()},_onRightKey:function(event){if(this._isRTL){this._focusPrevious()}else{this._focusNext()}event.detail.keyboardEvent.preventDefault()},_onKeydown:function(event){if(this.keyboardEventMatchesKeys(event,"up down left right esc")){return}this._focusWithKeyboardEvent(event)}};Polymer.IronMenubarBehavior=[Polymer.IronMenuBehavior,Polymer.IronMenubarBehaviorImpl];Polymer({is:"paper-tabs",behaviors:[Polymer.IronResizableBehavior,Polymer.IronMenubarBehavior],properties:{noink:{type:Boolean,value:false,observer:"_noinkChanged"},noBar:{type:Boolean,value:false},noSlide:{type:Boolean,value:false},scrollable:{type:Boolean,value:false},fitContainer:{type:Boolean,value:false},disableDrag:{type:Boolean,value:false},hideScrollButtons:{type:Boolean,value:false},alignBottom:{type:Boolean,value:false},selectable:{type:String,value:"paper-tab"},autoselect:{type:Boolean,value:false},autoselectDelay:{type:Number,value:0},_step:{type:Number,value:10},_holdDelay:{type:Number,value:1},_leftHidden:{type:Boolean,value:false},_rightHidden:{type:Boolean,value:false},_previousTab:{type:Object}},hostAttributes:{role:"tablist"},listeners:{"iron-resize":"_onTabSizingChanged","iron-items-changed":"_onTabSizingChanged","iron-select":"_onIronSelect","iron-deselect":"_onIronDeselect"},keyBindings:{"left:keyup right:keyup":"_onArrowKeyup"},created:function(){this._holdJob=null;this._pendingActivationItem=undefined;this._pendingActivationTimeout=undefined;this._bindDelayedActivationHandler=this._delayedActivationHandler.bind(this);this.addEventListener("blur",this._onBlurCapture.bind(this),true)},ready:function(){this.setScrollDirection("y",this.$.tabsContainer)},detached:function(){this._cancelPendingActivation()},_noinkChanged:function(noink){var childTabs=Polymer.dom(this).querySelectorAll("paper-tab");childTabs.forEach(noink?this._setNoinkAttribute:this._removeNoinkAttribute)},_setNoinkAttribute:function(element){element.setAttribute("noink","")},_removeNoinkAttribute:function(element){element.removeAttribute("noink")},_computeScrollButtonClass:function(hideThisButton,scrollable,hideScrollButtons){if(!scrollable||hideScrollButtons){return"hidden"}if(hideThisButton){return"not-visible"}return""},_computeTabsContentClass:function(scrollable,fitContainer){return scrollable?"scrollable"+(fitContainer?" fit-container":""):" fit-container"},_computeSelectionBarClass:function(noBar,alignBottom){if(noBar){return"hidden"}else if(alignBottom){return"align-bottom"}return""},_onTabSizingChanged:function(){this.debounce("_onTabSizingChanged",function(){this._scroll();this._tabChanged(this.selectedItem)},10)},_onIronSelect:function(event){this._tabChanged(event.detail.item,this._previousTab);this._previousTab=event.detail.item;this.cancelDebouncer("tab-changed")},_onIronDeselect:function(event){this.debounce("tab-changed",function(){this._tabChanged(null,this._previousTab);this._previousTab=null},1)},_activateHandler:function(){this._cancelPendingActivation();Polymer.IronMenuBehaviorImpl._activateHandler.apply(this,arguments)},_scheduleActivation:function(item,delay){this._pendingActivationItem=item;this._pendingActivationTimeout=this.async(this._bindDelayedActivationHandler,delay)},_delayedActivationHandler:function(){var item=this._pendingActivationItem;this._pendingActivationItem=undefined;this._pendingActivationTimeout=undefined;item.fire(this.activateEvent,null,{bubbles:true,cancelable:true})},_cancelPendingActivation:function(){if(this._pendingActivationTimeout!==undefined){this.cancelAsync(this._pendingActivationTimeout);this._pendingActivationItem=undefined;this._pendingActivationTimeout=undefined}},_onArrowKeyup:function(event){if(this.autoselect){this._scheduleActivation(this.focusedItem,this.autoselectDelay)}},_onBlurCapture:function(event){if(event.target===this._pendingActivationItem){this._cancelPendingActivation()}},get _tabContainerScrollSize(){return Math.max(0,this.$.tabsContainer.scrollWidth-this.$.tabsContainer.offsetWidth)},_scroll:function(e,detail){if(!this.scrollable){return}var ddx=detail&&-detail.ddx||0;this._affectScroll(ddx)},_down:function(e){this.async(function(){if(this._defaultFocusAsync){this.cancelAsync(this._defaultFocusAsync);this._defaultFocusAsync=null}},1)},_affectScroll:function(dx){this.$.tabsContainer.scrollLeft+=dx;var scrollLeft=this.$.tabsContainer.scrollLeft;this._leftHidden=scrollLeft===0;this._rightHidden=scrollLeft===this._tabContainerScrollSize},_onLeftScrollButtonDown:function(){this._scrollToLeft();this._holdJob=setInterval(this._scrollToLeft.bind(this),this._holdDelay)},_onRightScrollButtonDown:function(){this._scrollToRight();this._holdJob=setInterval(this._scrollToRight.bind(this),this._holdDelay)},_onScrollButtonUp:function(){clearInterval(this._holdJob);this._holdJob=null},_scrollToLeft:function(){this._affectScroll(-this._step)},_scrollToRight:function(){this._affectScroll(this._step)},_tabChanged:function(tab,old){if(!tab){this.$.selectionBar.classList.remove("expand");this.$.selectionBar.classList.remove("contract");this._positionBar(0,0);return}var r=this.$.tabsContent.getBoundingClientRect();var w=r.width;var tabRect=tab.getBoundingClientRect();var tabOffsetLeft=tabRect.left-r.left;this._pos={width:this._calcPercent(tabRect.width,w),left:this._calcPercent(tabOffsetLeft,w)};if(this.noSlide||old==null){this.$.selectionBar.classList.remove("expand");this.$.selectionBar.classList.remove("contract");this._positionBar(this._pos.width,this._pos.left);return}var oldRect=old.getBoundingClientRect();var oldIndex=this.items.indexOf(old);var index=this.items.indexOf(tab);var m=5;this.$.selectionBar.classList.add("expand");var moveRight=oldIndex<index;var isRTL=this._isRTL;if(isRTL){moveRight=!moveRight}if(moveRight){this._positionBar(this._calcPercent(tabRect.left+tabRect.width-oldRect.left,w)-m,this._left)}else{this._positionBar(this._calcPercent(oldRect.left+oldRect.width-tabRect.left,w)-m,this._calcPercent(tabOffsetLeft,w)+m)}if(this.scrollable){this._scrollToSelectedIfNeeded(tabRect.width,tabOffsetLeft)}},_scrollToSelectedIfNeeded:function(tabWidth,tabOffsetLeft){var l=tabOffsetLeft-this.$.tabsContainer.scrollLeft;if(l<0){this.$.tabsContainer.scrollLeft+=l}else{l+=tabWidth-this.$.tabsContainer.offsetWidth;if(l>0){this.$.tabsContainer.scrollLeft+=l}}},_calcPercent:function(w,w0){return 100*w/w0},_positionBar:function(width,left){width=width||0;left=left||0;this._width=width;this._left=left;this.transform("translateX("+left+"%) scaleX("+width/100+")",this.$.selectionBar)},_onBarTransitionEnd:function(e){var cl=this.$.selectionBar.classList;if(cl.contains("expand")){cl.remove("expand");cl.add("contract");this._positionBar(this._pos.width,this._pos.left)}else if(cl.contains("contract")){cl.remove("contract")}}}); \ No newline at end of file
diff --git a/chrome/browser/resources/md_history/lazy_load.vulcanized.html b/chrome/browser/resources/md_history/lazy_load.vulcanized.html index b36ec21..838b605 100644 --- a/chrome/browser/resources/md_history/lazy_load.vulcanized.html +++ b/chrome/browser/resources/md_history/lazy_load.vulcanized.html
@@ -898,6 +898,12 @@ cursor: var(--cr-actionable_-_cursor); } +.scroll-container { + display: flex; + flex-direction: column; + min-height: 1px; +} + [selectable]:focus, [selectable] > :focus { background-color: var(--cr-selectable-focus_-_background-color); outline: var(--cr-selectable-focus_-_outline); }
diff --git a/chrome/browser/resources/settings/compiled_resources2.gyp b/chrome/browser/resources/settings/compiled_resources2.gyp index 3626edf..09caa83 100644 --- a/chrome/browser/resources/settings/compiled_resources2.gyp +++ b/chrome/browser/resources/settings/compiled_resources2.gyp
@@ -72,15 +72,5 @@ ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, - { - 'target_name': 'settings', - 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - 'direction_delegate', - 'route', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, ], }
diff --git a/chrome/browser/resources/settings/settings.html b/chrome/browser/resources/settings/settings.html index 86370a0..6efcfe3 100644 --- a/chrome/browser/resources/settings/settings.html +++ b/chrome/browser/resources/settings/settings.html
@@ -4,30 +4,11 @@ <meta charset="utf-8"> <title>$i18n{settings}</title> <link rel="import" href="chrome://resources/html/polymer.html"> - <link rel="import" href="/i18n_setup.html"> - <link rel="import" href="/direction_delegate.html"> <link rel="import" href="/settings_ui/settings_ui.html"> - <link rel="import" href="/prefs/prefs.html"> - <link rel="import" href="/route.html"> - <link rel="import" href="/settings_vars_css.html"> <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> </head> <body> - - <dom-module id="cr-settings"> - <template> - <style> - :host { - color: var(--paper-grey-800); - } - </style> - <settings-prefs id="prefs" prefs="{{prefs_}}"></settings-prefs> - <settings-ui id="ui" prefs="{{prefs_}}"></settings-ui> - </template> - <script src="/settings.js"></script> - </dom-module> - - <cr-settings></cr-settings> + <settings-ui></settings-ui> <link rel="import" href="chrome://resources/html/i18n_template.html"> </body> </html>
diff --git a/chrome/browser/resources/settings/settings.js b/chrome/browser/resources/settings/settings.js deleted file mode 100644 index bb59abb..0000000 --- a/chrome/browser/resources/settings/settings.js +++ /dev/null
@@ -1,31 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.exportPath('settings'); -assert(!settings.defaultResourceLoaded, - 'settings.js run twice. You probably have an invalid import.'); -/** Global defined when the main Settings script runs. */ -settings.defaultResourceLoaded = true; - -/** - * @fileoverview - * 'cr-settings' is the main MD-Settings element, combining the UI and models. - * - * Example: - * - * <cr-settings></cr-settings> - */ -Polymer({ - is: 'cr-settings', - - /** @override */ - created: function() { - settings.initializeRouteFromUrl(); - }, - - /** @override */ - ready: function() { - this.$.ui.directionDelegate = new settings.DirectionDelegateImpl; - }, -});
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.html b/chrome/browser/resources/settings/settings_page/settings_subpage.html index d55220b..0a4c36f 100644 --- a/chrome/browser/resources/settings/settings_page/settings_subpage.html +++ b/chrome/browser/resources/settings/settings_page/settings_subpage.html
@@ -22,10 +22,11 @@ } paper-icon-button { + /* Centers the ripple on the icon with appropriate margin on right. */ + -webkit-margin-end: 8px; + -webkit-margin-start: -8px; /* The inner icon is 20px in size. paper-icon-button has 8px padding. */ height: 36px; - /* Centers the ripple on the icon with appropriate margin on right. */ - margin: 0 8px 0 -8px; width: 36px; }
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd index 0371670..cfd1d04 100644 --- a/chrome/browser/resources/settings/settings_resources.grd +++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -882,9 +882,6 @@ <structure name="IDR_SETTINGS_SETTINGS_HTML" file="settings.html" type="chrome_html" /> - <structure name="IDR_SETTINGS_SETTINGS_JS" - file="settings.js" - type="chrome_html" /> <structure name="IDR_SETTINGS_USB_DEVICES_HTML" file="site_settings/usb_devices.html" type="chrome_html" />
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html index 4153d77..baafd39c 100644 --- a/chrome/browser/resources/settings/settings_ui/settings_ui.html +++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -10,6 +10,9 @@ <link rel="import" href="/settings_main/settings_main.html"> <link rel="import" href="/settings_menu/settings_menu.html"> <link rel="import" href="/settings_shared_css.html"> +<link rel="import" href="/prefs/prefs.html"> +<link rel="import" href="/route.html"> +<link rel="import" href="/settings_vars_css.html"> <dom-module id="settings-ui"> <template> @@ -21,6 +24,7 @@ }; -webkit-user-select: none; background-color: var(--settings-background-color); + color: var(--paper-grey-800); display: flex; flex-direction: column; overflow: hidden; /* Prevent double scroll bar bugs. */ @@ -78,6 +82,7 @@ overflow: auto; } </style> + <settings-prefs id="prefs" prefs="{{prefs}}"></settings-prefs> <cr-toolbar page-name="$i18n{settings}" clear-label="$i18n{clearSearch}" search-prompt="$i18n{searchPrompt}" on-cr-menu-tap="onMenuButtonTap_" spinner-active="[[toolbarSpinnerActive_]]" show-menu
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js index 910bddc..52307933 100644 --- a/chrome/browser/resources/settings/settings_ui/settings_ui.js +++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -10,6 +10,12 @@ * * <settings-ui prefs="{{prefs}}"></settings-ui> */ +cr.exportPath('settings'); +assert(!settings.defaultResourceLoaded, + 'settings_ui.js run twice. You probably have an invalid import.'); +/** Global defined when the main Settings script runs. */ +settings.defaultResourceLoaded = true; + Polymer({ is: 'settings-ui', @@ -23,6 +29,7 @@ directionDelegate: { observer: 'directionDelegateChanged_', type: Object, + value: new settings.DirectionDelegateImpl(), }, /** @private {boolean} */ @@ -38,6 +45,11 @@ pageVisibility_: Object, }, + /** @override */ + created: function() { + settings.initializeRouteFromUrl(); + }, + /** * @override * @suppress {es5Strict} Object literals cannot contain duplicate keys in ES5
diff --git a/chrome/browser/resources/snippets_internals.html b/chrome/browser/resources/snippets_internals.html index f8251e9..eab56bb3 100644 --- a/chrome/browser/resources/snippets_internals.html +++ b/chrome/browser/resources/snippets_internals.html
@@ -137,7 +137,7 @@ <table> <tr> <td>ID - <td jscontent="suggestionId"> + <td jscontent="idWithinCategory"> <tr> <td>URL <td><a class="url" jsvalues="href:url" jscontent="url"></a>
diff --git a/chrome/browser/resources/vulcanize.py b/chrome/browser/resources/vulcanize.py index 66f7fa4..3752c965 100755 --- a/chrome/browser/resources/vulcanize.py +++ b/chrome/browser/resources/vulcanize.py
@@ -88,7 +88,6 @@ # TODO(tsergeant): Remove when JS resources are minified by default: # crbug.com/619091. _run_cmd(['uglifyjs', js_out_path, - '--beautify', 'indent-level=2,quote_style=3', '--comments', '/Copyright|license|LICENSE|\<\/?if/', '--output', js_out_path]) finally:
diff --git a/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc b/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc index b8c0766e..734e4d5 100644 --- a/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc +++ b/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc
@@ -180,6 +180,10 @@ return sync_data_to_return_; } + void AddLocalChangeObserver(syncer::LocalChangeObserver* observer) override {} + void RemoveLocalChangeObserver( + syncer::LocalChangeObserver* observer) override {} + void FailProcessSyncChangesWith(const syncer::SyncError& error) { error_ = error; }
diff --git a/chrome/browser/ui/login/login_handler_browsertest.cc b/chrome/browser/ui/login/login_handler_browsertest.cc index 532374d..d3e1ca5 100644 --- a/chrome/browser/ui/login/login_handler_browsertest.cc +++ b/chrome/browser/ui/login/login_handler_browsertest.cc
@@ -361,8 +361,9 @@ observer.Register(content::Source<NavigationController>(controller)); // One LOAD_STOP event for LoginInterstitial, second for kAuthURL and third - // for kNoAuthURL. - const int kLoadStopEvents = 3; + // for kNoAuthURL, unless PlzNavigate is active, in which case the + // interrupted ongoing navigation does not receive LOAD_STOP. + const int kLoadStopEvents = content::IsBrowserSideNavigationEnabled() ? 2 : 3; WindowedLoadStopObserver load_stop_waiter(controller, kLoadStopEvents); WindowedAuthNeededObserver auth_needed_waiter(controller); browser()->OpenURL(OpenURLParams(kAuthURL, Referrer(),
diff --git a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc b/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc index 6d2bf4c..817448ce 100644 --- a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc +++ b/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
@@ -259,6 +259,8 @@ { "keyboardOverlayDisableCapsLock", IDS_KEYBOARD_OVERLAY_DISABLE_CAPS_LOCK }, { "keyboardOverlayToggleChromevoxSpokenFeedback", IDS_KEYBOARD_OVERLAY_TOGGLE_CHROMEVOX_SPOKEN_FEEDBACK }, + { "keyboardOverlayToggleHighContrastMode", + IDS_KEYBOARD_OVERLAY_TOGGLE_HIGH_CONTRAST_MODE}, { "keyboardOverlayToggleProjectionTouchHud", IDS_KEYBOARD_OVERLAY_TOGGLE_PROJECTION_TOUCH_HUD }, { "keyboardOverlayUndo", IDS_KEYBOARD_OVERLAY_UNDO },
diff --git a/chrome/browser/ui/webui/md_history_ui.cc b/chrome/browser/ui/webui/md_history_ui.cc index bd1e358b0..56bbf9b 100644 --- a/chrome/browser/ui/webui/md_history_ui.cc +++ b/chrome/browser/ui/webui/md_history_ui.cc
@@ -35,6 +35,8 @@ namespace { +constexpr char kShowMenuPromoKey[] = "showMenuPromo"; + content::WebUIDataSource* CreateMdHistoryUIHTMLSource(Profile* profile, bool use_test_title) { content::WebUIDataSource* source = @@ -117,7 +119,7 @@ prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory); source->AddBoolean("allowDeletingHistory", allow_deleting_history); - source->AddBoolean("showMenuPromo", + source->AddBoolean(kShowMenuPromoKey, !prefs->GetBoolean(prefs::kMdHistoryMenuPromoShown)); bool group_by_domain = base::CommandLine::ForCurrentProcess()->HasSwitch( @@ -267,5 +269,5 @@ void MdHistoryUI::HandleMenuPromoShown(const base::ListValue* args) { Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean( prefs::kMdHistoryMenuPromoShown, true); - data_source_->AddBoolean("showMenuPromo", false); + data_source_->AddBoolean(kShowMenuPromoKey, false); }
diff --git a/chrome/browser/ui/webui/snippets_internals_message_handler.cc b/chrome/browser/ui/webui/snippets_internals_message_handler.cc index 761bad9..55030f8 100644 --- a/chrome/browser/ui/webui/snippets_internals_message_handler.cc +++ b/chrome/browser/ui/webui/snippets_internals_message_handler.cc
@@ -41,7 +41,7 @@ const ContentSuggestion& suggestion, int index) { auto entry = base::MakeUnique<base::DictionaryValue>(); - entry->SetString("suggestionId", suggestion.id()); + entry->SetString("idWithinCategory", suggestion.id().id_within_category()); entry->SetString("url", suggestion.url().spec()); entry->SetString("ampUrl", suggestion.amp_url().spec()); entry->SetString("title", suggestion.title()); @@ -144,8 +144,7 @@ } void SnippetsInternalsMessageHandler::OnSuggestionInvalidated( - ntp_snippets::Category category, - const std::string& suggestion_id) { + const ntp_snippets::ContentSuggestion::ID& suggestion_id) { if (!dom_loaded_) return; SendContentSuggestions();
diff --git a/chrome/browser/ui/webui/snippets_internals_message_handler.h b/chrome/browser/ui/webui/snippets_internals_message_handler.h index 6b423bc..4ca4ead 100644 --- a/chrome/browser/ui/webui/snippets_internals_message_handler.h +++ b/chrome/browser/ui/webui/snippets_internals_message_handler.h
@@ -41,8 +41,8 @@ void OnCategoryStatusChanged( ntp_snippets::Category category, ntp_snippets::CategoryStatus new_status) override; - void OnSuggestionInvalidated(ntp_snippets::Category category, - const std::string& suggestion_id) override; + void OnSuggestionInvalidated( + const ntp_snippets::ContentSuggestion::ID& suggestion_id) override; void ContentSuggestionsServiceShutdown() override; void HandleRefreshContent(const base::ListValue* args);
diff --git a/chrome/gpu/arc_gpu_video_decode_accelerator.cc b/chrome/gpu/arc_gpu_video_decode_accelerator.cc index 27e0a9e..dc194db3 100644 --- a/chrome/gpu/arc_gpu_video_decode_accelerator.cc +++ b/chrome/gpu/arc_gpu_video_decode_accelerator.cc
@@ -299,8 +299,8 @@ handle.native_pixmap_handle.fds.emplace_back( base::FileDescriptor(info.handle.release(), true)); for (const auto& plane : info.planes) { - handle.native_pixmap_handle.planes.emplace_back( - plane.stride, plane.offset, 0); + handle.native_pixmap_handle.planes.emplace_back(plane.stride, + plane.offset, 0, 0); } #endif vda_->ImportBufferForPicture(index, handle);
diff --git a/chrome/test/data/downloads/tiny_binary.bin b/chrome/test/data/downloads/tiny_binary.bin new file mode 100644 index 0000000..593f470 --- /dev/null +++ b/chrome/test/data/downloads/tiny_binary.bin Binary files differ
diff --git a/chrome/test/data/webui/settings/easy_unlock_browsertest_chromeos.js b/chrome/test/data/webui/settings/easy_unlock_browsertest_chromeos.js index d6214712..b3c1f4bb 100644 --- a/chrome/test/data/webui/settings/easy_unlock_browsertest_chromeos.js +++ b/chrome/test/data/webui/settings/easy_unlock_browsertest_chromeos.js
@@ -134,7 +134,7 @@ // Before clearing the body, save a copy of the real prefs so we can // cleanly re-create the People page element. - prefs = document.querySelector('cr-settings').$$('settings-prefs').prefs; + prefs = document.querySelector('settings-ui').$$('settings-prefs').prefs; }); setup(function() {
diff --git a/chrome/test/data/webui/settings/on_startup_browsertest.js b/chrome/test/data/webui/settings/on_startup_browsertest.js index f6c0db2..5c9e7aa3 100644 --- a/chrome/test/data/webui/settings/on_startup_browsertest.js +++ b/chrome/test/data/webui/settings/on_startup_browsertest.js
@@ -62,7 +62,7 @@ self.getPage('basic').set('pageVisibility.onStartup', true); Polymer.dom.flush(); - settingsPrefs = document.querySelector('cr-settings').$$( + settingsPrefs = document.querySelector('settings-ui').$$( 'settings-prefs'); assertTrue(!!settingsPrefs); return CrSettingsPrefs.initialized;
diff --git a/chrome/test/data/webui/settings/settings_page_browsertest.js b/chrome/test/data/webui/settings/settings_page_browsertest.js index a3a2fb1..afd3351 100644 --- a/chrome/test/data/webui/settings/settings_page_browsertest.js +++ b/chrome/test/data/webui/settings/settings_page_browsertest.js
@@ -55,9 +55,7 @@ * @return {!PolymerElement} The PolymerElement for the page. */ getPage: function(type) { - var settings = document.querySelector('cr-settings'); - assertTrue(!!settings); - var settingsUi = settings.$$('settings-ui'); + var settingsUi = document.querySelector('settings-ui'); assertTrue(!!settingsUi); var settingsMain = settingsUi.$$('settings-main'); assertTrue(!!settingsMain);
diff --git a/chrome/test/data/webui/settings/settings_ui_browsertest.js b/chrome/test/data/webui/settings/settings_ui_browsertest.js index 5e08ca8..bc44014 100644 --- a/chrome/test/data/webui/settings/settings_ui_browsertest.js +++ b/chrome/test/data/webui/settings/settings_ui_browsertest.js
@@ -31,8 +31,7 @@ var ui; suiteSetup(function() { - var settings = assert(document.querySelector('cr-settings')); - ui = assert(settings.$$('settings-ui')); + ui = assert(document.querySelector('settings-ui')); }); test('basic', function() {
diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc index 7ced1be..ffe6089 100644 --- a/components/cronet/android/cronet_url_request_context_adapter.cc +++ b/components/cronet/android/cronet_url_request_context_adapter.cc
@@ -775,7 +775,7 @@ return network_thread_->task_runner(); } -void CronetURLRequestContextAdapter::StartNetLogToFile( +bool CronetURLRequestContextAdapter::StartNetLogToFile( JNIEnv* env, const JavaParamRef<jobject>& jcaller, const JavaParamRef<jstring>& jfile_name, @@ -783,14 +783,14 @@ base::AutoLock lock(write_to_file_observer_lock_); // Do nothing if already logging to a file. if (write_to_file_observer_) - return; + return true; std::string file_name = base::android::ConvertJavaStringToUTF8(env, jfile_name); base::FilePath file_path(file_name); base::ScopedFILE file(base::OpenFile(file_path, "w")); if (!file) { LOG(ERROR) << "Failed to open NetLog file for writing."; - return; + return false; } write_to_file_observer_.reset(new net::WriteToFileNetLogObserver()); @@ -801,6 +801,8 @@ write_to_file_observer_->StartObserving( g_net_log.Get().net_log(), std::move(file), /*constants=*/nullptr, /*url_request_context=*/nullptr); + + return true; } void CronetURLRequestContextAdapter::StartNetLogToDisk(
diff --git a/components/cronet/android/cronet_url_request_context_adapter.h b/components/cronet/android/cronet_url_request_context_adapter.h index 901f26c6..647cc06 100644 --- a/components/cronet/android/cronet_url_request_context_adapter.h +++ b/components/cronet/android/cronet_url_request_context_adapter.h
@@ -79,8 +79,9 @@ net::URLRequestContext* GetURLRequestContext(); - // Starts NetLog logging to file. This can be called on any thread. - void StartNetLogToFile(JNIEnv* env, + // Starts NetLog logging to file. This can be called on any thread. Returns + // false if it fails to open log file. + bool StartNetLogToFile(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller, const base::android::JavaParamRef<jstring>& jfile_name, jboolean jlog_all);
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java index 2c2df1a7..f9656fa 100644 --- a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java +++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
@@ -260,7 +260,9 @@ public void startNetLogToFile(String fileName, boolean logAll) { synchronized (mLock) { checkHaveAdapter(); - nativeStartNetLogToFile(mUrlRequestContextAdapter, fileName, logAll); + if (!nativeStartNetLogToFile(mUrlRequestContextAdapter, fileName, logAll)) { + throw new RuntimeException("Unable to start NetLog"); + } mIsLogging = true; } } @@ -612,7 +614,7 @@ private native void nativeDestroy(long nativePtr); @NativeClassQualifiedName("CronetURLRequestContextAdapter") - private native void nativeStartNetLogToFile(long nativePtr, String fileName, boolean logAll); + private native boolean nativeStartNetLogToFile(long nativePtr, String fileName, boolean logAll); @NativeClassQualifiedName("CronetURLRequestContextAdapter") private native void nativeStartNetLogToDisk(
diff --git a/components/domain_reliability/quic_error_mapping.cc b/components/domain_reliability/quic_error_mapping.cc index 853f49f..3b247e8 100644 --- a/components/domain_reliability/quic_error_mapping.cc +++ b/components/domain_reliability/quic_error_mapping.cc
@@ -242,6 +242,10 @@ // has too many gaps. { net::QUIC_TOO_MANY_FRAME_GAPS, "quic.too_many_frame_gaps" }, + // Sequencer buffer get into weird state where continuing read/write + // will lead to crash. + { net::QUIC_STREAM_SEQUENCER_INVALID_STATE, + "quic.stream_sequencer_invalid_state" }, // No error. Used as bound while iterating. { net::QUIC_LAST_ERROR, "quic.last_error"}
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc index de1df93..53b20f9 100644 --- a/components/exo/wayland/server.cc +++ b/components/exo/wayland/server.cc
@@ -536,9 +536,9 @@ } std::vector<gfx::NativePixmapPlane> planes; - planes.emplace_back(stride0, offset0, 0); - planes.emplace_back(stride1, offset1, 0); - planes.emplace_back(stride2, offset2, 0); + planes.emplace_back(stride0, offset0, 0, 0); + planes.emplace_back(stride1, offset1, 0, 0); + planes.emplace_back(stride2, offset2, 0, 0); std::vector<base::ScopedFD> fds; size_t num_planes = @@ -694,7 +694,7 @@ return; } LinuxBufferParams::Plane& plane = plane_it->second; - planes.emplace_back(plane.stride, plane.offset, 0); + planes.emplace_back(plane.stride, plane.offset, 0, 0); if (plane.fd.is_valid()) fds.push_back(std::move(plane.fd)); }
diff --git a/components/gcm_driver/gcm_stats_recorder_impl.cc b/components/gcm_driver/gcm_stats_recorder_impl.cc index d632bd3..719f2538 100644 --- a/components/gcm_driver/gcm_stats_recorder_impl.cc +++ b/components/gcm_driver/gcm_stats_recorder_impl.cc
@@ -400,6 +400,8 @@ int message_byte_size, bool to_registered_app, ReceivedMessageType message_type) { + UMA_HISTOGRAM_BOOLEAN("GCM.DataMessageReceivedHasRegisteredApp", + to_registered_app); if (to_registered_app) UMA_HISTOGRAM_COUNTS("GCM.DataMessageReceived", 1);
diff --git a/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc b/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc index 27db5336..ca953b5 100644 --- a/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc +++ b/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc
@@ -154,14 +154,14 @@ } void BookmarkSuggestionsProvider::DismissSuggestion( - const std::string& suggestion_id) { + const ContentSuggestion::ID& suggestion_id) { DCHECK(bookmark_model_->loaded()); - GURL url(GetWithinCategoryIDFromUniqueID(suggestion_id)); + GURL url(suggestion_id.id_within_category()); MarkBookmarksDismissed(bookmark_model_, url); } void BookmarkSuggestionsProvider::FetchSuggestionImage( - const std::string& suggestion_id, + const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, gfx::Image())); @@ -260,9 +260,8 @@ ContentSuggestion BookmarkSuggestionsProvider::ConvertBookmark( const BookmarkNode* bookmark) { - ContentSuggestion suggestion( - MakeUniqueID(provided_category_, bookmark->url().spec()), - bookmark->url()); + ContentSuggestion suggestion(provided_category_, bookmark->url().spec(), + bookmark->url()); suggestion.set_title(bookmark->GetTitle()); suggestion.set_snippet_text(base::string16());
diff --git a/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h b/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h index 79b8718..68ede9d 100644 --- a/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h +++ b/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h
@@ -39,8 +39,8 @@ // ContentSuggestionsProvider implementation. CategoryStatus GetCategoryStatus(Category category) override; CategoryInfo GetCategoryInfo(Category category) override; - void DismissSuggestion(const std::string& suggestion_id) override; - void FetchSuggestionImage(const std::string& suggestion_id, + void DismissSuggestion(const ContentSuggestion::ID& suggestion_id) override; + void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) override; void ClearHistory( base::Time begin,
diff --git a/components/ntp_snippets/category_factory.cc b/components/ntp_snippets/category_factory.cc index 9962f077..4da984e 100644 --- a/components/ntp_snippets/category_factory.cc +++ b/components/ntp_snippets/category_factory.cc
@@ -12,13 +12,6 @@ namespace ntp_snippets { -namespace { - -const char kCombinedIDFormat[] = "%d|%s"; -const char kSeparator = '|'; - -} // namespace - CategoryFactory::CategoryFactory() { // Add all local categories in a fixed order. AddKnownCategory(KnownCategories::DOWNLOADS); @@ -66,32 +59,6 @@ ordered_categories_.end(), right); } -std::string CategoryFactory::MakeUniqueID( - Category category, - const std::string& within_category_id) const { - return base::StringPrintf(kCombinedIDFormat, category.id(), - within_category_id.c_str()); -} - -Category CategoryFactory::GetCategoryFromUniqueID( - const std::string& unique_id) { - size_t colon_index = unique_id.find(kSeparator); - DCHECK_NE(std::string::npos, colon_index) << "Not a valid unique_id: " - << unique_id; - int category = -1; - bool ret = base::StringToInt(unique_id.substr(0, colon_index), &category); - DCHECK(ret) << "Non-numeric category part in unique_id: " << unique_id; - return FromIDValue(category); -} - -std::string CategoryFactory::GetWithinCategoryIDFromUniqueID( - const std::string& unique_id) const { - size_t colon_index = unique_id.find(kSeparator); - DCHECK_NE(std::string::npos, colon_index) << "Not a valid unique_id: " - << unique_id; - return unique_id.substr(colon_index + 1); -} - //////////////////////////////////////////////////////////////////////////////// // Private methods
diff --git a/components/ntp_snippets/category_factory.h b/components/ntp_snippets/category_factory.h index db134d2..c3941ea 100644 --- a/components/ntp_snippets/category_factory.h +++ b/components/ntp_snippets/category_factory.h
@@ -44,23 +44,6 @@ // |FromRemoteCategory|. bool CompareCategories(const Category& left, const Category& right) const; - // TODO(treib): Remove the following 3 functions from here once we move to a - // more structured identification than the unique_id string and thus once we - // eliminate these functions. See crbug.com/649048. - - // Creates a unique ID. The given |within_category_id| must be unique among - // all suggestion IDs from this provider for the given |category|. This method - // combines it with the |category| to form an ID that is unique - // application-wide, because this provider is the only one that provides - // suggestions for that category. - std::string MakeUniqueID(Category category, - const std::string& within_category_id) const; - - // Reverse functions for MakeUniqueID() - Category GetCategoryFromUniqueID(const std::string& unique_id); - std::string GetWithinCategoryIDFromUniqueID( - const std::string& unique_id) const; - private: bool CategoryExists(int id); void AddKnownCategory(KnownCategories known_category);
diff --git a/components/ntp_snippets/content_suggestion.cc b/components/ntp_snippets/content_suggestion.cc index 0617f6e..44524cb 100644 --- a/components/ntp_snippets/content_suggestion.cc +++ b/components/ntp_snippets/content_suggestion.cc
@@ -6,13 +6,32 @@ namespace ntp_snippets { -ContentSuggestion::ContentSuggestion(const std::string& id, const GURL& url) +bool ContentSuggestion::ID::operator==(const ID& rhs) const { + return category_ == rhs.category_ && + id_within_category_ == rhs.id_within_category_; +} + +bool ContentSuggestion::ID::operator!=(const ID& rhs) const { + return !(*this == rhs); +} + +ContentSuggestion::ContentSuggestion(ID id, const GURL& url) : id_(id), url_(url), score_(0) {} +ContentSuggestion::ContentSuggestion(Category category, + const std::string& id_within_category, + const GURL& url) + : id_(category, id_within_category), url_(url), score_(0) {} + ContentSuggestion::ContentSuggestion(ContentSuggestion&&) = default; ContentSuggestion& ContentSuggestion::operator=(ContentSuggestion&&) = default; ContentSuggestion::~ContentSuggestion() = default; +std::ostream& operator<<(std::ostream& os, ContentSuggestion::ID id) { + os << id.category() << "|" << id.id_within_category(); + return os; +} + } // namespace ntp_snippets
diff --git a/components/ntp_snippets/content_suggestion.h b/components/ntp_snippets/content_suggestion.h index f289706..bf8442c 100644 --- a/components/ntp_snippets/content_suggestion.h +++ b/components/ntp_snippets/content_suggestion.h
@@ -5,13 +5,12 @@ #ifndef COMPONENTS_NTP_SNIPPETS_CONTENT_SUGGESTION_H_ #define COMPONENTS_NTP_SNIPPETS_CONTENT_SUGGESTION_H_ -#include <memory> #include <string> -#include <vector> #include "base/macros.h" #include "base/strings/string16.h" #include "base/time/time.h" +#include "components/ntp_snippets/category.h" #include "url/gurl.h" namespace ntp_snippets { @@ -20,9 +19,32 @@ // offline page, for example. class ContentSuggestion { public: + class ID { + public: + ID(Category category, const std::string& id_within_category) + : category_(category), id_within_category_(id_within_category) {} + + Category category() const { return category_; } + + const std::string& id_within_category() const { + return id_within_category_; + } + + bool operator==(const ID& rhs) const; + bool operator!=(const ID& rhs) const; + + private: + Category category_; + std::string id_within_category_; + + // Allow copy and assignment. + }; + // Creates a new ContentSuggestion. The caller must ensure that the |id| // passed in here is unique application-wide. - ContentSuggestion(const std::string& id, + ContentSuggestion(ID id, const GURL& url); + ContentSuggestion(Category category, + const std::string& id_within_category, const GURL& url); ContentSuggestion(ContentSuggestion&&); ContentSuggestion& operator=(ContentSuggestion&&); @@ -30,7 +52,7 @@ ~ContentSuggestion(); // An ID for identifying the suggestion. The ID is unique application-wide. - const std::string& id() const { return id_; } + const ID& id() const { return id_; } // The normal content URL where the content referenced by the suggestion can // be accessed. @@ -72,7 +94,7 @@ void set_score(float score) { score_ = score; } private: - std::string id_; + ID id_; GURL url_; GURL amp_url_; base::string16 title_; @@ -84,6 +106,8 @@ DISALLOW_COPY_AND_ASSIGN(ContentSuggestion); }; +std::ostream& operator<<(std::ostream& os, ContentSuggestion::ID id); + } // namespace ntp_snippets #endif // COMPONENTS_NTP_SNIPPETS_CONTENT_SUGGESTION_H_
diff --git a/components/ntp_snippets/content_suggestions_provider.cc b/components/ntp_snippets/content_suggestions_provider.cc index de3c759..3489657 100644 --- a/components/ntp_snippets/content_suggestions_provider.cc +++ b/components/ntp_snippets/content_suggestions_provider.cc
@@ -15,20 +15,4 @@ ContentSuggestionsProvider::~ContentSuggestionsProvider() = default; -std::string ContentSuggestionsProvider::MakeUniqueID( - Category category, - const std::string& within_category_id) const { - return category_factory()->MakeUniqueID(category, within_category_id); -} - -Category ContentSuggestionsProvider::GetCategoryFromUniqueID( - const std::string& unique_id) const { - return category_factory()->GetCategoryFromUniqueID(unique_id); -} - -std::string ContentSuggestionsProvider::GetWithinCategoryIDFromUniqueID( - const std::string& unique_id) const { - return category_factory()->GetWithinCategoryIDFromUniqueID(unique_id); -} - } // namespace ntp_snippets
diff --git a/components/ntp_snippets/content_suggestions_provider.h b/components/ntp_snippets/content_suggestions_provider.h index bc568f5..b3119669 100644 --- a/components/ntp_snippets/content_suggestions_provider.h +++ b/components/ntp_snippets/content_suggestions_provider.h
@@ -70,13 +70,13 @@ // |FetchSuggestionImage| or |DismissSuggestion| anymore, and should // immediately be cleared from the UI and caches. This happens, for example, // when the content that the suggestion refers to is gone. - // Note that this event may be fired even if the corresponding |category| is + // Note that this event may be fired even if the corresponding category is // not currently AVAILABLE, because open UIs may still be showing the // suggestion that is to be removed. This event may also be fired for // |suggestion_id|s that never existed and should be ignored in that case. - virtual void OnSuggestionInvalidated(ContentSuggestionsProvider* provider, - Category category, - const std::string& suggestion_id) = 0; + virtual void OnSuggestionInvalidated( + ContentSuggestionsProvider* provider, + const ContentSuggestion::ID& suggestion_id) = 0; }; virtual ~ContentSuggestionsProvider(); @@ -91,14 +91,15 @@ // a once-dismissed suggestion is never delivered again (through the // Observer). The provider must not call Observer::OnSuggestionsChanged if the // removal of the dismissed suggestion is the only change. - virtual void DismissSuggestion(const std::string& suggestion_id) = 0; + virtual void DismissSuggestion( + const ContentSuggestion::ID& suggestion_id) = 0; // Fetches the image for the suggestion with the given ID and returns it // through the callback. This fetch may occur locally or from the internet. // If that suggestion doesn't exist, doesn't have an image or if the fetch // fails, the callback gets a null image. The callback will not be called // synchronously. - virtual void FetchSuggestionImage(const std::string& suggestion_id, + virtual void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) = 0; // Removes history from the specified time range where the URL matches the @@ -136,19 +137,6 @@ ContentSuggestionsProvider(Observer* observer, CategoryFactory* category_factory); - // Creates a unique ID. The given |within_category_id| must be unique among - // all suggestion IDs from this provider for the given |category|. This method - // combines it with the |category| to form an ID that is unique - // application-wide, because this provider is the only one that provides - // suggestions for that category. - std::string MakeUniqueID(Category category, - const std::string& within_category_id) const; - - // Reverse functions for MakeUniqueID() - Category GetCategoryFromUniqueID(const std::string& unique_id) const; - std::string GetWithinCategoryIDFromUniqueID( - const std::string& unique_id) const; - Observer* observer() const { return observer_; } CategoryFactory* category_factory() const { return category_factory_; }
diff --git a/components/ntp_snippets/content_suggestions_service.cc b/components/ntp_snippets/content_suggestions_service.cc index f141eab..f52a980 100644 --- a/components/ntp_snippets/content_suggestions_service.cc +++ b/components/ntp_snippets/content_suggestions_service.cc
@@ -72,18 +72,17 @@ } void ContentSuggestionsService::FetchSuggestionImage( - const std::string& suggestion_id, + const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) { - Category category = category_factory_.GetCategoryFromUniqueID(suggestion_id); - if (!providers_by_category_.count(category)) { + if (!providers_by_category_.count(suggestion_id.category())) { LOG(WARNING) << "Requested image for suggestion " << suggestion_id - << " for unavailable category " << category; + << " for unavailable category " << suggestion_id.category(); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, gfx::Image())); return; } - providers_by_category_[category]->FetchSuggestionImage(suggestion_id, - callback); + providers_by_category_[suggestion_id.category()]->FetchSuggestionImage( + suggestion_id, callback); } void ContentSuggestionsService::ClearHistory( @@ -130,17 +129,17 @@ } void ContentSuggestionsService::DismissSuggestion( - const std::string& suggestion_id) { - Category category = category_factory_.GetCategoryFromUniqueID(suggestion_id); - if (!providers_by_category_.count(category)) { + const ContentSuggestion::ID& suggestion_id) { + if (!providers_by_category_.count(suggestion_id.category())) { LOG(WARNING) << "Dismissed suggestion " << suggestion_id - << " for unavailable category " << category; + << " for unavailable category " << suggestion_id.category(); return; } - providers_by_category_[category]->DismissSuggestion(suggestion_id); + providers_by_category_[suggestion_id.category()]->DismissSuggestion( + suggestion_id); // Remove the suggestion locally. - bool removed = RemoveSuggestionByID(category, suggestion_id); + bool removed = RemoveSuggestionByID(suggestion_id); DCHECK(removed) << "The dismissed suggestion " << suggestion_id << " has already been removed. Providers must not call" << " OnNewSuggestions in response to DismissSuggestion."; @@ -214,11 +213,10 @@ void ContentSuggestionsService::OnSuggestionInvalidated( ContentSuggestionsProvider* provider, - Category category, - const std::string& suggestion_id) { - RemoveSuggestionByID(category, suggestion_id); + const ContentSuggestion::ID& suggestion_id) { + RemoveSuggestionByID(suggestion_id); FOR_EACH_OBSERVER(Observer, observers_, - OnSuggestionInvalidated(category, suggestion_id)); + OnSuggestionInvalidated(suggestion_id)); } // history::HistoryServiceObserver implementation. @@ -287,10 +285,9 @@ } bool ContentSuggestionsService::RemoveSuggestionByID( - Category category, - const std::string& suggestion_id) { + const ContentSuggestion::ID& suggestion_id) { std::vector<ContentSuggestion>* suggestions = - &suggestions_by_category_[category]; + &suggestions_by_category_[suggestion_id.category()]; auto position = std::find_if(suggestions->begin(), suggestions->end(), [&suggestion_id](const ContentSuggestion& suggestion) { @@ -302,7 +299,7 @@ // The positioning of the bookmarks category depends on whether it's empty. // TODO(treib): Remove this temporary hack, crbug.com/640568. - if (category.IsKnownCategory(KnownCategories::BOOKMARKS)) + if (suggestion_id.category().IsKnownCategory(KnownCategories::BOOKMARKS)) SortCategories(); return true;
diff --git a/components/ntp_snippets/content_suggestions_service.h b/components/ntp_snippets/content_suggestions_service.h index 8c4a4f7..045785f0 100644 --- a/components/ntp_snippets/content_suggestions_service.h +++ b/components/ntp_snippets/content_suggestions_service.h
@@ -63,12 +63,12 @@ // Fired when a suggestion has been invalidated. The UI must immediately // clear the suggestion even from open NTPs. Invalidation happens, for // example, when the content that the suggestion refers to is gone. - // Note that this event may be fired even if the corresponding |category| is + // Note that this event may be fired even if the corresponding category is // not currently AVAILABLE, because open UIs may still be showing the // suggestion that is to be removed. This event may also be fired for // |suggestion_id|s that never existed and should be ignored in that case. - virtual void OnSuggestionInvalidated(Category category, - const std::string& suggestion_id) = 0; + virtual void OnSuggestionInvalidated( + const ContentSuggestion::ID& suggestion_id) = 0; // Sent when the service is shutting down. After the service has shut down, // it will not provide any data anymore, though calling the getters is still @@ -114,12 +114,12 @@ // runs the |callback|. If that suggestion doesn't exist or the fetch fails, // the callback gets an empty image. The callback will not be called // synchronously. - void FetchSuggestionImage(const std::string& suggestion_id, + void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback); // Dismisses the suggestion with the given |suggestion_id|, if it exists. // This will not trigger an update through the observers. - void DismissSuggestion(const std::string& suggestion_id); + void DismissSuggestion(const ContentSuggestion::ID& suggestion_id); // Dismisses the given |category|, if it exists. // This will not trigger an update through the observers. @@ -193,9 +193,9 @@ void OnCategoryStatusChanged(ContentSuggestionsProvider* provider, Category category, CategoryStatus new_status) override; - void OnSuggestionInvalidated(ContentSuggestionsProvider* provider, - Category category, - const std::string& suggestion_id) override; + void OnSuggestionInvalidated( + ContentSuggestionsProvider* provider, + const ContentSuggestion::ID& suggestion_id) override; // history::HistoryServiceObserver implementation. void OnURLsDeleted(history::HistoryService* history_service, @@ -214,8 +214,7 @@ // Removes a suggestion from the local store |suggestions_by_category_|, if it // exists. Returns true if a suggestion was removed. - bool RemoveSuggestionByID(Category category, - const std::string& suggestion_id); + bool RemoveSuggestionByID(const ContentSuggestion::ID& suggestion_id); // Fires the OnCategoryStatusChanged event for the given |category|. void NotifyCategoryStatusChanged(Category category);
diff --git a/components/ntp_snippets/content_suggestions_service_unittest.cc b/components/ntp_snippets/content_suggestions_service_unittest.cc index d0629cb..a2245bf 100644 --- a/components/ntp_snippets/content_suggestions_service_unittest.cc +++ b/components/ntp_snippets/content_suggestions_service_unittest.cc
@@ -77,9 +77,8 @@ statuses_[category.id()]); } - void FireSuggestionInvalidated(Category category, - const std::string& suggestion_id) { - observer()->OnSuggestionInvalidated(this, category, suggestion_id); + void FireSuggestionInvalidated(const ContentSuggestion::ID& suggestion_id) { + observer()->OnSuggestionInvalidated(this, suggestion_id); } MOCK_METHOD3(ClearHistory, @@ -91,9 +90,10 @@ void(Category category, const DismissedSuggestionsCallback& callback)); MOCK_METHOD1(ClearDismissedSuggestionsForDebugging, void(Category category)); - MOCK_METHOD1(DismissSuggestion, void(const std::string& suggestion_id)); + MOCK_METHOD1(DismissSuggestion, + void(const ContentSuggestion::ID& suggestion_id)); MOCK_METHOD2(FetchSuggestionImage, - void(const std::string& suggestion_id, + void(const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback)); private: @@ -109,8 +109,8 @@ MOCK_METHOD1(OnNewSuggestions, void(Category category)); MOCK_METHOD2(OnCategoryStatusChanged, void(Category changed_category, CategoryStatus new_status)); - MOCK_METHOD2(OnSuggestionInvalidated, - void(Category category, const std::string& suggestion_id)); + MOCK_METHOD1(OnSuggestionInvalidated, + void(const ContentSuggestion::ID& suggestion_id)); MOCK_METHOD0(ContentSuggestionsServiceShutdown, void()); private: @@ -143,11 +143,9 @@ for (const auto& suggestion : service()->GetSuggestionsForCategory(category)) { - std::string within_category_id = - service()->category_factory()->GetWithinCategoryIDFromUniqueID( - suggestion.id()); + std::string id_within_category = suggestion.id().id_within_category(); int id; - ASSERT_TRUE(base::StringToInt(within_category_id, &id)); + ASSERT_TRUE(base::StringToInt(id_within_category, &id)); auto position = std::find(numbers.begin(), numbers.end(), id); if (position == numbers.end()) { ADD_FAILURE() << "Unexpected suggestion with ID " << id; @@ -205,8 +203,7 @@ // Returns a suggestion instance for testing. ContentSuggestion CreateSuggestion(Category category, int number) { return ContentSuggestion( - service()->category_factory()->MakeUniqueID(category, - base::IntToString(number)), + category, base::IntToString(number), GURL("http://testsuggestion/" + base::IntToString(number))); } @@ -299,7 +296,7 @@ provider1->FireSuggestionsChanged(articles_category, CreateSuggestions(articles_category, {1})); - std::string suggestion_id = CreateSuggestion(articles_category, 1).id(); + ContentSuggestion::ID suggestion_id(articles_category, "1"); EXPECT_CALL(*provider1, FetchSuggestionImage(suggestion_id, _)); EXPECT_CALL(*provider2, FetchSuggestionImage(_, _)).Times(0); @@ -315,7 +312,8 @@ base::RunLoop run_loop; // Assuming there will never be a category with the id below. - std::string suggestion_id = "21563|TestID"; + ContentSuggestion::ID suggestion_id(category_factory()->FromIDValue(21563), + "TestID"); EXPECT_CALL(*this, OnImageFetched(Property(&gfx::Image::IsEmpty, Eq(true)))) .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); service()->FetchSuggestionImage( @@ -333,7 +331,7 @@ provider2->FireSuggestionsChanged( offline_pages_category, CreateSuggestions(offline_pages_category, {11})); - std::string suggestion_id = CreateSuggestion(offline_pages_category, 11).id(); + ContentSuggestion::ID suggestion_id(offline_pages_category, "11"); EXPECT_CALL(*provider1, DismissSuggestion(_)).Times(0); EXPECT_CALL(*provider2, DismissSuggestion(suggestion_id)); @@ -351,19 +349,18 @@ articles_category, CreateSuggestions(articles_category, {11, 12, 13})); ExpectThatSuggestionsAre(articles_category, {11, 12, 13}); - std::string suggestion_id = CreateSuggestion(articles_category, 12).id(); - EXPECT_CALL(observer, - OnSuggestionInvalidated(articles_category, suggestion_id)); - provider->FireSuggestionInvalidated(articles_category, suggestion_id); + ContentSuggestion::ID suggestion_id(articles_category, "12"); + EXPECT_CALL(observer, OnSuggestionInvalidated(suggestion_id)); + provider->FireSuggestionInvalidated(suggestion_id); ExpectThatSuggestionsAre(articles_category, {11, 13}); Mock::VerifyAndClearExpectations(&observer); // Unknown IDs must be forwarded (though no change happens to the service's // internal data structures) because previously opened UIs, which can still // show the invalidated suggestion, must be notified. - std::string unknown_id = CreateSuggestion(articles_category, 1234).id(); - EXPECT_CALL(observer, OnSuggestionInvalidated(articles_category, unknown_id)); - provider->FireSuggestionInvalidated(articles_category, unknown_id); + ContentSuggestion::ID unknown_id(articles_category, "1234"); + EXPECT_CALL(observer, OnSuggestionInvalidated(unknown_id)); + provider->FireSuggestionInvalidated(unknown_id); ExpectThatSuggestionsAre(articles_category, {11, 13}); Mock::VerifyAndClearExpectations(&observer); @@ -576,10 +573,10 @@ bookmarks, CreateSuggestions(bookmarks, {1, 2})); EXPECT_THAT(service()->GetCategories(), ElementsAre(bookmarks, remote)); bookmarks_provider->FireSuggestionInvalidated( - bookmarks, CreateSuggestion(bookmarks, 1).id()); + ContentSuggestion::ID(bookmarks, "1")); EXPECT_THAT(service()->GetCategories(), ElementsAre(bookmarks, remote)); bookmarks_provider->FireSuggestionInvalidated( - bookmarks, CreateSuggestion(bookmarks, 2).id()); + ContentSuggestion::ID(bookmarks, "2")); EXPECT_THAT(service()->GetCategories(), ElementsAre(remote, bookmarks)); // Same thing, but now the bookmarks category updates "naturally".
diff --git a/components/ntp_snippets/mock_content_suggestions_provider_observer.h b/components/ntp_snippets/mock_content_suggestions_provider_observer.h index 01e273d..12bb1fd 100644 --- a/components/ntp_snippets/mock_content_suggestions_provider_observer.h +++ b/components/ntp_snippets/mock_content_suggestions_provider_observer.h
@@ -37,10 +37,9 @@ void(ContentSuggestionsProvider* provider, Category category, CategoryStatus new_status)); - MOCK_METHOD3(OnSuggestionInvalidated, + MOCK_METHOD2(OnSuggestionInvalidated, void(ContentSuggestionsProvider* provider, - Category category, - const std::string& suggestion_id)); + const ContentSuggestion::ID& suggestion_id)); }; } // namespace ntp_snippets
diff --git a/components/ntp_snippets/ntp_snippets_service.cc b/components/ntp_snippets/ntp_snippets_service.cc index c59770b..e24233b 100644 --- a/components/ntp_snippets/ntp_snippets_service.cc +++ b/components/ntp_snippets/ntp_snippets_service.cc
@@ -15,6 +15,7 @@ #include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/path_service.h" +#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/task_runner_util.h" @@ -321,39 +322,36 @@ /* show_if_empty */ true); } -void NTPSnippetsService::DismissSuggestion(const std::string& suggestion_id) { +void NTPSnippetsService::DismissSuggestion( + const ContentSuggestion::ID& suggestion_id) { if (!ready()) return; - Category category = GetCategoryFromUniqueID(suggestion_id); - std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); + DCHECK(base::ContainsKey(categories_, suggestion_id.category())); - DCHECK(categories_.find(category) != categories_.end()); - - CategoryContent* content = &categories_[category]; - auto it = - std::find_if(content->snippets.begin(), content->snippets.end(), - [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { - return snippet->id() == snippet_id; - }); + CategoryContent* content = &categories_[suggestion_id.category()]; + auto it = std::find_if( + content->snippets.begin(), content->snippets.end(), + [&suggestion_id](const std::unique_ptr<NTPSnippet>& snippet) { + return snippet->id() == suggestion_id.id_within_category(); + }); if (it == content->snippets.end()) return; (*it)->set_dismissed(true); database_->SaveSnippet(**it); - database_->DeleteImage(snippet_id); + database_->DeleteImage(suggestion_id.id_within_category()); content->dismissed.push_back(std::move(*it)); content->snippets.erase(it); } void NTPSnippetsService::FetchSuggestionImage( - const std::string& suggestion_id, + const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) { - std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); database_->LoadImage( - snippet_id, + suggestion_id.id_within_category(), base::Bind(&NTPSnippetsService::OnSnippetImageFetchedFromDatabase, base::Unretained(this), callback, suggestion_id)); } @@ -400,7 +398,7 @@ for (const std::unique_ptr<NTPSnippet>& snippet : content.dismissed) { if (!snippet->is_complete()) continue; - ContentSuggestion suggestion(MakeUniqueID(category, snippet->id()), + ContentSuggestion suggestion(category, snippet->id(), snippet->best_source().url); suggestion.set_amp_url(snippet->best_source().amp_url); suggestion.set_title(base::UTF8ToUTF16(snippet->title())); @@ -451,49 +449,38 @@ // Private methods GURL NTPSnippetsService::FindSnippetImageUrl( - Category category, - const std::string& snippet_id) const { - DCHECK(categories_.find(category) != categories_.end()); + const ContentSuggestion::ID& suggestion_id) const { + DCHECK(categories_.find(suggestion_id.category()) != categories_.end()); - const CategoryContent& content = categories_.at(category); - // Search for the snippet in current and archived snippets. - auto it = - std::find_if(content.snippets.begin(), content.snippets.end(), - [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { - return snippet->id() == snippet_id; - }); - if (it != content.snippets.end()) - return (*it)->salient_image_url(); - - it = std::find_if(content.archived.begin(), content.archived.end(), - [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { - return snippet->id() == snippet_id; - }); - if (it != content.archived.end()) - return (*it)->salient_image_url(); - - return GURL(); + const CategoryContent& content = categories_.at(suggestion_id.category()); + const NTPSnippet* snippet = + content.FindSnippet(suggestion_id.id_within_category()); + if (!snippet) + return GURL(); + return snippet->salient_image_url(); } // image_fetcher::ImageFetcherDelegate implementation. -void NTPSnippetsService::OnImageDataFetched(const std::string& suggestion_id, - const std::string& image_data) { +void NTPSnippetsService::OnImageDataFetched( + const std::string& id_within_category, + const std::string& image_data) { if (image_data.empty()) return; - Category category = GetCategoryFromUniqueID(suggestion_id); - std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); - - if (categories_.find(category) == categories_.end()) - return; - // Only save the image if the corresponding snippet still exists. - if (FindSnippetImageUrl(category, snippet_id).is_empty()) + bool found = false; + for (const std::pair<const Category, CategoryContent>& entry : categories_) { + if (entry.second.FindSnippet(id_within_category)) { + found = true; + break; + } + } + if (!found) return; // Only cache the data in the DB, the actual serving is done in the callback // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()). - database_->SaveImage(snippet_id, image_data); + database_->SaveImage(id_within_category, image_data); } void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { @@ -819,7 +806,7 @@ void NTPSnippetsService::OnSnippetImageFetchedFromDatabase( const ImageFetchedCallback& callback, - const std::string& suggestion_id, + const ContentSuggestion::ID& suggestion_id, std::string data) { // |image_decoder_| is null in tests. if (image_decoder_ && !data.empty()) { @@ -835,7 +822,7 @@ void NTPSnippetsService::OnSnippetImageDecodedFromDatabase( const ImageFetchedCallback& callback, - const std::string& suggestion_id, + const ContentSuggestion::ID& suggestion_id, const gfx::Image& image) { if (!image.IsEmpty()) { callback.Run(image); @@ -843,24 +830,21 @@ } // If decoding the image failed, delete the DB entry. - std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); - database_->DeleteImage(snippet_id); + database_->DeleteImage(suggestion_id.id_within_category()); FetchSnippetImageFromNetwork(suggestion_id, callback); } void NTPSnippetsService::FetchSnippetImageFromNetwork( - const std::string& suggestion_id, + const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) { - Category category = GetCategoryFromUniqueID(suggestion_id); - std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id); - - if (categories_.find(category) == categories_.end()) { - OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); + if (categories_.find(suggestion_id.category()) == categories_.end()) { + OnSnippetImageDecodedFromNetwork( + callback, suggestion_id.id_within_category(), gfx::Image()); return; } - GURL image_url = FindSnippetImageUrl(category, snippet_id); + GURL image_url = FindSnippetImageUrl(suggestion_id); if (image_url.is_empty() || !thumbnail_requests_throttler_.DemandQuotaForRequest( @@ -868,19 +852,20 @@ // Return an empty image. Directly, this is never synchronous with the // original FetchSuggestionImage() call - an asynchronous database query has // happened in the meantime. - OnSnippetImageDecodedFromNetwork(callback, suggestion_id, gfx::Image()); + OnSnippetImageDecodedFromNetwork( + callback, suggestion_id.id_within_category(), gfx::Image()); return; } image_fetcher_->StartOrQueueNetworkRequest( - suggestion_id, image_url, + suggestion_id.id_within_category(), image_url, base::Bind(&NTPSnippetsService::OnSnippetImageDecodedFromNetwork, base::Unretained(this), callback)); } void NTPSnippetsService::OnSnippetImageDecodedFromNetwork( const ImageFetchedCallback& callback, - const std::string& suggestion_id, + const std::string& id_within_category, const gfx::Image& image) { callback.Run(image); } @@ -1026,7 +1011,7 @@ // incomplete ones we kept. if (!snippet->is_complete()) continue; - ContentSuggestion suggestion(MakeUniqueID(category, snippet->id()), + ContentSuggestion suggestion(category, snippet->id(), snippet->best_source().url); suggestion.set_amp_url(snippet->best_source().amp_url); suggestion.set_title(base::UTF8ToUTF16(snippet->title())); @@ -1064,6 +1049,28 @@ } } +const NTPSnippet* NTPSnippetsService::CategoryContent::FindSnippet( + const std::string& id_within_category) const { + // Search for the snippet in current and archived snippets. + auto it = std::find_if( + snippets.begin(), snippets.end(), + [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { + return snippet->id() == id_within_category; + }); + if (it != snippets.end()) + return it->get(); + + it = std::find_if( + archived.begin(), archived.end(), + [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) { + return snippet->id() == id_within_category; + }); + if (it != archived.end()) + return it->get(); + + return nullptr; +} + NTPSnippetsService::CategoryContent::CategoryContent() = default; NTPSnippetsService::CategoryContent::CategoryContent(CategoryContent&&) = default;
diff --git a/components/ntp_snippets/ntp_snippets_service.h b/components/ntp_snippets/ntp_snippets_service.h index 1aac5ec6..d3f41e5 100644 --- a/components/ntp_snippets/ntp_snippets_service.h +++ b/components/ntp_snippets/ntp_snippets_service.h
@@ -118,8 +118,8 @@ // ContentSuggestionsProvider implementation CategoryStatus GetCategoryStatus(Category category) override; CategoryInfo GetCategoryInfo(Category category) override; - void DismissSuggestion(const std::string& suggestion_id) override; - void FetchSuggestionImage(const std::string& suggestion_id, + void DismissSuggestion(const ContentSuggestion::ID& suggestion_id) override; + void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) override; void ClearHistory( base::Time begin, @@ -199,12 +199,12 @@ }; // Returns the URL of the image of a snippet if it is among the current or - // among the archived snippets in |category|. Returns an empty URL, otherwise. - GURL FindSnippetImageUrl(Category category, - const std::string& snippet_id) const; + // among the archived snippets in the matching category. Returns an empty URL + // otherwise. + GURL FindSnippetImageUrl(const ContentSuggestion::ID& suggestion_id) const; // image_fetcher::ImageFetcherDelegate implementation. - void OnImageDataFetched(const std::string& suggestion_id, + void OnImageDataFetched(const std::string& id_within_category, const std::string& image_data) override; // Callbacks for the NTPSnippetsDatabase. @@ -242,19 +242,21 @@ // observers. This is done after construction, once the database is loaded. void FinishInitialization(); - void OnSnippetImageFetchedFromDatabase(const ImageFetchedCallback& callback, - const std::string& suggestion_id, - std::string data); + void OnSnippetImageFetchedFromDatabase( + const ImageFetchedCallback& callback, + const ContentSuggestion::ID& suggestion_id, + std::string data); - void OnSnippetImageDecodedFromDatabase(const ImageFetchedCallback& callback, - const std::string& suggestion_id, - const gfx::Image& image); + void OnSnippetImageDecodedFromDatabase( + const ImageFetchedCallback& callback, + const ContentSuggestion::ID& suggestion_id, + const gfx::Image& image); - void FetchSnippetImageFromNetwork(const std::string& suggestion_id, + void FetchSnippetImageFromNetwork(const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback); void OnSnippetImageDecodedFromNetwork(const ImageFetchedCallback& callback, - const std::string& suggestion_id, + const std::string& id_within_category, const gfx::Image& image); // Triggers a state transition depending on the provided reason to be @@ -320,6 +322,10 @@ // expire so we won't re-add them to |snippets| on the next fetch. NTPSnippet::PtrVector dismissed; + // Returns a non-dismissed snippet with the given |id_within_category|, or + // null if none exist. + const NTPSnippet* FindSnippet(const std::string& id_within_category) const; + CategoryContent(); CategoryContent(CategoryContent&&); ~CategoryContent();
diff --git a/components/ntp_snippets/ntp_snippets_service_unittest.cc b/components/ntp_snippets/ntp_snippets_service_unittest.cc index d9708da..a1dec51 100644 --- a/components/ntp_snippets/ntp_snippets_service_unittest.cc +++ b/components/ntp_snippets/ntp_snippets_service_unittest.cc
@@ -299,9 +299,9 @@ statuses_[category] = new_status; } - void OnSuggestionInvalidated(ContentSuggestionsProvider* provider, - Category category, - const std::string& suggestion_id) override {} + void OnSuggestionInvalidated( + ContentSuggestionsProvider* provider, + const ContentSuggestion::ID& suggestion_id) override {} const std::map<Category, CategoryStatus, Category::CompareByID>& statuses() const { @@ -423,18 +423,16 @@ *service = MakeSnippetsService(); } - std::string MakeArticleID(const NTPSnippetsService& service, - const std::string& within_category_id) { - return service.MakeUniqueID(articles_category(), within_category_id); + ContentSuggestion::ID MakeArticleID(const std::string& id_within_category) { + return ContentSuggestion::ID(articles_category(), id_within_category); } Category articles_category() { return category_factory_.FromKnownCategory(KnownCategories::ARTICLES); } - std::string MakeOtherID(const NTPSnippetsService& service, - const std::string& within_category_id) { - return service.MakeUniqueID(other_category(), within_category_id); + ContentSuggestion::ID MakeOtherID(const std::string& id_within_category) { + return ContentSuggestion::ID(other_category(), id_within_category); } Category other_category() { return category_factory_.FromRemoteCategory(2); } @@ -610,7 +608,7 @@ const ContentSuggestion& suggestion = observer().SuggestionsForCategory(articles_category()).front(); - EXPECT_EQ(MakeArticleID(*service, kSnippetUrl), suggestion.id()); + EXPECT_EQ(MakeArticleID(kSnippetUrl), suggestion.id()); EXPECT_EQ(kSnippetTitle, base::UTF16ToUTF8(suggestion.title())); EXPECT_EQ(kSnippetText, base::UTF16ToUTF8(suggestion.snippet_text())); EXPECT_EQ(GetDefaultCreationTime(), suggestion.publish_date()); @@ -644,8 +642,7 @@ { const ContentSuggestion& suggestion = observer().SuggestionsForCategory(articles_category()).front(); - EXPECT_EQ(MakeArticleID(*service, std::string(kSnippetUrl) + "/0"), - suggestion.id()); + EXPECT_EQ(MakeArticleID(std::string(kSnippetUrl) + "/0"), suggestion.id()); EXPECT_EQ(kSnippetTitle, base::UTF16ToUTF8(suggestion.title())); EXPECT_EQ(kSnippetText, base::UTF16ToUTF8(suggestion.snippet_text())); EXPECT_EQ(GetDefaultCreationTime(), suggestion.publish_date()); @@ -657,8 +654,7 @@ { const ContentSuggestion& suggestion = observer().SuggestionsForCategory(other_category()).front(); - EXPECT_EQ(MakeOtherID(*service, std::string(kSnippetUrl) + "/1"), - suggestion.id()); + EXPECT_EQ(MakeOtherID(std::string(kSnippetUrl) + "/1"), suggestion.id()); EXPECT_EQ(kSnippetTitle, base::UTF16ToUTF8(suggestion.title())); EXPECT_EQ(kSnippetText, base::UTF16ToUTF8(suggestion.snippet_text())); EXPECT_EQ(GetDefaultCreationTime(), suggestion.publish_date()); @@ -751,11 +747,11 @@ ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1)); // Dismissing a non-existent snippet shouldn't do anything. - service->DismissSuggestion(MakeArticleID(*service, "http://othersite.com")); + service->DismissSuggestion(MakeArticleID("http://othersite.com")); EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1)); // Dismiss the snippet. - service->DismissSuggestion(MakeArticleID(*service, kSnippetUrl)); + service->DismissSuggestion(MakeArticleID(kSnippetUrl)); EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty()); // Make sure that fetching the same snippet again does not re-add it. @@ -779,7 +775,7 @@ LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); - service->DismissSuggestion(MakeArticleID(*service, kSnippetUrl)); + service->DismissSuggestion(MakeArticleID(kSnippetUrl)); service->GetDismissedSuggestionsForDebugging( articles_category(), @@ -788,8 +784,7 @@ std::vector<ContentSuggestion> dismissed_suggestions) { EXPECT_EQ(1u, dismissed_suggestions.size()); for (auto& suggestion : dismissed_suggestions) { - EXPECT_EQ(test->MakeArticleID(*service, kSnippetUrl), - suggestion.id()); + EXPECT_EQ(test->MakeArticleID(kSnippetUrl), suggestion.id()); } }, service.get(), this)); @@ -829,7 +824,7 @@ LoadFromJSONString(service.get(), json_str1); // Dismiss the suggestion service->DismissSuggestion( - service->MakeUniqueID(service->articles_category_, kSnippetUrl)); + ContentSuggestion::ID(articles_category(), kSnippetUrl)); // Load a different snippet - this will clear the expired dismissed ones. std::string json_str2(GetTestJson({GetSnippetWithUrl(kSnippetUrl2)})); @@ -928,7 +923,7 @@ // Dismissing a snippet should decrease the list size. This will only be // logged after the next fetch. - service->DismissSuggestion(MakeArticleID(*service, kSnippetUrl)); + service->DismissSuggestion(MakeArticleID(kSnippetUrl)); LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), ElementsAre(base::Bucket(/*min=*/0, /*count=*/3), @@ -982,7 +977,7 @@ publishers[0], amp_urls[0])})); ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1)); // Dismiss the snippet via the mashable source corpus ID. - service->DismissSuggestion(MakeArticleID(*service, source_urls[0])); + service->DismissSuggestion(MakeArticleID(source_urls[0])); EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty()); // The same article from the AOL domain should now be detected as dismissed. @@ -1035,7 +1030,7 @@ } service->FetchSuggestionImage( - MakeArticleID(*service, kSnippetUrl), + MakeArticleID(kSnippetUrl), base::Bind(&MockFunction<void(const gfx::Image&)>::Call, base::Unretained(&image_fetched))); base::RunLoop().RunUntilIdle(); @@ -1052,7 +1047,7 @@ EXPECT_CALL(image_fetched, Call(_)).WillOnce(SaveArg<0>(&image)); service->FetchSuggestionImage( - MakeArticleID(*service, kSnippetUrl2), + MakeArticleID(kSnippetUrl2), base::Bind(&MockFunction<void(const gfx::Image&)>::Call, base::Unretained(&image_fetched))); @@ -1069,7 +1064,7 @@ LoadFromJSONString(service.get(), json_str); ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(2)); - service->DismissSuggestion(MakeArticleID(*service, "http://url1.com")); + service->DismissSuggestion(MakeArticleID("http://url1.com")); ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1)); ASSERT_THAT(service->GetDismissedSnippetsForTesting(articles_category()), SizeIs(1));
diff --git a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc index 8d489a65..d1525ba4f 100644 --- a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc +++ b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc
@@ -135,16 +135,15 @@ } void OfflinePageSuggestionsProvider::DismissSuggestion( - const std::string& suggestion_id) { - Category category = GetCategoryFromUniqueID(suggestion_id); - std::string offline_page_id = GetWithinCategoryIDFromUniqueID(suggestion_id); - std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs(category); - dismissed_ids.insert(offline_page_id); - StoreDismissedIDsToPrefs(category, dismissed_ids); + const ContentSuggestion::ID& suggestion_id) { + std::set<std::string> dismissed_ids = + ReadDismissedIDsFromPrefs(suggestion_id.category()); + dismissed_ids.insert(suggestion_id.id_within_category()); + StoreDismissedIDsToPrefs(suggestion_id.category(), dismissed_ids); } void OfflinePageSuggestionsProvider::FetchSuggestionImage( - const std::string& suggestion_id, + const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) { // TODO(pke): Fetch proper thumbnail from OfflinePageModel once it's available // there. @@ -314,9 +313,9 @@ // TODO(pke): Make sure the URL is actually opened as an offline URL. // Currently, the browser opens the offline URL and then immediately // redirects to the online URL if the device is online. - ContentSuggestion suggestion( - MakeUniqueID(category, base::IntToString(offline_page.offline_id)), - offline_page.GetOfflineURL()); + ContentSuggestion suggestion(category, + base::IntToString(offline_page.offline_id), + offline_page.GetOfflineURL()); if (offline_page.title.empty()) { // TODO(pke): Remove this fallback once the OfflinePageModel provides titles @@ -348,8 +347,8 @@ void OfflinePageSuggestionsProvider::InvalidateSuggestion(Category category, int64_t offline_id) { std::string offline_page_id = base::IntToString(offline_id); - observer()->OnSuggestionInvalidated(this, category, - MakeUniqueID(category, offline_page_id)); + observer()->OnSuggestionInvalidated( + this, ContentSuggestion::ID(category, offline_page_id)); std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs(category); auto it = dismissed_ids.find(offline_page_id);
diff --git a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h index 19a058a5..41d421e 100644 --- a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h +++ b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h
@@ -52,8 +52,8 @@ // ContentSuggestionsProvider implementation. CategoryStatus GetCategoryStatus(Category category) override; CategoryInfo GetCategoryInfo(Category category) override; - void DismissSuggestion(const std::string& suggestion_id) override; - void FetchSuggestionImage(const std::string& suggestion_id, + void DismissSuggestion(const ContentSuggestion::ID& suggestion_id) override; + void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) override; void ClearHistory( base::Time begin,
diff --git a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider_unittest.cc b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider_unittest.cc index 039ac5b..4ad0254f 100644 --- a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider_unittest.cc +++ b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider_unittest.cc
@@ -134,8 +134,8 @@ return category_factory_.FromKnownCategory(KnownCategories::DOWNLOADS); } - std::string GetDummySuggestionId(Category category, int id) { - return provider_->MakeUniqueID(category, base::IntToString(id)); + ContentSuggestion::ID GetDummySuggestionId(Category category, int id) { + return ContentSuggestion::ID(category, base::IntToString(id)); } ContentSuggestion CreateDummySuggestion(Category category, int id) { @@ -348,10 +348,9 @@ FireOfflinePageModelChanged(); // Invalidation of suggestion 2 should be forwarded. - EXPECT_CALL( - *observer(), - OnSuggestionInvalidated(_, recent_tabs_category(), - GetDummySuggestionId(recent_tabs_category(), 2))); + EXPECT_CALL(*observer(), + OnSuggestionInvalidated( + _, GetDummySuggestionId(recent_tabs_category(), 2))); FireOfflinePageDeleted(model()->items().at(1)); }
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc index 22dd7ed..b87f20e 100644 --- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc +++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
@@ -45,9 +45,8 @@ for (const UrlInfo& url_info : urls) { if (suggestions.size() >= kMaxSuggestionsCount) break; - ContentSuggestion suggestion( - MakeUniqueID(provided_category_, url_info.site_url.spec()), - url_info.site_url); + ContentSuggestion suggestion(provided_category_, url_info.site_url.spec(), + url_info.site_url); suggestion.set_title(base::UTF8ToUTF16(url_info.title)); suggestion.set_snippet_text(base::UTF8ToUTF16(url_info.description)); @@ -75,13 +74,14 @@ } void PhysicalWebPageSuggestionsProvider::DismissSuggestion( - const std::string& suggestion_id) { + const ContentSuggestion::ID& suggestion_id) { // TODO(vitaliii): Implement this and then // ClearDismissedSuggestionsForDebugging. } void PhysicalWebPageSuggestionsProvider::FetchSuggestionImage( - const std::string& suggestion_id, const ImageFetchedCallback& callback) { + const ContentSuggestion::ID& suggestion_id, + const ImageFetchedCallback& callback) { // TODO(vitaliii): Implement. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, gfx::Image()));
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h index b2c7431a..934dd74 100644 --- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h +++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h
@@ -45,8 +45,8 @@ // ContentSuggestionsProvider implementation. CategoryStatus GetCategoryStatus(Category category) override; CategoryInfo GetCategoryInfo(Category category) override; - void DismissSuggestion(const std::string& suggestion_id) override; - void FetchSuggestionImage(const std::string& suggestion_id, + void DismissSuggestion(const ContentSuggestion::ID& suggestion_id) override; + void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) override; void ClearHistory( base::Time begin,
diff --git a/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.cc b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.cc index 0a6aaa7..f913d66 100644 --- a/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.cc +++ b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.cc
@@ -125,21 +125,21 @@ } void ForeignSessionsSuggestionsProvider::DismissSuggestion( - const std::string& suggestion_id) { + const ContentSuggestion::ID& suggestion_id) { // TODO(skym): Right now this continuously grows, without clearing out old and // irrelevant entries. Could either use a timestamp and expire after a // threshold, or compare with current foreign tabs and remove anything that // isn't actively blockign a foreign_sessions tab. std::set<std::string> dismissed_ids = prefs::ReadDismissedIDsFromPrefs( *pref_service_, prefs::kDismissedForeignSessionsSuggestions); - dismissed_ids.insert(suggestion_id); + dismissed_ids.insert(suggestion_id.id_within_category()); prefs::StoreDismissedIDsToPrefs(pref_service_, prefs::kDismissedForeignSessionsSuggestions, dismissed_ids); } void ForeignSessionsSuggestionsProvider::FetchSuggestionImage( - const std::string& suggestion_id, + const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, gfx::Image())); @@ -152,7 +152,7 @@ std::set<std::string> dismissed_ids = prefs::ReadDismissedIDsFromPrefs( *pref_service_, prefs::kDismissedForeignSessionsSuggestions); for (auto iter = dismissed_ids.begin(); iter != dismissed_ids.end();) { - if (filter.Run(GURL(base::StringPiece(*iter)))) { + if (filter.Run(GURL(*iter))) { iter = dismissed_ids.erase(iter); } else { ++iter; @@ -277,13 +277,12 @@ continue; const SerializedNavigationEntry& navigation = tab->navigations.back(); - const std::string unique_id = - MakeUniqueID(provided_category_, navigation.virtual_url().spec()); + const std::string id = navigation.virtual_url().spec(); // TODO(skym): Filter out internal pages. Tabs that contain only // non-syncable content should never reach the local client, but // sometimes the most recent navigation may be internal while one // of the previous ones was more valid. - if (dismissed_ids.find(unique_id) == dismissed_ids.end() && + if (dismissed_ids.find(id) == dismissed_ids.end() && (base::Time::Now() - tab->timestamp) < max_foreign_tab_age) { suggestion_candidates.push_back( SessionData{session, tab.get(), &navigation}); @@ -296,9 +295,9 @@ ContentSuggestion ForeignSessionsSuggestionsProvider::BuildSuggestion( const SessionData& data) { - ContentSuggestion suggestion( - MakeUniqueID(provided_category_, data.navigation->virtual_url().spec()), - data.navigation->virtual_url()); + ContentSuggestion suggestion(provided_category_, + data.navigation->virtual_url().spec(), + data.navigation->virtual_url()); suggestion.set_title(data.navigation->title()); suggestion.set_publish_date(data.tab->timestamp); // TODO(skym): It's unclear if this simple approach is sufficient for
diff --git a/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h index bb9540b9..4d21a3f 100644 --- a/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h +++ b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h
@@ -54,8 +54,8 @@ // ContentSuggestionsProvider implementation. CategoryStatus GetCategoryStatus(Category category) override; CategoryInfo GetCategoryInfo(Category category) override; - void DismissSuggestion(const std::string& suggestion_id) override; - void FetchSuggestionImage(const std::string& suggestion_id, + void DismissSuggestion(const ContentSuggestion::ID& suggestion_id) override; + void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, const ImageFetchedCallback& callback) override; void ClearHistory( base::Time begin,
diff --git a/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc index 202c0398..e36cf23 100644 --- a/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc +++ b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc
@@ -149,8 +149,8 @@ } void Dismiss(const std::string& url) { - // The url of a given suggestion is used as the |within_category_id|. - provider_->DismissSuggestion(provider_->MakeUniqueID(category(), url)); + // The url of a given suggestion is used as the |id_within_category|. + provider_->DismissSuggestion(ContentSuggestion::ID(category(), url)); } Category category() {
diff --git a/components/safe_browsing_db/database_manager.cc b/components/safe_browsing_db/database_manager.cc index a882409a..91e4d7b6 100644 --- a/components/safe_browsing_db/database_manager.cc +++ b/components/safe_browsing_db/database_manager.cc
@@ -67,9 +67,8 @@ return api_checks_.end(); } -std::unordered_set<ListIdentifier> -SafeBrowsingDatabaseManager::GetStoresForFullHashRequests() { - return std::unordered_set<ListIdentifier>({GetChromeUrlApiId()}); +StoresToCheck SafeBrowsingDatabaseManager::GetStoresForFullHashRequests() { + return StoresToCheck({GetChromeUrlApiId()}); } void SafeBrowsingDatabaseManager::OnThreatMetadataResponse(
diff --git a/components/safe_browsing_db/database_manager.h b/components/safe_browsing_db/database_manager.h index 687c7be..0d24b8bf 100644 --- a/components/safe_browsing_db/database_manager.h +++ b/components/safe_browsing_db/database_manager.h
@@ -172,7 +172,7 @@ // // Returns the lists that this DatabaseManager should get full hashes for. - virtual std::unordered_set<ListIdentifier> GetStoresForFullHashRequests(); + virtual StoresToCheck GetStoresForFullHashRequests(); // Returns the ThreatSource for this implementation. virtual ThreatSource GetThreatSource() const = 0;
diff --git a/components/safe_browsing_db/v4_database.cc b/components/safe_browsing_db/v4_database.cc index 532475b1..efcbe7b 100644 --- a/components/safe_browsing_db/v4_database.cc +++ b/components/safe_browsing_db/v4_database.cc
@@ -174,11 +174,11 @@ void V4Database::GetStoresMatchingFullHash( const FullHash& full_hash, - const std::unordered_set<ListIdentifier>& stores_to_look, + const StoresToCheck& stores_to_check, StoreAndHashPrefixes* matched_store_and_hash_prefixes) { DCHECK_CURRENTLY_ON(BrowserThread::IO); matched_store_and_hash_prefixes->clear(); - for (const ListIdentifier& identifier : stores_to_look) { + for (const ListIdentifier& identifier : stores_to_check) { const auto& store_pair = store_map_->find(identifier); DCHECK(store_pair != store_map_->end()); const std::unique_ptr<V4Store>& store = store_pair->second;
diff --git a/components/safe_browsing_db/v4_database.h b/components/safe_browsing_db/v4_database.h index 6b41731..bddba598 100644 --- a/components/safe_browsing_db/v4_database.h +++ b/components/safe_browsing_db/v4_database.h
@@ -109,11 +109,11 @@ std::unique_ptr<StoreStateMap> GetStoreStateMap(); // Searches for a hash prefix matching the |full_hash| in stores in the - // database, filtered by |stores_to_look|, and returns the identifier of the + // database, filtered by |stores_to_check|, and returns the identifier of the // store along with the matching hash prefix in |matched_hash_prefix_map|. virtual void GetStoresMatchingFullHash( const FullHash& full_hash, - const std::unordered_set<ListIdentifier>& stores_to_look, + const StoresToCheck& stores_to_check, StoreAndHashPrefixes* matched_store_and_full_hashes); // Deletes the current database and creates a new one.
diff --git a/components/safe_browsing_db/v4_database_unittest.cc b/components/safe_browsing_db/v4_database_unittest.cc index e16f182..2ce05ee 100644 --- a/components/safe_browsing_db/v4_database_unittest.cc +++ b/components/safe_browsing_db/v4_database_unittest.cc
@@ -380,17 +380,16 @@ base::RunLoop().RunUntilIdle(); EXPECT_EQ(true, created_and_called_back_); - std::unordered_set<ListIdentifier> stores_to_look( - {linux_malware_id_, win_malware_id_}); + StoresToCheck stores_to_check({linux_malware_id_, win_malware_id_}); StoreAndHashPrefixes store_and_hash_prefixes; - v4_database_->GetStoresMatchingFullHash("anything", stores_to_look, + v4_database_->GetStoresMatchingFullHash("anything", stores_to_check, &store_and_hash_prefixes); EXPECT_EQ(2u, store_and_hash_prefixes.size()); - std::unordered_set<ListIdentifier> stores_found; + StoresToCheck stores_found; for (const auto& it : store_and_hash_prefixes) { stores_found.insert(it.list_id); } - EXPECT_EQ(stores_to_look, stores_found); + EXPECT_EQ(stores_to_check, stores_found); } // Test to ensure the case that no stores match a given full hash. @@ -407,11 +406,10 @@ base::RunLoop().RunUntilIdle(); EXPECT_EQ(true, created_and_called_back_); - std::unordered_set<ListIdentifier> stores_to_look( - {linux_malware_id_, win_malware_id_}); StoreAndHashPrefixes store_and_hash_prefixes; - v4_database_->GetStoresMatchingFullHash("anything", stores_to_look, - &store_and_hash_prefixes); + v4_database_->GetStoresMatchingFullHash( + "anything", StoresToCheck({linux_malware_id_, win_malware_id_}), + &store_and_hash_prefixes); EXPECT_TRUE(store_and_hash_prefixes.empty()); } @@ -435,18 +433,17 @@ v4_database_->store_map_->at(win_malware_id_).get()); store->set_hash_prefix_matches(true); - std::unordered_set<ListIdentifier> stores_to_look( - {linux_malware_id_, win_malware_id_}); StoreAndHashPrefixes store_and_hash_prefixes; - v4_database_->GetStoresMatchingFullHash("anything", stores_to_look, - &store_and_hash_prefixes); + v4_database_->GetStoresMatchingFullHash( + "anything", StoresToCheck({linux_malware_id_, win_malware_id_}), + &store_and_hash_prefixes); EXPECT_EQ(1u, store_and_hash_prefixes.size()); EXPECT_EQ(store_and_hash_prefixes.begin()->list_id, win_malware_id_); EXPECT_FALSE(store_and_hash_prefixes.begin()->hash_prefix.empty()); } // Test to ensure the case that only some stores are reported to match a given -// full hash because of stores_to_look. +// full hash because of StoresToCheck. TEST_F(V4DatabaseTest, TestSomeStoresMatchFullHashBecauseOfStoresToMatch) { // Setup all stores to match the full hash. bool hash_prefix_matches = true; @@ -461,11 +458,10 @@ base::RunLoop().RunUntilIdle(); EXPECT_EQ(true, created_and_called_back_); - std::unordered_set<ListIdentifier> stores_to_look({linux_malware_id_}); - // Don't add win_malware_id_ to the stores_to_look. + // Don't add win_malware_id_ to the StoresToCheck. StoreAndHashPrefixes store_and_hash_prefixes; - v4_database_->GetStoresMatchingFullHash("anything", stores_to_look, - &store_and_hash_prefixes); + v4_database_->GetStoresMatchingFullHash( + "anything", StoresToCheck({linux_malware_id_}), &store_and_hash_prefixes); EXPECT_EQ(1u, store_and_hash_prefixes.size()); EXPECT_EQ(store_and_hash_prefixes.begin()->list_id, linux_malware_id_); EXPECT_FALSE(store_and_hash_prefixes.begin()->hash_prefix.empty());
diff --git a/components/safe_browsing_db/v4_get_hash_protocol_manager.cc b/components/safe_browsing_db/v4_get_hash_protocol_manager.cc index dcd03e7..5994c4f 100644 --- a/components/safe_browsing_db/v4_get_hash_protocol_manager.cc +++ b/components/safe_browsing_db/v4_get_hash_protocol_manager.cc
@@ -142,10 +142,10 @@ ~V4GetHashProtocolManagerFactoryImpl() override {} std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager( net::URLRequestContextGetter* request_context_getter, - const std::unordered_set<ListIdentifier>& stores_to_request, + const StoresToCheck& stores_to_check, const V4ProtocolConfig& config) override { return base::WrapUnique(new V4GetHashProtocolManager( - request_context_getter, stores_to_request, config)); + request_context_getter, stores_to_check, config)); } private: @@ -211,12 +211,12 @@ // static std::unique_ptr<V4GetHashProtocolManager> V4GetHashProtocolManager::Create( net::URLRequestContextGetter* request_context_getter, - const std::unordered_set<ListIdentifier>& stores_to_request, + const StoresToCheck& stores_to_check, const V4ProtocolConfig& config) { if (!factory_) factory_ = new V4GetHashProtocolManagerFactoryImpl(); return factory_->CreateProtocolManager(request_context_getter, - stores_to_request, config); + stores_to_check, config); } // static @@ -229,7 +229,7 @@ V4GetHashProtocolManager::V4GetHashProtocolManager( net::URLRequestContextGetter* request_context_getter, - const std::unordered_set<ListIdentifier>& stores_to_request, + const StoresToCheck& stores_to_check, const V4ProtocolConfig& config) : gethash_error_count_(0), gethash_back_off_mult_(1), @@ -238,8 +238,8 @@ request_context_getter_(request_context_getter), url_fetcher_id_(0), clock_(new base::DefaultClock()) { - DCHECK(!stores_to_request.empty()); - for (const ListIdentifier& store : stores_to_request) { + DCHECK(!stores_to_check.empty()); + for (const ListIdentifier& store : stores_to_check) { platform_types_.insert(store.platform_type()); threat_entry_types_.insert(store.threat_entry_type()); threat_types_.insert(store.threat_type());
diff --git a/components/safe_browsing_db/v4_get_hash_protocol_manager.h b/components/safe_browsing_db/v4_get_hash_protocol_manager.h index 29f49c1..44b05d7 100644 --- a/components/safe_browsing_db/v4_get_hash_protocol_manager.h +++ b/components/safe_browsing_db/v4_get_hash_protocol_manager.h
@@ -147,7 +147,7 @@ // Create an instance of the safe browsing v4 protocol manager. static std::unique_ptr<V4GetHashProtocolManager> Create( net::URLRequestContextGetter* request_context_getter, - const std::unordered_set<ListIdentifier>& stores_to_request, + const StoresToCheck& stores_to_check, const V4ProtocolConfig& config); // Makes the passed |factory| the factory used to instantiate @@ -183,12 +183,11 @@ void OnURLFetchComplete(const net::URLFetcher* source) override; protected: - // Constructs a V4GetHashProtocolManager that issues - // network requests using |request_context_getter|. - V4GetHashProtocolManager( - net::URLRequestContextGetter* request_context_getter, - const std::unordered_set<ListIdentifier>& stores_to_request, - const V4ProtocolConfig& config); + // Constructs a V4GetHashProtocolManager that issues network requests using + // |request_context_getter|. + V4GetHashProtocolManager(net::URLRequestContextGetter* request_context_getter, + const StoresToCheck& stores_to_check, + const V4ProtocolConfig& config); private: FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestGetHashRequest); @@ -345,7 +344,7 @@ virtual ~V4GetHashProtocolManagerFactory() {} virtual std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager( net::URLRequestContextGetter* request_context_getter, - const std::unordered_set<ListIdentifier>& stores_to_request, + const StoresToCheck& stores_to_check, const V4ProtocolConfig& config) = 0; private:
diff --git a/components/safe_browsing_db/v4_get_hash_protocol_manager_unittest.cc b/components/safe_browsing_db/v4_get_hash_protocol_manager_unittest.cc index c8b0542e..bb89fd9 100644 --- a/components/safe_browsing_db/v4_get_hash_protocol_manager_unittest.cc +++ b/components/safe_browsing_db/v4_get_hash_protocol_manager_unittest.cc
@@ -80,9 +80,8 @@ config.client_name = kClient; config.version = kAppVer; config.key_param = kKeyParam; - std::unordered_set<ListIdentifier> stores_to_look( - {GetUrlMalwareId(), GetChromeUrlApiId()}); - return V4GetHashProtocolManager::Create(NULL, stores_to_look, config); + StoresToCheck stores_to_check({GetUrlMalwareId(), GetChromeUrlApiId()}); + return V4GetHashProtocolManager::Create(NULL, stores_to_check, config); } static void SetupFetcherToReturnOKResponse(
diff --git a/components/safe_browsing_db/v4_local_database_manager.cc b/components/safe_browsing_db/v4_local_database_manager.cc index 6d46cde..9215dc7 100644 --- a/components/safe_browsing_db/v4_local_database_manager.cc +++ b/components/safe_browsing_db/v4_local_database_manager.cc
@@ -52,11 +52,15 @@ V4LocalDatabaseManager::PendingCheck::PendingCheck( Client* client, ClientCallbackType client_callback_type, + const StoresToCheck& stores_to_check, const GURL& url) : client(client), client_callback_type(client_callback_type), result_threat_type(SB_THREAT_TYPE_SAFE), - url(url) {} + stores_to_check(stores_to_check), + url(url) { + DCHECK_GT(ClientCallbackType::CHECK_MAX, client_callback_type); +} V4LocalDatabaseManager::PendingCheck::~PendingCheck() {} @@ -86,7 +90,14 @@ pending_clients_.erase(it); } - // TODO(vakh): Handle the case of queued checks. + auto queued_it = + std::find_if(std::begin(queued_checks_), std::end(queued_checks_), + [&client](const std::unique_ptr<PendingCheck>& check) { + return check->client == client; + }); + if (queued_it != queued_checks_.end()) { + queued_checks_.erase(queued_it); + } } bool V4LocalDatabaseManager::CanCheckResourceType( @@ -111,47 +122,21 @@ return true; } - if (v4_database_) { - std::unordered_set<FullHash> full_hashes; - V4ProtocolManagerUtil::UrlToFullHashes(url, &full_hashes); - - std::unordered_set<ListIdentifier> stores_to_look( - {GetUrlMalwareId(), GetUrlSocEngId()}); - std::unordered_set<HashPrefix> matched_hash_prefixes; - std::unordered_set<ListIdentifier> matched_stores; - StoreAndHashPrefixes matched_store_and_hash_prefixes; - FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes; - for (const auto& full_hash : full_hashes) { - matched_store_and_hash_prefixes.clear(); - v4_database_->GetStoresMatchingFullHash(full_hash, stores_to_look, - &matched_store_and_hash_prefixes); - if (!matched_store_and_hash_prefixes.empty()) { - full_hash_to_store_and_hash_prefixes[full_hash] = - matched_store_and_hash_prefixes; - } - } - - if (full_hash_to_store_and_hash_prefixes.empty()) { - return true; - } else { - std::unique_ptr<PendingCheck> pending_check = - base::MakeUnique<PendingCheck>( - client, ClientCallbackType::CHECK_BROWSE_URL, url); - - pending_clients_.insert(client); - - v4_get_hash_protocol_manager_->GetFullHashes( - full_hash_to_store_and_hash_prefixes, - base::Bind(&V4LocalDatabaseManager::OnFullHashResponse, - base::Unretained(this), base::Passed(&pending_check))); - - return false; - } - } else { - // TODO(vakh): Queue the check and process it when the database becomes - // ready. + std::unique_ptr<PendingCheck> check = base::MakeUnique<PendingCheck>( + client, ClientCallbackType::CHECK_BROWSE_URL, + StoresToCheck({GetUrlMalwareId(), GetUrlSocEngId()}), url); + if (!v4_database_) { + queued_checks_.push_back(std::move(check)); return false; } + + FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes; + if (!GetPrefixMatches(check, &full_hash_to_store_and_hash_prefixes)) { + return true; + } + + PerformFullHashCheck(std::move(check), full_hash_to_store_and_hash_prefixes); + return false; } bool V4LocalDatabaseManager::CheckDownloadUrl( @@ -255,6 +240,8 @@ pending_clients_.clear(); + RespondSafeToQueuedChecks(); + // Delete the V4Database. Any pending writes to disk are completed. // This operation happens on the task_runner on which v4_database_ operates // and doesn't block the IO thread. @@ -285,6 +272,8 @@ // The database is in place. Start fetching updates now. v4_update_protocol_manager_->ScheduleNextUpdate( v4_database_->GetStoreStateMap()); + + ProcessQueuedChecks(); } else { // Schedule the deletion of v4_database off IO thread. V4Database::Destroy(std::move(v4_database)); @@ -298,6 +287,39 @@ } } +bool V4LocalDatabaseManager::GetPrefixMatches( + const std::unique_ptr<PendingCheck>& check, + FullHashToStoreAndHashPrefixesMap* full_hash_to_store_and_hash_prefixes) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + DCHECK(enabled_); + DCHECK(v4_database_); + DCHECK_GT(ClientCallbackType::CHECK_MAX, check->client_callback_type); + + if (check->client_callback_type == ClientCallbackType::CHECK_BROWSE_URL) { + std::unordered_set<FullHash> full_hashes; + V4ProtocolManagerUtil::UrlToFullHashes(check->url, &full_hashes); + + StoreAndHashPrefixes matched_store_and_hash_prefixes; + for (const auto& full_hash : full_hashes) { + matched_store_and_hash_prefixes.clear(); + v4_database_->GetStoresMatchingFullHash(full_hash, check->stores_to_check, + &matched_store_and_hash_prefixes); + if (!matched_store_and_hash_prefixes.empty()) { + (*full_hash_to_store_and_hash_prefixes)[full_hash] = + matched_store_and_hash_prefixes; + } + } + + // No hash prefixes found in the local database so that resource must be + // safe. + return !full_hash_to_store_and_hash_prefixes->empty(); + } + + NOTREACHED() << "Unexpected client_callback_type encountered"; + return false; +} + void V4LocalDatabaseManager::GetSeverestThreatTypeAndMetadata( SBThreatType* result_threat_type, ThreatMetadata* metadata, @@ -316,6 +338,14 @@ } } +StoresToCheck V4LocalDatabaseManager::GetStoresForFullHashRequests() { + StoresToCheck stores_for_full_hash; + for (auto it : list_infos_) { + stores_for_full_hash.insert(it.list_id()); + } + return stores_for_full_hash; +} + // Returns the SBThreatType corresponding to a given SafeBrowsing list. SBThreatType V4LocalDatabaseManager::GetSBThreatTypeForList( const ListIdentifier& list_id) { @@ -327,15 +357,6 @@ return it->sb_threat_type(); } -std::unordered_set<ListIdentifier> -V4LocalDatabaseManager::GetStoresForFullHashRequests() { - std::unordered_set<ListIdentifier> stores_for_full_hash; - for (auto it : list_infos_) { - stores_for_full_hash.insert(it.list_id()); - } - return stores_for_full_hash; -} - void V4LocalDatabaseManager::OnFullHashResponse( std::unique_ptr<PendingCheck> pending_check, const std::vector<FullHashInfo>& full_hash_infos) { @@ -356,8 +377,46 @@ GetSeverestThreatTypeAndMetadata(&pending_check->result_threat_type, &pending_check->url_metadata, full_hash_infos); - RespondToClient(std::move(pending_check)); pending_clients_.erase(it); + RespondToClient(std::move(pending_check)); +} + +void V4LocalDatabaseManager::PerformFullHashCheck( + std::unique_ptr<PendingCheck> check, + const FullHashToStoreAndHashPrefixesMap& + full_hash_to_store_and_hash_prefixes) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + DCHECK(enabled_); + DCHECK(!full_hash_to_store_and_hash_prefixes.empty()); + + pending_clients_.insert(check->client); + + v4_get_hash_protocol_manager_->GetFullHashes( + full_hash_to_store_and_hash_prefixes, + base::Bind(&V4LocalDatabaseManager::OnFullHashResponse, + base::Unretained(this), base::Passed(std::move(check)))); +} + +void V4LocalDatabaseManager::ProcessQueuedChecks() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + for (auto& it : queued_checks_) { + FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes; + if (!GetPrefixMatches(it, &full_hash_to_store_and_hash_prefixes)) { + RespondToClient(std::move(it)); + } else { + PerformFullHashCheck(std::move(it), full_hash_to_store_and_hash_prefixes); + } + } + queued_checks_.clear(); +} + +void V4LocalDatabaseManager::RespondSafeToQueuedChecks() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + for (std::unique_ptr<PendingCheck>& it : queued_checks_) { + RespondToClient(std::move(it)); + } + queued_checks_.clear(); } void V4LocalDatabaseManager::RespondToClient(
diff --git a/components/safe_browsing_db/v4_local_database_manager.h b/components/safe_browsing_db/v4_local_database_manager.h index 4dd4707..f9510c4 100644 --- a/components/safe_browsing_db/v4_local_database_manager.h +++ b/components/safe_browsing_db/v4_local_database_manager.h
@@ -37,36 +37,6 @@ CHECK_MAX }; - // The information we need to return the response to the SafeBrowsing client - // that asked for the safety reputation of a URL if we can't determine that - // synchronously. - // TODO(vakh): In its current form, it only includes information for - // |CheckBrowseUrl| method. Extend it to serve other methods on |client|. - struct PendingCheck { - PendingCheck(Client* client, - ClientCallbackType client_callback_type, - const GURL& url); - - ~PendingCheck(); - - // The SafeBrowsing client that's waiting for the safe/unsafe verdict. - Client* client; - - // Determines which funtion from the |client| needs to be called once we - // know whether the URL in |url| is safe or unsafe. - ClientCallbackType client_callback_type; - - // The threat verdict for the URL being checked. - SBThreatType result_threat_type; - - // The URL that is being checked for being unsafe. - GURL url; - - // The metadata associated with the full hash of the severest match found - // for that URL. - ThreatMetadata url_metadata; - }; - // Construct V4LocalDatabaseManager. // Must be initialized by calling StartOnIOThread() before using. V4LocalDatabaseManager(const base::FilePath& base_path); @@ -104,7 +74,44 @@ // protected: - std::unordered_set<ListIdentifier> GetStoresForFullHashRequests() override; + // The information we need to process a URL safety reputation request and + // respond to the SafeBrowsing client that asked for it. + // TODO(vakh): In its current form, it only includes information for + // |CheckBrowseUrl| method. Extend it to serve other methods on |client|. + struct PendingCheck { + PendingCheck(Client* client, + ClientCallbackType client_callback_type, + const StoresToCheck& stores_to_check, + const GURL& url); + + ~PendingCheck(); + + // The SafeBrowsing client that's waiting for the safe/unsafe verdict. + Client* client; + + // Determines which funtion from the |client| needs to be called once we + // know whether the URL in |url| is safe or unsafe. + ClientCallbackType client_callback_type; + + // The threat verdict for the URL being checked. + SBThreatType result_threat_type; + + // The SafeBrowsing lists to check hash prefixes in. + StoresToCheck stores_to_check; + + // The URL that is being checked for being unsafe. + GURL url; + + // The metadata associated with the full hash of the severest match found + // for that URL. + ThreatMetadata url_metadata; + }; + + typedef std::vector<std::unique_ptr<PendingCheck>> QueuedChecks; + + // The stores/lists to always get full hashes for, regardless of which store + // the hash prefix matched. + StoresToCheck GetStoresForFullHashRequests() override; private: friend class V4LocalDatabaseManagerTest; @@ -128,6 +135,12 @@ // Called when the database has been updated and schedules the next update. void DatabaseUpdated(); + // Return the prefixes and the store they matched in, for a given URL. Returns + // true if a hash prefix match is found; false otherwise. + bool GetPrefixMatches( + const std::unique_ptr<PendingCheck>& check, + FullHashToStoreAndHashPrefixesMap* full_hash_to_store_and_hash_prefixes); + // Finds the most severe |SBThreatType| and the corresponding |metadata| from // |full_hash_infos|. void GetSeverestThreatTypeAndMetadata( @@ -144,6 +157,19 @@ void OnFullHashResponse(std::unique_ptr<PendingCheck> pending_check, const std::vector<FullHashInfo>& full_hash_infos); + // Performs the full hash checking of the URL in |check|. + void PerformFullHashCheck(std::unique_ptr<PendingCheck> check, + const FullHashToStoreAndHashPrefixesMap& + full_hash_to_store_and_hash_prefixes); + + // When the database is ready to use, process the checks that were queued + // while the database was loading from disk. + void ProcessQueuedChecks(); + + // Called on StopOnIOThread, it responds to the clients that are waiting for + // the database to become available with the verdict as SAFE. + void RespondSafeToQueuedChecks(); + // Calls the appopriate method on the |client| object, based on the contents // of |pending_check|. void RespondToClient(std::unique_ptr<PendingCheck> pending_check); @@ -182,6 +208,10 @@ // SafeBrowsing service. PendingClients pending_clients_; + // The checks that need to be scheduled when the database becomes ready for + // use. + QueuedChecks queued_checks_; + // The sequenced task runner for running safe browsing database operations. scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/components/safe_browsing_db/v4_local_database_manager_unittest.cc b/components/safe_browsing_db/v4_local_database_manager_unittest.cc index 580ed74..d3e75cb 100644 --- a/components/safe_browsing_db/v4_local_database_manager_unittest.cc +++ b/components/safe_browsing_db/v4_local_database_manager_unittest.cc
@@ -25,7 +25,7 @@ void GetStoresMatchingFullHash( const FullHash& full_hash, - const std::unordered_set<ListIdentifier>& stores_to_look, + const StoresToCheck& stores_to_check, StoreAndHashPrefixes* store_and_hash_prefixes) override { *store_and_hash_prefixes = store_and_hash_prefixes_; } @@ -34,6 +34,22 @@ const StoreAndHashPrefixes& store_and_hash_prefixes_; }; +class TestClient : public SafeBrowsingDatabaseManager::Client { + public: + TestClient(SBThreatType sb_threat_type, const GURL& url) + : expected_sb_threat_type(sb_threat_type), expected_url(url) {} + + void OnCheckBrowseUrlResult(const GURL& url, + SBThreatType threat_type, + const ThreatMetadata& metadata) override { + DCHECK_EQ(expected_url, url); + DCHECK_EQ(expected_sb_threat_type, threat_type); + } + + SBThreatType expected_sb_threat_type; + GURL expected_url; +}; + class V4LocalDatabaseManagerTest : public PlatformTest { public: V4LocalDatabaseManagerTest() : task_runner_(new base::TestSimpleTaskRunner) {} @@ -48,15 +64,11 @@ make_scoped_refptr(new V4LocalDatabaseManager(base_dir_.GetPath())); v4_local_database_manager_->SetTaskRunnerForTest(task_runner_); - SetupLocalDatabaseManager(); + StartLocalDatabaseManager(); } void TearDown() override { - v4_local_database_manager_->StopOnIOThread(true); - - // Force destruction of the database. - task_runner_->RunPendingTasks(); - base::RunLoop().RunUntilIdle(); + StopLocalDatabaseManager(); PlatformTest::TearDown(); } @@ -65,18 +77,32 @@ v4_local_database_manager_->enabled_ = false; } + const V4LocalDatabaseManager::QueuedChecks& GetQueuedChecks() { + return v4_local_database_manager_->queued_checks_; + } + void ReplaceV4Database(const StoreAndHashPrefixes& store_and_hash_prefixes) { v4_local_database_manager_->v4_database_.reset(new FakeV4Database( task_runner_, base::MakeUnique<StoreMap>(), store_and_hash_prefixes)); } - void SetupLocalDatabaseManager() { + void ResetV4Database() { v4_local_database_manager_->v4_database_.reset(); } + + void StartLocalDatabaseManager() { v4_local_database_manager_->StartOnIOThread(NULL, V4ProtocolConfig()); task_runner_->RunPendingTasks(); base::RunLoop().RunUntilIdle(); } + void StopLocalDatabaseManager() { + v4_local_database_manager_->StopOnIOThread(true); + + // Force destruction of the database. + task_runner_->RunPendingTasks(); + base::RunLoop().RunUntilIdle(); + } + base::ScopedTempDir base_dir_; scoped_refptr<base::TestSimpleTaskRunner> task_runner_; content::TestBrowserThreadBundle thread_bundle_; @@ -162,4 +188,31 @@ EXPECT_EQ("malware_popid", metadata.population_id); } +TEST_F(V4LocalDatabaseManagerTest, TestChecksAreQueued) { + const GURL url("https://www.example.com/"); + TestClient client(SB_THREAT_TYPE_SAFE, url); + EXPECT_TRUE(GetQueuedChecks().empty()); + v4_local_database_manager_->CheckBrowseUrl(url, &client); + // The database is available so the check shouldn't get queued. + EXPECT_TRUE(GetQueuedChecks().empty()); + + ResetV4Database(); + v4_local_database_manager_->CheckBrowseUrl(url, &client); + // The database is unavailable so the check should get queued. + EXPECT_EQ(1ul, GetQueuedChecks().size()); + + // The following function calls StartOnIOThread which should load the + // database from disk and cause the queued check to be performed. + StartLocalDatabaseManager(); + EXPECT_TRUE(GetQueuedChecks().empty()); + + ResetV4Database(); + v4_local_database_manager_->CheckBrowseUrl(url, &client); + // The database is unavailable so the check should get queued. + EXPECT_EQ(1ul, GetQueuedChecks().size()); + + StopLocalDatabaseManager(); + EXPECT_TRUE(GetQueuedChecks().empty()); +} + } // namespace safe_browsing
diff --git a/components/safe_browsing_db/v4_protocol_manager_util.h b/components/safe_browsing_db/v4_protocol_manager_util.h index 9ca00849..34e2412 100644 --- a/components/safe_browsing_db/v4_protocol_manager_util.h +++ b/components/safe_browsing_db/v4_protocol_manager_util.h
@@ -297,6 +297,8 @@ DISALLOW_COPY_AND_ASSIGN(V4ProtocolManagerUtil); }; +typedef std::unordered_set<ListIdentifier> StoresToCheck; + } // namespace safe_browsing namespace std {
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn index 9569724..1ebece93 100644 --- a/components/sync/BUILD.gn +++ b/components/sync/BUILD.gn
@@ -29,6 +29,7 @@ "api/entity_change.h", "api/entity_data.cc", "api/entity_data.h", + "api/local_change_observer.h", "api/metadata_batch.cc", "api/metadata_batch.h", "api/metadata_change_list.h",
diff --git a/components/sync/api/fake_sync_change_processor.cc b/components/sync/api/fake_sync_change_processor.cc index 0060cf0..a675151 100644 --- a/components/sync/api/fake_sync_change_processor.cc +++ b/components/sync/api/fake_sync_change_processor.cc
@@ -33,6 +33,12 @@ return syncer::SyncError(); } +void FakeSyncChangeProcessor::AddLocalChangeObserver( + syncer::LocalChangeObserver* observer) {} + +void FakeSyncChangeProcessor::RemoveLocalChangeObserver( + syncer::LocalChangeObserver* observer) {} + const syncer::SyncChangeList& FakeSyncChangeProcessor::changes() const { return changes_; }
diff --git a/components/sync/api/fake_sync_change_processor.h b/components/sync/api/fake_sync_change_processor.h index d599f55e..1498b2d 100644 --- a/components/sync/api/fake_sync_change_processor.h +++ b/components/sync/api/fake_sync_change_processor.h
@@ -36,6 +36,9 @@ syncer::SyncError UpdateDataTypeContext(ModelType type, ContextRefreshStatus refresh_status, const std::string& context) override; + void AddLocalChangeObserver(syncer::LocalChangeObserver* observer) override; + void RemoveLocalChangeObserver( + syncer::LocalChangeObserver* observer) override; virtual const syncer::SyncChangeList& changes() const; virtual syncer::SyncChangeList& changes();
diff --git a/components/sync/api/local_change_observer.h b/components/sync/api/local_change_observer.h new file mode 100644 index 0000000..1c9f7d2 --- /dev/null +++ b/components/sync/api/local_change_observer.h
@@ -0,0 +1,32 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SYNC_API_LOCAL_CHANGE_OBSERVER_H_ +#define COMPONENTS_SYNC_API_LOCAL_CHANGE_OBSERVER_H_ + +namespace syncer { + +class SyncChange; +namespace syncable { +class Entry; +} // namespace syncable + +// Interface for observers that want to be notified of local sync changes. +// The OnLocalChange function will be called when a local change made through +// ProcessSyncChanges is about to be applied to the directory. This is useful +// for inspecting the specifics of sync data being written locally and comparing +// it against the current state of the directory. Registering a local change +// observer is done by calling the AddLocalChangeObserver function on an +// instance of SyncChangeProcessor. +class LocalChangeObserver { + public: + virtual ~LocalChangeObserver() {} + // Function called to notify observer of the local change. current_entry + // should reflect the state of the entry *before* change is applied. + virtual void OnLocalChange(const syncable::Entry* current_entry, + const SyncChange& change) = 0; +}; +} // namespace syncer + +#endif // COMPONENTS_SYNC_API_LOCAL_CHANGE_OBSERVER_H_
diff --git a/components/sync/api/sync_change_processor.cc b/components/sync/api/sync_change_processor.cc index f954a6ac..3a0b6a0 100644 --- a/components/sync/api/sync_change_processor.cc +++ b/components/sync/api/sync_change_processor.cc
@@ -18,4 +18,14 @@ return syncer::SyncError(); } +void SyncChangeProcessor::AddLocalChangeObserver( + LocalChangeObserver* observer) { + NOTREACHED(); +} + +void SyncChangeProcessor::RemoveLocalChangeObserver( + LocalChangeObserver* observer) { + NOTREACHED(); +} + } // namespace syncer
diff --git a/components/sync/api/sync_change_processor.h b/components/sync/api/sync_change_processor.h index d5884a2..2d31b8a9 100644 --- a/components/sync/api/sync_change_processor.h +++ b/components/sync/api/sync_change_processor.h
@@ -20,6 +20,8 @@ class SyncChange; +class LocalChangeObserver; + typedef std::vector<SyncChange> SyncChangeList; // An interface for services that handle receiving SyncChanges. @@ -75,6 +77,12 @@ ModelType type, ContextRefreshStatus refresh_status, const std::string& context); + + // Adds an observer of local sync changes. This observer is notified when + // local sync changes are applied by GenericChangeProcessor. observer is + // not owned by the SyncChangeProcessor. + virtual void AddLocalChangeObserver(LocalChangeObserver* observer); + virtual void RemoveLocalChangeObserver(LocalChangeObserver* observer); }; } // namespace syncer
diff --git a/components/sync/driver/generic_change_processor.cc b/components/sync/driver/generic_change_processor.cc index a54a6a75..0042c16 100644 --- a/components/sync/driver/generic_change_processor.cc +++ b/components/sync/driver/generic_change_processor.cc
@@ -15,6 +15,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "components/sync/api/data_type_error_handler.h" +#include "components/sync/api/local_change_observer.h" #include "components/sync/api/sync_change.h" #include "components/sync/api/sync_error.h" #include "components/sync/api/syncable_service.h" @@ -244,6 +245,16 @@ return syncer::SyncError(); } +void GenericChangeProcessor::AddLocalChangeObserver( + syncer::LocalChangeObserver* observer) { + local_change_observers_.AddObserver(observer); +} + +void GenericChangeProcessor::RemoveLocalChangeObserver( + syncer::LocalChangeObserver* observer) { + local_change_observers_.RemoveObserver(observer); +} + void GenericChangeProcessor::OnAttachmentUploaded( const syncer::AttachmentId& attachment_id) { syncer::WriteTransaction trans(FROM_HERE, share_handle()); @@ -366,11 +377,14 @@ } } -syncer::SyncError AttemptDelete(const syncer::SyncChange& change, - syncer::ModelType type, - const std::string& type_str, - syncer::WriteNode* node, - syncer::DataTypeErrorHandler* error_handler) { +} // namespace + +syncer::SyncError GenericChangeProcessor::AttemptDelete( + const syncer::SyncChange& change, + syncer::ModelType type, + const std::string& type_str, + syncer::WriteNode* node, + syncer::DataTypeErrorHandler* error_handler) { DCHECK_EQ(change.change_type(), syncer::SyncChange::ACTION_DELETE); if (change.sync_data().IsLocal()) { const std::string& tag = syncer::SyncDataLocal(change.sync_data()).GetTag(); @@ -405,6 +419,7 @@ type, error_handler); } } + NotifyLocalChangeObservers(node->GetEntry(), change); if (IsActOnceDataType(type)) node->Drop(); else @@ -412,8 +427,6 @@ return syncer::SyncError(); } -} // namespace - syncer::SyncError GenericChangeProcessor::ProcessSyncChanges( const tracked_objects::Location& from_here, const syncer::SyncChangeList& list_of_changes) { @@ -553,6 +566,8 @@ } } } + NotifyLocalChangeObservers(sync_node->GetEntry(), change); + sync_node->SetTitle(change.sync_data().GetTitle()); SetNodeSpecifics(sync_data_local.GetSpecifics(), sync_node); @@ -617,6 +632,8 @@ } } + NotifyLocalChangeObservers(sync_node->GetEntry(), change); + sync_node->SetTitle(change.sync_data().GetTitle()); SetNodeSpecifics(sync_data_local.GetSpecifics(), sync_node); syncer::AttachmentIdList attachment_ids = sync_data_local.GetAttachmentIds(); @@ -683,6 +700,13 @@ } } +void GenericChangeProcessor::NotifyLocalChangeObservers( + const syncer::syncable::Entry* current_entry, + const syncer::SyncChange& change) { + FOR_EACH_OBSERVER(syncer::LocalChangeObserver, local_change_observers_, + OnLocalChange(current_entry, change)); +} + std::unique_ptr<syncer::AttachmentService> GenericChangeProcessor::GetAttachmentService() const { return std::unique_ptr<syncer::AttachmentService>(
diff --git a/components/sync/driver/generic_change_processor.h b/components/sync/driver/generic_change_processor.h index 36c08aa2..18ba548 100644 --- a/components/sync/driver/generic_change_processor.h +++ b/components/sync/driver/generic_change_processor.h
@@ -30,6 +30,10 @@ class WriteTransaction; typedef std::vector<syncer::SyncData> SyncDataList; + +namespace syncable { +class Entry; +} // namespace syncable } // namespace syncer namespace sync_driver { @@ -82,6 +86,9 @@ syncer::ModelType type, syncer::SyncChangeProcessor::ContextRefreshStatus refresh_status, const std::string& context) override; + void AddLocalChangeObserver(syncer::LocalChangeObserver* observer) override; + void RemoveLocalChangeObserver( + syncer::LocalChangeObserver* observer) override; // syncer::AttachmentService::Delegate implementation. void OnAttachmentUploaded(const syncer::AttachmentId& attachment_id) override; @@ -113,6 +120,11 @@ syncer::UserShare* share_handle() const override; private: + syncer::SyncError AttemptDelete(const syncer::SyncChange& change, + syncer::ModelType type, + const std::string& type_str, + syncer::WriteNode* node, + syncer::DataTypeErrorHandler* error_handler); // Logically part of ProcessSyncChanges. // // |new_attachments| is an output parameter containing newly added attachments @@ -138,6 +150,11 @@ // server. void UploadAllAttachmentsNotOnServer(); + // Notify every registered local change observer that |change| is about to be + // applied to |current_entry|. + void NotifyLocalChangeObservers(const syncer::syncable::Entry* current_entry, + const syncer::SyncChange& change); + const syncer::ModelType type_; // The SyncableService this change processor will forward changes on to. @@ -166,6 +183,9 @@ // attachments. std::unique_ptr<syncer::AttachmentService> attachment_service_; + // List of observers that want to be notified of local changes being written. + base::ObserverList<syncer::LocalChangeObserver> local_change_observers_; + // Must be destroyed before attachment_service_ to ensure WeakPtrs are // invalidated before attachment_service_ is destroyed. // Can be NULL if attachment_service_ is NULL;
diff --git a/components/sync/driver/shared_change_processor.cc b/components/sync/driver/shared_change_processor.cc index 60f010a..fc8eea8 100644 --- a/components/sync/driver/shared_change_processor.cc +++ b/components/sync/driver/shared_change_processor.cc
@@ -21,7 +21,7 @@ namespace syncer { class AttachmentService; -} +} // namespace syncer namespace sync_driver { @@ -259,6 +259,22 @@ context); } +void SharedChangeProcessor::AddLocalChangeObserver( + syncer::LocalChangeObserver* observer) { + DCHECK(backend_task_runner_.get()); + DCHECK(backend_task_runner_->BelongsToCurrentThread()); + + generic_change_processor_->AddLocalChangeObserver(observer); +} + +void SharedChangeProcessor::RemoveLocalChangeObserver( + syncer::LocalChangeObserver* observer) { + DCHECK(backend_task_runner_.get()); + DCHECK(backend_task_runner_->BelongsToCurrentThread()); + + generic_change_processor_->RemoveLocalChangeObserver(observer); +} + bool SharedChangeProcessor::SyncModelHasUserCreatedNodes(bool* has_nodes) { DCHECK(backend_task_runner_.get()); DCHECK(backend_task_runner_->BelongsToCurrentThread());
diff --git a/components/sync/driver/shared_change_processor.h b/components/sync/driver/shared_change_processor.h index 0edc3ac..5488ac3f 100644 --- a/components/sync/driver/shared_change_processor.h +++ b/components/sync/driver/shared_change_processor.h
@@ -106,6 +106,8 @@ syncer::ModelType type, syncer::SyncChangeProcessor::ContextRefreshStatus refresh_status, const std::string& context); + virtual void AddLocalChangeObserver(syncer::LocalChangeObserver* observer); + virtual void RemoveLocalChangeObserver(syncer::LocalChangeObserver* observer); virtual bool SyncModelHasUserCreatedNodes(bool* has_nodes); virtual bool CryptoReadyIfNecessary();
diff --git a/components/sync/driver/shared_change_processor_ref.cc b/components/sync/driver/shared_change_processor_ref.cc index 49df65eb..a989e1c2 100644 --- a/components/sync/driver/shared_change_processor_ref.cc +++ b/components/sync/driver/shared_change_processor_ref.cc
@@ -33,6 +33,16 @@ context); } +void SharedChangeProcessorRef::AddLocalChangeObserver( + syncer::LocalChangeObserver* observer) { + change_processor_->AddLocalChangeObserver(observer); +} + +void SharedChangeProcessorRef::RemoveLocalChangeObserver( + syncer::LocalChangeObserver* observer) { + change_processor_->RemoveLocalChangeObserver(observer); +} + syncer::SyncError SharedChangeProcessorRef::CreateAndUploadError( const tracked_objects::Location& from_here, const std::string& message) {
diff --git a/components/sync/driver/shared_change_processor_ref.h b/components/sync/driver/shared_change_processor_ref.h index c0167b2..a8a9b9b 100644 --- a/components/sync/driver/shared_change_processor_ref.h +++ b/components/sync/driver/shared_change_processor_ref.h
@@ -33,6 +33,9 @@ syncer::ModelType type, syncer::SyncChangeProcessor::ContextRefreshStatus refresh_status, const std::string& context) override; + void AddLocalChangeObserver(syncer::LocalChangeObserver* observer) override; + void RemoveLocalChangeObserver( + syncer::LocalChangeObserver* observer) override; // syncer::SyncErrorFactory implementation. syncer::SyncError CreateAndUploadError(
diff --git a/components/sync_sessions/BUILD.gn b/components/sync_sessions/BUILD.gn index b7ef585..1515037 100644 --- a/components/sync_sessions/BUILD.gn +++ b/components/sync_sessions/BUILD.gn
@@ -7,6 +7,8 @@ "favicon_cache.cc", "favicon_cache.h", "local_session_event_router.h", + "lost_navigations_recorder.cc", + "lost_navigations_recorder.h", "open_tabs_ui_delegate.cc", "open_tabs_ui_delegate.h", "revisit/bookmarks_by_url_provider_impl.cc", @@ -90,6 +92,7 @@ testonly = true sources = [ "favicon_cache_unittest.cc", + "lost_navigations_recorder_unittest.cc", "revisit/bookmarks_page_revisit_observer_unittest.cc", "revisit/current_tab_matcher_unittest.cc", "revisit/offset_tab_matcher_unittest.cc", @@ -112,6 +115,7 @@ "//components/sessions:test_support", "//components/sync", "//components/sync:test_support_sync_api", + "//components/sync:test_support_sync_core", "//components/sync:test_support_sync_driver", "//testing/gmock", "//testing/gtest",
diff --git a/components/sync_sessions/lost_navigations_recorder.cc b/components/sync_sessions/lost_navigations_recorder.cc new file mode 100644 index 0000000..5f9b1f9 --- /dev/null +++ b/components/sync_sessions/lost_navigations_recorder.cc
@@ -0,0 +1,136 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/metrics/histogram_macros.h" +#include "base/stl_util.h" +#include "components/sync/api/sync_change.h" +#include "components/sync/syncable/entry.h" +#include "components/sync_sessions/lost_navigations_recorder.h" + +namespace sync_sessions { + +LostNavigationsRecorder::LostNavigationsRecorder() + : recorder_state_(RECORDER_STATE_NOT_SYNCING) {} + +LostNavigationsRecorder::~LostNavigationsRecorder() {} + +void LostNavigationsRecorder::OnLocalChange( + const syncer::syncable::Entry* current_entry, + const syncer::SyncChange& change) { + if (change.sync_data().GetSpecifics().session().has_tab()) { + TransitionState(current_entry->GetSyncing(), + current_entry->GetIsUnsynced()); + } + RecordChange(change); +} + +// Record a change either by adjusting our list of tabs or by recording the set +// of navigations in the updated tab. +void LostNavigationsRecorder::RecordChange(const syncer::SyncChange& change) { + sync_pb::SessionSpecifics session_specifics = + change.sync_data().GetSpecifics().session(); + if (session_specifics.has_header()) { + DeleteTabs(session_specifics.header()); + return; + } else if (!session_specifics.has_tab()) { + // There isn't any data we care about if neither a tab or window is + // modified. + return; + } + sync_pb::SessionTab tab = session_specifics.tab(); + id_type tab_id = tab.tab_id(); + + IdSet& latest = latest_navigation_ids_[tab_id]; + latest.clear(); + + for (sync_pb::TabNavigation nav : tab.navigation()) { + id_type uid = nav.unique_id(); + // Only record an id if it's "new" i.e. larger than the largest seen for + // that tab. If the id is smaller than this, it's not new; ids are generated + // in increasing order. + if (uid > max_recorded_for_tab_[tab_id]) { + recorded_navigation_ids_[tab_id].insert(uid); + max_recorded_for_tab_[tab_id] = uid; + } + latest.insert(nav.unique_id()); + } +} + +void LostNavigationsRecorder::DeleteTabs(const sync_pb::SessionHeader& header) { + IdSet new_tab_ids; + IdSet current_tab_ids; + // Find the set of tab ids that are still there after the deletion. + for (sync_pb::SessionWindow window : header.window()) { + for (id_type tab_id : window.tab()) { + new_tab_ids.insert(tab_id); + } + } + for (auto pair : recorded_navigation_ids_) { + current_tab_ids.insert(pair.first); + } + // The set of deleted tabs is the difference between the set of tabs before + // the pending change and the set of tabs following the pending change. + IdSet deleted_tabs = + base::STLSetDifference<IdSet>(current_tab_ids, new_tab_ids); + for (id_type tab_id : deleted_tabs) { + recorded_navigation_ids_.erase(tab_id); + latest_navigation_ids_.erase(tab_id); + max_recorded_for_tab_.erase(tab_id); + } +} + +// Change the current state of the recorder, possibly triggering reconciliation, +// based on the status of the directory entry. Reconciliation triggers on the +// observation of two conditions. +// 1) The entry transitioning into the syncing state +// 2) If we miss the transition to syncing state, the entry transitioning into +// the synced state. +void LostNavigationsRecorder::TransitionState(bool is_syncing, + bool is_unsynced) { + switch (recorder_state_) { + case RECORDER_STATE_NOT_SYNCING: + // If a sync cycle is happening or already finished, reconcile. + // It's possible that this will trigger reconciliation multiple times per + // sync cycle; once per tab that finishes the cycle in the synced state + // and the user performs a navigation in. In theory this will cause + // under-counting, since reconciliation clears each tab's remembered set + // of navigations. In practice the number of unique tabs written to in + // between two adjacent sync cycles should be pretty low, + // making the undercounting tolerable. + if (is_syncing || !is_unsynced) { + ReconcileLostNavs(); + } + // If we're currently in a sync cycle, remember that. + if (is_syncing) { + recorder_state_ = RECORDER_STATE_SYNCING; + } + break; + case RECORDER_STATE_SYNCING: + if (!is_syncing) { + recorder_state_ = RECORDER_STATE_NOT_SYNCING; + } + break; + } +} + +// Reconcile the number of "lost" navigations by checking all the unique ids we +// recorded against what was actually synced. +void LostNavigationsRecorder::ReconcileLostNavs() { + for (auto pair : recorded_navigation_ids_) { + id_type tab_id = pair.first; + IdSet& latest = latest_navigation_ids_[tab_id]; + IdSet& recorded = recorded_navigation_ids_[tab_id]; + if (recorded.size() < 1) { + continue; + } + + // The set of lost navigations is anything we recorded as new that's not + // present in latest. + IdSet lost_navs = base::STLSetDifference<IdSet>(recorded, latest); + int quantity_lost = lost_navs.size(); + UMA_HISTOGRAM_COUNTS("Sync.LostNavigationCount", quantity_lost); + recorded.clear(); + } +} +} // namespace sync_sessions
diff --git a/components/sync_sessions/lost_navigations_recorder.h b/components/sync_sessions/lost_navigations_recorder.h new file mode 100644 index 0000000..10b264f --- /dev/null +++ b/components/sync_sessions/lost_navigations_recorder.h
@@ -0,0 +1,55 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SYNC_SESSIONS_LOST_NAVIGATIONS_RECORDER_H_ +#define COMPONENTS_SYNC_SESSIONS_LOST_NAVIGATIONS_RECORDER_H_ + +#include "base/macros.h" +#include "components/sessions/core/session_id.h" +#include "components/sync/api/local_change_observer.h" +#include "components/sync/api/sync_change.h" +#include "components/sync/protocol/session_specifics.pb.h" + +namespace sync_sessions { + +// Recorder class that tracks the number of navigations written locally that +// aren't synced. This is done by recording set of locally observed navigations +// and reconciling these sets against what was ultimately synced. These +// navigations ultimately feed chrome history, so losing them prevents them from +// being reflected in the history page. +class LostNavigationsRecorder : public syncer::LocalChangeObserver { + public: + typedef SessionID::id_type id_type; + typedef std::set<id_type> IdSet; + enum RecorderState { RECORDER_STATE_NOT_SYNCING, RECORDER_STATE_SYNCING }; + + LostNavigationsRecorder(); + ~LostNavigationsRecorder() override; + + // syncer::LocalChangeObserver implementation. + void OnLocalChange(const syncer::syncable::Entry* current_entry, + const syncer::SyncChange& change) override; + + private: + void RecordChange(const syncer::SyncChange& change); + void DeleteTabs(const sync_pb::SessionHeader& header); + void TransitionState(bool is_syncing, bool is_unsynced); + void ReconcileLostNavs(); + + // State that records whether the most recently observed directory state was + // syncing or not syncing. + RecorderState recorder_state_; + + // The set of all navigation ids we've observed for each tab_id since the last + // sync. + std::map<id_type, IdSet> recorded_navigation_ids_; + // The set of navigation ids most recently recorded for each tab_id. + std::map<id_type, IdSet> latest_navigation_ids_; + // The maximum navigation_id recorded for each tab_id. + std::map<id_type, id_type> max_recorded_for_tab_; + DISALLOW_COPY_AND_ASSIGN(LostNavigationsRecorder); +}; +}; // namespace sync_sessions + +#endif // COMPONENTS_SYNC_SESSIONS_LOST_NAVIGATIONS_RECORDER_H_
diff --git a/components/sync_sessions/lost_navigations_recorder_unittest.cc b/components/sync_sessions/lost_navigations_recorder_unittest.cc new file mode 100644 index 0000000..bffb921 --- /dev/null +++ b/components/sync_sessions/lost_navigations_recorder_unittest.cc
@@ -0,0 +1,398 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> + +#include "base/message_loop/message_loop.h" +#include "base/test/histogram_tester.h" +#include "components/sessions/core/session_id.h" +#include "components/sync/core/attachments/attachment_service_proxy_for_test.h" +#include "components/sync/syncable/entry.h" +#include "components/sync/syncable/mutable_entry.h" +#include "components/sync/syncable/syncable_base_transaction.h" +#include "components/sync/syncable/syncable_read_transaction.h" +#include "components/sync/syncable/syncable_write_transaction.h" +#include "components/sync/test/engine/test_directory_setter_upper.h" +#include "components/sync/test/engine/test_id_factory.h" +#include "components/sync_sessions/lost_navigations_recorder.h" +#include "components/sync_sessions/sessions_sync_manager.h" +#include "testing/gtest/include/gtest/gtest.h" + +using syncer::syncable::Entry; +using syncer::syncable::Id; +using syncer::syncable::MutableEntry; +using syncer::syncable::WriteTransaction; + +namespace sync_sessions { +namespace { +typedef SessionID::id_type id_type; + +const char kTab1SyncTag[] = "tab-YWRkcjHvv74="; +const char kTab2SyncTag[] = "tab-2FyZDHvv74="; + +class LostNavigationsRecorderTest : public testing::Test { + protected: + void SetUp() override { + dir_maker_.SetUp(); + _id = 1; + } + + void TearDown() override { dir_maker_.TearDown(); } + + syncer::syncable::Directory* directory() { return dir_maker_.directory(); } + + LostNavigationsRecorder* recorder() { return &recorder_; } + + void AddNavigation(sync_pb::SessionSpecifics* tab_base, + id_type id_override = -1) { + sync_pb::SessionTab* tab = tab_base->mutable_tab(); + sync_pb::TabNavigation* navigation = tab->add_navigation(); + navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED); + navigation->set_unique_id(id_override > 0 ? id_override : _id++); + } + + void BuildTabSpecifics(const std::string& tag, + int tab_id, + sync_pb::SessionSpecifics* tab_base, + int num_unique_navs = 1) { + tab_base->set_session_tag(tag); + tab_base->set_tab_node_id(0); + sync_pb::SessionTab* tab = tab_base->mutable_tab(); + tab->set_tab_id(tab_id); + tab->set_current_navigation_index(0); + for (int i = 0; i < num_unique_navs; ++i) { + AddNavigation(tab_base); + } + } + + void BuildWindowSpecifics(int window_id, + sync_pb::SessionSpecifics* window_base) { + sync_pb::SessionHeader* header = window_base->mutable_header(); + sync_pb::SessionWindow* window = header->add_window(); + window->set_window_id(window_id); + } + + const Id& CreateEntry() { + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::CREATE, + syncer::SESSIONS, wtrans.root_id(), + "entrynamutable_entry"); + EXPECT_TRUE(mutable_entry.good()); + return mutable_entry.GetId(); + } + + const syncer::SyncData CreateLocalData( + const sync_pb::SessionSpecifics& specifics, + const std::string& sync_tag) const { + sync_pb::EntitySpecifics entity; + entity.mutable_session()->CopyFrom(specifics); + return syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity); + } + + syncer::SyncChange MakeChange(const std::string& sync_tag, + const sync_pb::SessionSpecifics& specifics, + syncer::SyncChange::SyncChangeType type) const { + return syncer::SyncChange(FROM_HERE, type, + CreateLocalData(specifics, sync_tag)); + } + + void RecordChange(const Entry* entry, int num_unique_navs) { + sync_pb::EntitySpecifics specifics; + BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), + num_unique_navs); + RecordChange(entry, specifics); + } + + void RecordChange(const Entry* entry, sync_pb::EntitySpecifics specifics) { + syncer::SyncChange change = MakeChange(kTab1SyncTag, specifics.session(), + syncer::SyncChange::ACTION_UPDATE); + recorder()->OnLocalChange(entry, change); + } + + void TriggerReconcile(MutableEntry* mutable_entry, + bool trigger_by_syncing = true, + sync_pb::EntitySpecifics* specifics = nullptr) { + mutable_entry->PutSyncing(trigger_by_syncing); + mutable_entry->PutIsUnsynced(trigger_by_syncing); + if (specifics == nullptr) { + RecordChange(mutable_entry, 0); + } else { + RecordChange(mutable_entry, *specifics); + } + } + + private: + base::MessageLoop message_loop; + id_type _id; + LostNavigationsRecorder recorder_; + syncer::TestDirectorySetterUpper dir_maker_; +}; + +TEST_F(LostNavigationsRecorderTest, MultipleNavsNoneLost) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + mutable_entry.PutIsUnsynced(true); + + RecordChange(&mutable_entry, 6); + + TriggerReconcile(&mutable_entry); + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 0, 1); +} + +TEST_F(LostNavigationsRecorderTest, MultipleNavsOneLost) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + mutable_entry.PutIsUnsynced(true); + + RecordChange(&mutable_entry, 1); + RecordChange(&mutable_entry, 1); + + TriggerReconcile(&mutable_entry); + + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 1, 1); +} + +TEST_F(LostNavigationsRecorderTest, MultipleNavsMultipleLost) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + mutable_entry.PutIsUnsynced(true); + + RecordChange(&mutable_entry, 5); + RecordChange(&mutable_entry, 1); + + TriggerReconcile(&mutable_entry); + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 5, 1); +} + +TEST_F(LostNavigationsRecorderTest, MultipleWritesWhileSyncing) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + + sync_pb::EntitySpecifics specifics; + BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); + mutable_entry.PutIsUnsynced(true); + RecordChange(&mutable_entry, specifics); + + mutable_entry.PutSyncing(true); + RecordChange(&mutable_entry, specifics); + + specifics.mutable_session()->mutable_tab()->clear_navigation(); + for (int i = 0; i < 5; i++) { + AddNavigation(specifics.mutable_session()); + RecordChange(&mutable_entry, specifics); + } + + TriggerReconcile(&mutable_entry, false); + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 0, 1); +} + +TEST_F(LostNavigationsRecorderTest, MultipleWritesMultipleEntriesNoneLost) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + const Id& id2 = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + MutableEntry mutable_entry2(&wtrans, syncer::syncable::GET_BY_ID, id2); + mutable_entry.PutIsUnsynced(true); + mutable_entry2.PutIsUnsynced(true); + + sync_pb::EntitySpecifics specifics; + sync_pb::EntitySpecifics specifics2; + BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); + BuildTabSpecifics(kTab2SyncTag, 2, specifics2.mutable_session(), 5); + RecordChange(&mutable_entry, specifics); + RecordChange(&mutable_entry2, specifics2); + + mutable_entry.PutSyncing(true); + mutable_entry2.PutSyncing(true); + RecordChange(&mutable_entry, specifics); + RecordChange(&mutable_entry2, specifics2); + + specifics.mutable_session()->mutable_tab()->clear_navigation(); + specifics2.mutable_session()->mutable_tab()->clear_navigation(); + for (int i = 0; i < 5; i++) { + AddNavigation(specifics.mutable_session()); + AddNavigation(specifics2.mutable_session()); + + RecordChange(&mutable_entry, specifics); + RecordChange(&mutable_entry2, specifics2); + } + + TriggerReconcile(&mutable_entry, false, &specifics); + TriggerReconcile(&mutable_entry2, false, &specifics2); + + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 0, 4); +} + +TEST_F(LostNavigationsRecorderTest, MultipleWritesMultipleEntriesMultipleLost) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + const Id& id2 = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + MutableEntry mutable_entry2(&wtrans, syncer::syncable::GET_BY_ID, id2); + mutable_entry.PutIsUnsynced(true); + mutable_entry2.PutIsUnsynced(true); + + sync_pb::EntitySpecifics specifics; + sync_pb::EntitySpecifics specifics2; + BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); + BuildTabSpecifics(kTab2SyncTag, 2, specifics2.mutable_session(), 5); + RecordChange(&mutable_entry, specifics); + RecordChange(&mutable_entry2, specifics2); + + mutable_entry.PutSyncing(true); + mutable_entry2.PutSyncing(true); + RecordChange(&mutable_entry, specifics); + RecordChange(&mutable_entry2, specifics2); + + specifics.mutable_session()->mutable_tab()->clear_navigation(); + specifics2.mutable_session()->mutable_tab()->clear_navigation(); + for (int i = 0; i < 5; i++) { + AddNavigation(specifics.mutable_session()); + AddNavigation(specifics2.mutable_session()); + + RecordChange(&mutable_entry, specifics); + RecordChange(&mutable_entry2, specifics2); + } + + specifics.mutable_session() + ->mutable_tab() + ->mutable_navigation() + ->DeleteSubrange(0, 2); + specifics2.mutable_session() + ->mutable_tab() + ->mutable_navigation() + ->DeleteSubrange(0, 2); + RecordChange(&mutable_entry, specifics); + RecordChange(&mutable_entry, specifics2); + + TriggerReconcile(&mutable_entry, false, &specifics); + TriggerReconcile(&mutable_entry2, false, &specifics2); + + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 2, 2); +} + +TEST_F(LostNavigationsRecorderTest, NoWritesWhileSyncingMultipleLost) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + + sync_pb::EntitySpecifics specifics; + BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); + syncer::SyncChange change = MakeChange(kTab1SyncTag, specifics.session(), + syncer::SyncChange::ACTION_UPDATE); + mutable_entry.PutIsUnsynced(true); + recorder()->OnLocalChange(&mutable_entry, change); + + specifics.mutable_session()->mutable_tab()->clear_navigation(); + AddNavigation(specifics.mutable_session()); + change = MakeChange(kTab1SyncTag, specifics.session(), + syncer::SyncChange::ACTION_UPDATE); + recorder()->OnLocalChange(&mutable_entry, change); + + mutable_entry.PutIsUnsynced(false); + recorder()->OnLocalChange(&mutable_entry, change); + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 5, 1); +} + +TEST_F(LostNavigationsRecorderTest, WindowChangeDoesNotTriggerReconcile) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + const Id& id2 = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + MutableEntry mutable_entry2(&wtrans, syncer::syncable::GET_BY_ID, id2); + mutable_entry.PutIsUnsynced(true); + + RecordChange(&mutable_entry, 1); + + sync_pb::EntitySpecifics specifics; + BuildWindowSpecifics(1, specifics.mutable_session()); + RecordChange(&mutable_entry2, specifics); + + EXPECT_EQ(0ul, + histogram_tester.GetAllSamples("Sync.LostNavigationCount").size()); +} + +TEST_F(LostNavigationsRecorderTest, Samutable_entryNavigationSetAcrossStates) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + + sync_pb::EntitySpecifics specifics; + BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); + syncer::SyncChange change = MakeChange(kTab1SyncTag, specifics.session(), + syncer::SyncChange::ACTION_UPDATE); + mutable_entry.PutIsUnsynced(true); + recorder()->OnLocalChange(&mutable_entry, change); + recorder()->OnLocalChange(&mutable_entry, change); + + mutable_entry.PutIsUnsynced(false); + recorder()->OnLocalChange(&mutable_entry, change); + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 0, 1); +} + +TEST_F(LostNavigationsRecorderTest, RevisitPreviousNavs) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + mutable_entry.PutIsUnsynced(true); + + sync_pb::EntitySpecifics specifics; + BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 3); + RecordChange(&mutable_entry, specifics); + + AddNavigation(specifics.mutable_session()); + RecordChange(&mutable_entry, specifics); + + specifics.mutable_session() + ->mutable_tab() + ->mutable_navigation() + ->RemoveLast(); + RecordChange(&mutable_entry, specifics); + + TriggerReconcile(&mutable_entry, true, &specifics); + + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 1, 1); +} + +TEST_F(LostNavigationsRecorderTest, MultipleNavsMultipleLostWithOverlap) { + base::HistogramTester histogram_tester; + const Id& id = CreateEntry(); + WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); + MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); + mutable_entry.PutIsUnsynced(true); + + sync_pb::EntitySpecifics specifics; + BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); + RecordChange(&mutable_entry, specifics); + + AddNavigation(specifics.mutable_session()); + AddNavigation(specifics.mutable_session()); + + specifics.mutable_session() + ->mutable_tab() + ->mutable_navigation() + ->DeleteSubrange(0, 2); + RecordChange(&mutable_entry, specifics); + + TriggerReconcile(&mutable_entry, true, &specifics); + + histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 2, 1); +} + +}; // namespace +}; // namespace sync_sessions
diff --git a/components/sync_sessions/sessions_sync_manager.cc b/components/sync_sessions/sessions_sync_manager.cc index a1e5d13..fd7fd50 100644 --- a/components/sync_sessions/sessions_sync_manager.cc +++ b/components/sync_sessions/sessions_sync_manager.cc
@@ -7,6 +7,7 @@ #include <algorithm> #include <utility> +#include "base/memory/ptr_util.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram_macros.h" #include "build/build_config.h" @@ -16,6 +17,7 @@ #include "components/sync/api/time.h" #include "components/sync/device_info/local_device_info_provider.h" #include "components/sync/syncable/syncable_util.h" +#include "components/sync_sessions/lost_navigations_recorder.h" #include "components/sync_sessions/sync_sessions_client.h" #include "components/sync_sessions/synced_tab_delegate.h" #include "components/sync_sessions/synced_window_delegate.h" @@ -77,7 +79,7 @@ // |local_device| is owned by ProfileSyncService, its lifetime exceeds // lifetime of SessionSyncManager. SessionsSyncManager::SessionsSyncManager( - SyncSessionsClient* sessions_client, + sync_sessions::SyncSessionsClient* sessions_client, sync_driver::SyncPrefs* sync_prefs, LocalDeviceInfoProvider* local_device, std::unique_ptr<LocalSessionEventRouter> router, @@ -120,6 +122,10 @@ error_handler_ = std::move(error_handler); sync_processor_ = std::move(sync_processor); + lost_navigations_recorder_ = + base::MakeUnique<sync_sessions::LostNavigationsRecorder>(); + sync_processor_->AddLocalChangeObserver(lost_navigations_recorder_.get()); + local_session_header_node_id_ = TabNodePool::kInvalidTabNodeID; // Make sure we have a machine tag. We do this now (versus earlier) as it's @@ -436,8 +442,13 @@ void SessionsSyncManager::StopSyncing(syncer::ModelType type) { local_event_router_->Stop(); + if (sync_processor_.get() && lost_navigations_recorder_.get()) { + sync_processor_->RemoveLocalChangeObserver( + lost_navigations_recorder_.get()); + } sync_processor_.reset(NULL); error_handler_.reset(); + lost_navigations_recorder_.reset(); session_tracker_.Clear(); local_tab_map_.clear(); local_tab_pool_.Clear();
diff --git a/components/sync_sessions/sessions_sync_manager.h b/components/sync_sessions/sessions_sync_manager.h index 6735bcc..360601b 100644 --- a/components/sync_sessions/sessions_sync_manager.h +++ b/components/sync_sessions/sessions_sync_manager.h
@@ -25,6 +25,7 @@ #include "components/sync/driver/sync_prefs.h" #include "components/sync_sessions/favicon_cache.h" #include "components/sync_sessions/local_session_event_router.h" +#include "components/sync_sessions/lost_navigations_recorder.h" #include "components/sync_sessions/open_tabs_ui_delegate.h" #include "components/sync_sessions/revisit/page_revisit_broadcaster.h" #include "components/sync_sessions/synced_session.h" @@ -370,6 +371,9 @@ // Owns revisiting instrumentation logic for page visit events. PageRevisitBroadcaster page_revisit_broadcaster_; + std::unique_ptr<sync_sessions::LostNavigationsRecorder> + lost_navigations_recorder_; + // Callback to inform interested observer that new sessions data has arrived. base::Closure sessions_updated_callback_;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index eefa129..1ce8e50 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1074,17 +1074,11 @@ "renderer_host/media/shared_memory_buffer_handle.h", "renderer_host/media/shared_memory_buffer_tracker.cc", "renderer_host/media/shared_memory_buffer_tracker.h", - "renderer_host/media/video_capture_buffer_handle.h", - "renderer_host/media/video_capture_buffer_pool.cc", - "renderer_host/media/video_capture_buffer_pool.h", - "renderer_host/media/video_capture_buffer_tracker.h", - "renderer_host/media/video_capture_buffer_tracker_factory.cc", - "renderer_host/media/video_capture_buffer_tracker_factory.h", + "renderer_host/media/video_capture_buffer_tracker_factory_impl.cc", + "renderer_host/media/video_capture_buffer_tracker_factory_impl.h", "renderer_host/media/video_capture_controller.cc", "renderer_host/media/video_capture_controller.h", "renderer_host/media/video_capture_controller_event_handler.h", - "renderer_host/media/video_capture_device_client.cc", - "renderer_host/media/video_capture_device_client.h", "renderer_host/media/video_capture_gpu_jpeg_decoder.cc", "renderer_host/media/video_capture_gpu_jpeg_decoder.h", "renderer_host/media/video_capture_host.cc",
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc index 26e5b48..afcbd003 100644 --- a/content/browser/accessibility/browser_accessibility.cc +++ b/content/browser/accessibility/browser_accessibility.cc
@@ -414,7 +414,7 @@ else start = 0; } - return RelativeToAbsoluteBounds(gfx::RectF(bounds), false); + return bounds; } int end = start + len; @@ -457,7 +457,7 @@ int end_pixel_offset = local_end > 0 ? character_offsets[local_end - 1] : 0; - gfx::Rect child_rect = gfx::ToEnclosingRect(child->GetLocation()); + gfx::Rect child_rect = child->GetPageBoundsRect(); auto text_direction = static_cast<ui::AXTextDirection>( child->GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION)); gfx::Rect child_overlap_rect; @@ -501,7 +501,7 @@ bounds.Union(child_overlap_rect); } - return RelativeToAbsoluteBounds(gfx::RectF(bounds), false); + return bounds; } gfx::Rect BrowserAccessibility::GetScreenBoundsForRange(int start, int len)
diff --git a/content/browser/download/quarantine_win.cc b/content/browser/download/quarantine_win.cc index f45a694..59605bf9 100644 --- a/content/browser/download/quarantine_win.cc +++ b/content/browser/download/quarantine_win.cc
@@ -82,16 +82,6 @@ void RecordAttachmentServicesSaveResult(const base::FilePath& file, HRESULT hr) { bool file_exists = base::PathExists(file); - if (SUCCEEDED(hr)) { - bool motw_exists = file_exists && ZoneIdentifierPresentForFile(file); - RecordAttachmentServicesResult( - file_exists - ? motw_exists ? AttachmentServicesResult::SUCCESS_WITH_MOTW - : AttachmentServicesResult::SUCCESS_WITHOUT_MOTW - : AttachmentServicesResult::SUCCESS_WITHOUT_FILE); - return; - } - switch (hr) { case INET_E_SECURITY_PROBLEM: RecordAttachmentServicesResult( @@ -107,12 +97,26 @@ case E_ACCESSDENIED: case ERROR_ACCESS_DENIED: + // ERROR_ACCESS_DENIED is not a valid HRESULT. However, + // IAttachmentExecute::Save() is known to return it and other system error + // codes in practice. RecordAttachmentServicesResult( file_exists ? AttachmentServicesResult::ACCESS_DENIED_WITH_FILE : AttachmentServicesResult::ACCESS_DENIED_WITHOUT_FILE); break; default: + if (SUCCEEDED(hr)) { + bool motw_exists = file_exists && ZoneIdentifierPresentForFile(file); + RecordAttachmentServicesResult( + file_exists + ? motw_exists ? AttachmentServicesResult::SUCCESS_WITH_MOTW + : AttachmentServicesResult::SUCCESS_WITHOUT_MOTW + : AttachmentServicesResult::SUCCESS_WITHOUT_FILE); + return; + } + + // Failure codes. RecordAttachmentServicesResult( file_exists ? AttachmentServicesResult::OTHER_WITH_FILE : AttachmentServicesResult::OTHER_WITHOUT_FILE);
diff --git a/content/browser/download/quarantine_win_unittest.cc b/content/browser/download/quarantine_win_unittest.cc index f0229f5..95bdc63 100644 --- a/content/browser/download/quarantine_win_unittest.cc +++ b/content/browser/download/quarantine_win_unittest.cc
@@ -6,11 +6,13 @@ #include <wininet.h> -#include "content/browser/download/quarantine.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/macros.h" #include "base/test/histogram_tester.h" +#include "base/test/test_file_util.h" +#include "content/browser/download/quarantine.h" #include "net/base/filename_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -27,6 +29,8 @@ const base::FilePath::CharType kMotwStreamSuffix[] = FILE_PATH_LITERAL(":Zone.Identifier"); +const char kTestData[] = "Hello world!"; + const char* const kUntrustedURLs[] = { "http://example.com/foo", "https://example.com/foo", @@ -68,7 +72,8 @@ for (const auto source_url : kLocalSourceURLs) { SCOPED_TRACE(::testing::Message() << "Trying URL " << source_url); - ASSERT_EQ(5, base::WriteFile(test_file, "Hello", 5u)); + ASSERT_EQ(static_cast<int>(arraysize(kTestData)), + base::WriteFile(test_file, kTestData, arraysize(kTestData))); EXPECT_EQ( QuarantineFileResult::OK, @@ -107,7 +112,8 @@ for (const auto source_url : kUntrustedURLs) { SCOPED_TRACE(::testing::Message() << "Trying URL " << source_url); - ASSERT_EQ(5, base::WriteFile(test_file, "Hello", 5u)); + ASSERT_EQ(static_cast<int>(arraysize(kTestData)), + base::WriteFile(test_file, kTestData, arraysize(kTestData))); EXPECT_EQ( QuarantineFileResult::OK, QuarantineFile(test_file, GURL(source_url), GURL(), kDummyClientGuid)); @@ -145,7 +151,8 @@ for (const auto referrer_url : unsafe_referrers) { SCOPED_TRACE(::testing::Message() << "Trying URL " << referrer_url); - ASSERT_EQ(5, base::WriteFile(test_file, "Hello", 5u)); + ASSERT_EQ(static_cast<int>(arraysize(kTestData)), + base::WriteFile(test_file, kTestData, arraysize(kTestData))); EXPECT_EQ(QuarantineFileResult::OK, QuarantineFile(test_file, GURL("http://example.com/good"), GURL(referrer_url), kDummyClientGuid)); @@ -175,7 +182,8 @@ base::ScopedTempDir test_dir; ASSERT_TRUE(test_dir.CreateUniqueTempDir()); base::FilePath test_file = test_dir.path().AppendASCII("foo.exe"); - ASSERT_EQ(5, base::WriteFile(test_file, "Hello", 5u)); + ASSERT_EQ(static_cast<int>(arraysize(kTestData)), + base::WriteFile(test_file, kTestData, arraysize(kTestData))); EXPECT_EQ(QuarantineFileResult::OK, QuarantineFile(test_file, GURL(), GURL(), kDummyClientGuid)); @@ -195,6 +203,7 @@ // the file is passed to AVScanFile, then there wouldn't be a MOTW attached to // it and the test would fail. TEST(QuarantineWinTest, EmptyFile) { + base::HistogramTester histogram_tester; base::ScopedTempDir test_dir; ASSERT_TRUE(test_dir.CreateUniqueTempDir()); base::FilePath test_file = test_dir.path().AppendASCII("foo.exe"); @@ -207,6 +216,9 @@ ASSERT_TRUE(base::ReadFileToString( base::FilePath(test_file.value() + kMotwStreamSuffix), &motw_contents)); EXPECT_STREQ(kMotwForInternetZone, motw_contents.c_str()); + + // Attachment services shouldn't have been invoked at all. + histogram_tester.ExpectTotalCount("Download.AttachmentServices.Result", 0); } // If there is no client GUID supplied to the QuarantineFile() call, then rather @@ -217,7 +229,8 @@ base::ScopedTempDir test_dir; ASSERT_TRUE(test_dir.CreateUniqueTempDir()); base::FilePath test_file = test_dir.path().AppendASCII("foo.exe"); - ASSERT_EQ(5, base::WriteFile(test_file, "Hello", 5u)); + ASSERT_EQ(static_cast<int>(arraysize(kTestData)), + base::WriteFile(test_file, kTestData, arraysize(kTestData))); EXPECT_EQ(QuarantineFileResult::OK, QuarantineFile(test_file, net::FilePathToFileURL(test_file), GURL(), @@ -235,7 +248,8 @@ base::ScopedTempDir test_dir; ASSERT_TRUE(test_dir.CreateUniqueTempDir()); base::FilePath test_file = test_dir.path().AppendASCII("foo.exe"); - ASSERT_EQ(5, base::WriteFile(test_file, "Hello", 5u)); + ASSERT_EQ(static_cast<int>(arraysize(kTestData)), + base::WriteFile(test_file, kTestData, arraysize(kTestData))); std::string source_url("http://example.com/"); source_url.append(INTERNET_MAX_URL_LENGTH * 2, 'a');
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc index c0ddb1b..4466dab2 100644 --- a/content/browser/frame_host/navigation_controller_impl.cc +++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -772,7 +772,8 @@ bool NavigationControllerImpl::RendererDidNavigate( RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params, - LoadCommittedDetails* details) { + LoadCommittedDetails* details, + bool is_navigation_within_page) { is_initial_navigation_ = false; // Save the previous state before we clobber it. @@ -799,8 +800,7 @@ details->type = ClassifyNavigation(rfh, params); // is_in_page must be computed before the entry gets committed. - details->is_in_page = IsURLInPageNavigation(params.url, params.origin, - params.was_within_same_page, rfh); + details->is_in_page = is_navigation_within_page; switch (details->type) { case NAVIGATION_TYPE_NEW_PAGE:
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h index 20bb0f6..f1dd023 100644 --- a/content/browser/frame_host/navigation_controller_impl.h +++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -149,7 +149,8 @@ bool RendererDidNavigate( RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params, - LoadCommittedDetails* details); + LoadCommittedDetails* details, + bool is_navigation_within_page); // Notifies us that we just became active. This is used by the WebContentsImpl // so that we know to load URLs that were pending as "lazy" loads.
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc index 405e6500..e88f507f 100644 --- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc +++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -27,6 +27,7 @@ #include "content/public/browser/resource_dispatcher_host_delegate.h" #include "content/public/browser/resource_throttle.h" #include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/bindings_policy.h" #include "content/public/common/browser_side_navigation_policy.h" @@ -5968,7 +5969,7 @@ EXPECT_EQ(start_url.GetOrigin().spec(), origin + "/"); } -// A BrowserMessageFilter that delays FrameHostMsg_DidCommitProvisionaLoad IPC +// A BrowserMessageFilter that delays FrameHostMsg_DidCommitProvisionalLoad IPC // message for a specified URL, navigates the WebContents back and then // processes the commit message. class GoBackAndCommitFilter : public BrowserMessageFilter { @@ -5996,7 +5997,7 @@ if (message.type() != FrameHostMsg_DidCommitProvisionalLoad::ID) return false; - // Parse the IPC message so the URL can be checked agains the expected one. + // Parse the IPC message so the URL can be checked against the expected one. base::PickleIterator iter(message); FrameHostMsg_DidCommitProvisionalLoad_Params validated_params; if (!IPC::ParamTraits<FrameHostMsg_DidCommitProvisionalLoad_Params>::Read( @@ -6137,4 +6138,100 @@ EXPECT_FALSE(favicon_status3.valid); } +namespace { + +// A BrowserMessageFilter that delays the FrameHostMsg_RunJavaScriptMessage IPC +// message until a commit happens on a given WebContents. This allows testing a +// race condition. +class AllowDialogIPCOnCommitFilter : public BrowserMessageFilter, + public WebContentsDelegate { + public: + AllowDialogIPCOnCommitFilter(WebContents* web_contents) + : BrowserMessageFilter(FrameMsgStart), + render_frame_host_(web_contents->GetMainFrame()) { + web_contents_observer_.Observe(web_contents); + } + + protected: + ~AllowDialogIPCOnCommitFilter() override {} + + private: + // BrowserMessageFilter: + bool OnMessageReceived(const IPC::Message& message) override { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (message.type() != FrameHostMsg_RunJavaScriptMessage::ID) + return false; + + // Suspend the message. + web_contents_observer_.SetCallback( + base::Bind(&RenderFrameHost::OnMessageReceived, + base::Unretained(render_frame_host_), message)); + return true; + } + + // WebContentsDelegate: + JavaScriptDialogManager* GetJavaScriptDialogManager( + WebContents* source) override { + CHECK(false); + return nullptr; // agh compiler + } + + // Separate because WebContentsObserver and BrowserMessageFilter each have an + // OnMessageReceived function; this is the simplest way to disambiguate. + class : public WebContentsObserver { + public: + using Callback = base::Callback<bool()>; + + using WebContentsObserver::Observe; + + void SetCallback(Callback callback) { callback_ = callback; } + + private: + void DidNavigateAnyFrame(RenderFrameHost* render_frame_host, + const LoadCommittedDetails& details, + const FrameNavigateParams& params) override { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + // Resume the message. + callback_.Run(); + } + + Callback callback_; + } web_contents_observer_; + + RenderFrameHost* render_frame_host_; + + DISALLOW_COPY_AND_ASSIGN(AllowDialogIPCOnCommitFilter); +}; + +} // namespace + +// Check that swapped out frames cannot spawn JavaScript dialogs. +IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, + NoDialogsFromSwappedOutFrames) { + // Start on a normal page. + GURL url1 = embedded_test_server()->GetURL( + "/navigation_controller/beforeunload_dialog.html"); + EXPECT_TRUE(NavigateToURL(shell(), url1)); + + // Add a filter to allow us to force an IPC race. + WebContents* web_contents = shell()->web_contents(); + scoped_refptr<AllowDialogIPCOnCommitFilter> filter = + new AllowDialogIPCOnCommitFilter(web_contents); + web_contents->SetDelegate(filter.get()); + web_contents->GetMainFrame()->GetProcess()->AddFilter(filter.get()); + + // Use a chrome:// url to force the second page to be in a different process. + GURL url2(std::string(kChromeUIScheme) + url::kStandardSchemeSeparator + + kChromeUIGpuHost); + EXPECT_TRUE(NavigateToURL(shell(), url2)); + + // What happens now is that attempting to unload the first page will trigger a + // JavaScript alert but allow navigation. The alert IPC will be suspended by + // the message filter. The commit of the second page will unblock the IPC. If + // the dialog IPC is allowed to spawn a dialog, the call by the WebContents to + // its delegate to get the JavaScriptDialogManager will cause a CHECK and the + // test will fail. +} + } // namespace content
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc index aafb3ccf..0060835 100644 --- a/content/browser/frame_host/navigator_impl.cc +++ b/content/browser/frame_host/navigator_impl.cc
@@ -562,8 +562,8 @@ int old_entry_count = controller_->GetEntryCount(); LoadCommittedDetails details; - bool did_navigate = controller_->RendererDidNavigate(render_frame_host, - params, &details); + bool did_navigate = controller_->RendererDidNavigate( + render_frame_host, params, &details, is_navigation_within_page); // If the history length and/or offset changed, update other renderers in the // FrameTree.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 8da6a4d..1cf8c1fe 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1585,6 +1585,11 @@ const GURL& frame_url, JavaScriptMessageType type, IPC::Message* reply_msg) { + if (!is_active()) { + JavaScriptDialogClosed(reply_msg, true, base::string16(), true); + return; + } + int32_t message_length = static_cast<int32_t>(message.length()); if (GetParent()) { UMA_HISTOGRAM_COUNTS("JSDialogs.CharacterCount.Subframe", message_length);
diff --git a/content/browser/loader/mime_sniffing_resource_handler.cc b/content/browser/loader/mime_sniffing_resource_handler.cc index 1c157df..25a4794 100644 --- a/content/browser/loader/mime_sniffing_resource_handler.cc +++ b/content/browser/loader/mime_sniffing_resource_handler.cc
@@ -414,7 +414,8 @@ std::unique_ptr<ResourceHandler> handler( host_->CreateResourceHandlerForDownload(request(), true, // is_content_initiated - must_download)); + must_download, + false /* is_new_request */)); intercepting_handler_->UseNewHandler(std::move(handler), std::string()); return true; }
diff --git a/content/browser/loader/mime_sniffing_resource_handler_unittest.cc b/content/browser/loader/mime_sniffing_resource_handler_unittest.cc index ed407eb1..4964e28 100644 --- a/content/browser/loader/mime_sniffing_resource_handler_unittest.cc +++ b/content/browser/loader/mime_sniffing_resource_handler_unittest.cc
@@ -151,7 +151,8 @@ std::unique_ptr<ResourceHandler> CreateResourceHandlerForDownload( net::URLRequest* request, bool is_content_initiated, - bool must_download) override { + bool must_download, + bool is_new_request) override { return CreateNewResourceHandler(); }
diff --git a/content/browser/loader/mojo_async_resource_handler_unittest.cc b/content/browser/loader/mojo_async_resource_handler_unittest.cc index d20c649..5386d6e 100644 --- a/content/browser/loader/mojo_async_resource_handler_unittest.cc +++ b/content/browser/loader/mojo_async_resource_handler_unittest.cc
@@ -84,6 +84,7 @@ ResourceContext* resource_context, bool is_content_initiated, bool must_download, + bool is_new_request, ScopedVector<ResourceThrottle>* throttles) override { ADD_FAILURE() << "DownloadStarting should not be called."; }
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 2b3539ed..aa3e925 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -648,15 +648,17 @@ ResourceDispatcherHostImpl::CreateResourceHandlerForDownload( net::URLRequest* request, bool is_content_initiated, - bool must_download) { + bool must_download, + bool is_new_request) { DCHECK(!create_download_handler_intercept_.is_null()); // TODO(ananta) // Find a better way to create the download handler and notifying the // delegate of the download start. std::unique_ptr<ResourceHandler> handler = create_download_handler_intercept_.Run(request); - handler = HandleDownloadStarted(request, std::move(handler), - is_content_initiated, must_download); + handler = + HandleDownloadStarted(request, std::move(handler), is_content_initiated, + must_download, is_new_request); return handler; } @@ -2353,8 +2355,9 @@ blob_context->context()->GetBlobDataFromPublicURL( request->original_url())); } - handler = HandleDownloadStarted(request.get(), std::move(handler), - is_content_initiated, true); + handler = HandleDownloadStarted( + request.get(), std::move(handler), is_content_initiated, + true /* force_download */, true /* is_new_request */); } BeginRequestInternal(std::move(request), std::move(handler)); } @@ -2684,14 +2687,15 @@ net::URLRequest* request, std::unique_ptr<ResourceHandler> handler, bool is_content_initiated, - bool must_download) { + bool must_download, + bool is_new_request) { if (delegate()) { const ResourceRequestInfoImpl* request_info( ResourceRequestInfoImpl::ForRequest(request)); ScopedVector<ResourceThrottle> throttles; - delegate()->DownloadStarting( - request, request_info->GetContext(), is_content_initiated, true, - &throttles); + delegate()->DownloadStarting(request, request_info->GetContext(), + is_content_initiated, true, is_new_request, + &throttles); if (!throttles.empty()) { handler.reset(new ThrottlingResourceHandler(std::move(handler), request, std::move(throttles)));
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h index 76e6d282..1d42442 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.h +++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -234,7 +234,8 @@ virtual std::unique_ptr<ResourceHandler> CreateResourceHandlerForDownload( net::URLRequest* request, bool is_content_initiated, - bool must_download); + bool must_download, + bool is_new_request); // Called to determine whether the response to |request| should be intercepted // and handled as a stream. Streams are used to pass direct access to a @@ -651,7 +652,8 @@ net::URLRequest* request, std::unique_ptr<ResourceHandler> handler, bool is_content_initiated, - bool must_download); + bool must_download, + bool is_new_request); LoaderMap pending_loaders_;
diff --git a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc index 60d20ff..9b08283 100644 --- a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc +++ b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
@@ -20,8 +20,7 @@ #include "build/build_config.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/frame_host/render_frame_host_impl.h" -#include "content/browser/renderer_host/media/video_capture_buffer_handle.h" -#include "content/browser/renderer_host/media/video_capture_buffer_pool.h" +#include "content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.h" #include "content/browser/renderer_host/render_view_host_factory.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" @@ -38,6 +37,7 @@ #include "media/base/video_frame.h" #include "media/base/video_util.h" #include "media/base/yuv_convert.h" +#include "media/capture/video/video_capture_buffer_pool_impl.h" #include "skia/ext/platform_canvas.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -333,7 +333,8 @@ const base::Closure& error_callback) : report_callback_(report_callback), error_callback_(error_callback) { - buffer_pool_ = new VideoCaptureBufferPoolImpl(2); + buffer_pool_ = new media::VideoCaptureBufferPoolImpl( + base::MakeUnique<VideoCaptureBufferTrackerFactoryImpl>(), 2); } ~StubClient() override {} @@ -352,10 +353,11 @@ media::VideoPixelFormat format, media::VideoPixelStorage storage) override { CHECK_EQ(format, media::PIXEL_FORMAT_I420); - int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId; // Ignored. + int buffer_id_to_drop = + media::VideoCaptureBufferPool::kInvalidId; // Ignored. const int buffer_id = buffer_pool_->ReserveForProducer( dimensions, format, storage, &buffer_id_to_drop); - if (buffer_id == VideoCaptureBufferPool::kInvalidId) + if (buffer_id == media::VideoCaptureBufferPool::kInvalidId) return NULL; return std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>( @@ -406,7 +408,7 @@ CHECK_EQ(format, media::PIXEL_FORMAT_I420); const int buffer_id = buffer_pool_->ResurrectLastForProducer(dimensions, format, storage); - if (buffer_id == VideoCaptureBufferPool::kInvalidId) + if (buffer_id == media::VideoCaptureBufferPool::kInvalidId) return nullptr; return std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>( new AutoReleaseBuffer( @@ -423,9 +425,10 @@ private: class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { public: - AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool, - std::unique_ptr<VideoCaptureBufferHandle> buffer_handle, - int buffer_id) + AutoReleaseBuffer( + const scoped_refptr<media::VideoCaptureBufferPool>& pool, + std::unique_ptr<media::VideoCaptureBufferHandle> buffer_handle, + int buffer_id) : id_(buffer_id), pool_(pool), buffer_handle_(std::move(buffer_handle)) { @@ -450,11 +453,11 @@ ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); } const int id_; - const scoped_refptr<VideoCaptureBufferPool> pool_; - const std::unique_ptr<VideoCaptureBufferHandle> buffer_handle_; + const scoped_refptr<media::VideoCaptureBufferPool> pool_; + const std::unique_ptr<media::VideoCaptureBufferHandle> buffer_handle_; }; - scoped_refptr<VideoCaptureBufferPool> buffer_pool_; + scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_; base::Callback<void(SkColor, const gfx::Size&)> report_callback_; base::Closure error_callback_;
diff --git a/content/browser/renderer_host/DEPS b/content/browser/renderer_host/DEPS index 3c872db..79ab5a9 100644 --- a/content/browser/renderer_host/DEPS +++ b/content/browser/renderer_host/DEPS
@@ -3,7 +3,6 @@ "+components/display_compositor", "+services/ui/public", "+third_party/zlib", - "+third_party/libyuv", # The renderer_host files should only call upwards in the layering via the # delegate interfaces.
diff --git a/content/browser/renderer_host/media/gpu_memory_buffer_handle.h b/content/browser/renderer_host/media/gpu_memory_buffer_handle.h index 05e5ccf6..4dd61a22 100644 --- a/content/browser/renderer_host/media/gpu_memory_buffer_handle.h +++ b/content/browser/renderer_host/media/gpu_memory_buffer_handle.h
@@ -5,7 +5,7 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_GPU_MEMORY_BUFFER_HANDLE_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_GPU_MEMORY_BUFFER_HANDLE_H_ -#include "content/browser/renderer_host/media/video_capture_buffer_handle.h" +#include "media/capture/video/video_capture_buffer_handle.h" namespace content { @@ -13,7 +13,7 @@ // A simple proxy that implements the BufferHandle interface, providing // accessors to GpuMemoryBufferTracker's memory and properties. -class GpuMemoryBufferBufferHandle : public VideoCaptureBufferHandle { +class GpuMemoryBufferBufferHandle : public media::VideoCaptureBufferHandle { public: // |tracker| must outlive GpuMemoryBufferBufferHandle. This is ensured since // a tracker is pinned until ownership of this GpuMemoryBufferBufferHandle
diff --git a/content/browser/renderer_host/media/gpu_memory_buffer_tracker.cc b/content/browser/renderer_host/media/gpu_memory_buffer_tracker.cc index b4e601c9..a19461f7 100644 --- a/content/browser/renderer_host/media/gpu_memory_buffer_tracker.cc +++ b/content/browser/renderer_host/media/gpu_memory_buffer_tracker.cc
@@ -58,7 +58,7 @@ return true; } -std::unique_ptr<VideoCaptureBufferHandle> +std::unique_ptr<media::VideoCaptureBufferHandle> GpuMemoryBufferTracker::GetBufferHandle() { DCHECK_EQ(gpu_memory_buffers_.size(), media::VideoFrame::NumPlanes(pixel_format()));
diff --git a/content/browser/renderer_host/media/gpu_memory_buffer_tracker.h b/content/browser/renderer_host/media/gpu_memory_buffer_tracker.h index e12a88ce..c3f13d16 100644 --- a/content/browser/renderer_host/media/gpu_memory_buffer_tracker.h +++ b/content/browser/renderer_host/media/gpu_memory_buffer_tracker.h
@@ -5,13 +5,13 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_GPU_MEMORY_BUFFER_TRACKER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_GPU_MEMORY_BUFFER_TRACKER_H_ -#include "content/browser/renderer_host/media/video_capture_buffer_tracker.h" +#include "media/capture/video/video_capture_buffer_tracker.h" namespace content { // Tracker specifics for GpuMemoryBuffer. Owns GpuMemoryBuffers and its // associated pixel dimensions. -class GpuMemoryBufferTracker final : public VideoCaptureBufferTracker { +class GpuMemoryBufferTracker final : public media::VideoCaptureBufferTracker { public: GpuMemoryBufferTracker(); ~GpuMemoryBufferTracker() override; @@ -21,7 +21,7 @@ media::VideoPixelStorage storage_type, base::Lock* lock) override; - std::unique_ptr<VideoCaptureBufferHandle> GetBufferHandle() override; + std::unique_ptr<media::VideoCaptureBufferHandle> GetBufferHandle() override; bool ShareToProcess(base::ProcessHandle process_handle, base::SharedMemoryHandle* new_handle) override;
diff --git a/content/browser/renderer_host/media/shared_memory_buffer_handle.h b/content/browser/renderer_host/media/shared_memory_buffer_handle.h index 3f327528..da29983 100644 --- a/content/browser/renderer_host/media/shared_memory_buffer_handle.h +++ b/content/browser/renderer_host/media/shared_memory_buffer_handle.h
@@ -5,7 +5,7 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_SHARED_MEMORY_BUFFER_HANDLE_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_SHARED_MEMORY_BUFFER_HANDLE_H_ -#include "content/browser/renderer_host/media/video_capture_buffer_handle.h" +#include "media/capture/video/video_capture_buffer_handle.h" namespace content { @@ -13,7 +13,7 @@ // A simple proxy that implements the BufferHandle interface, providing // accessors to SharedMemTracker's memory and properties. -class SharedMemoryBufferHandle : public VideoCaptureBufferHandle { +class SharedMemoryBufferHandle : public media::VideoCaptureBufferHandle { public: // |tracker| must outlive SimpleBufferHandle. This is ensured since a // tracker is pinned until ownership of this SimpleBufferHandle is returned
diff --git a/content/browser/renderer_host/media/shared_memory_buffer_tracker.cc b/content/browser/renderer_host/media/shared_memory_buffer_tracker.cc index 27dca64e..af9235be 100644 --- a/content/browser/renderer_host/media/shared_memory_buffer_tracker.cc +++ b/content/browser/renderer_host/media/shared_memory_buffer_tracker.cc
@@ -30,7 +30,7 @@ return shared_memory_.CreateAndMapAnonymous(mapped_size_); } -std::unique_ptr<VideoCaptureBufferHandle> +std::unique_ptr<media::VideoCaptureBufferHandle> SharedMemoryBufferTracker::GetBufferHandle() { return base::MakeUnique<SharedMemoryBufferHandle>(this); }
diff --git a/content/browser/renderer_host/media/shared_memory_buffer_tracker.h b/content/browser/renderer_host/media/shared_memory_buffer_tracker.h index 2aad666..81504378 100644 --- a/content/browser/renderer_host/media/shared_memory_buffer_tracker.h +++ b/content/browser/renderer_host/media/shared_memory_buffer_tracker.h
@@ -5,12 +5,13 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_SHARED_MEMORY_BUFFER_TRACKER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_SHARED_MEMORY_BUFFER_TRACKER_H_ -#include "content/browser/renderer_host/media/video_capture_buffer_tracker.h" +#include "media/capture/video/video_capture_buffer_tracker.h" namespace content { // Tracker specifics for SharedMemory. -class SharedMemoryBufferTracker final : public VideoCaptureBufferTracker { +class SharedMemoryBufferTracker final + : public media::VideoCaptureBufferTracker { public: SharedMemoryBufferTracker(); @@ -19,7 +20,7 @@ media::VideoPixelStorage storage_type, base::Lock* lock) override; - std::unique_ptr<VideoCaptureBufferHandle> GetBufferHandle() override; + std::unique_ptr<media::VideoCaptureBufferHandle> GetBufferHandle() override; bool ShareToProcess(base::ProcessHandle process_handle, base::SharedMemoryHandle* new_handle) override; bool ShareToProcess2(int plane,
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc index 9933c76..e31d446 100644 --- a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc +++ b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
@@ -4,7 +4,7 @@ // Unit test for VideoCaptureBufferPool. -#include "content/browser/renderer_host/media/video_capture_buffer_pool.h" +#include "media/capture/video/video_capture_buffer_pool.h" #include <stddef.h> #include <stdint.h> @@ -24,9 +24,10 @@ #include "cc/test/test_web_graphics_context_3d.h" #include "components/display_compositor/buffer_queue.h" #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" -#include "content/browser/renderer_host/media/video_capture_buffer_handle.h" +#include "content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.h" #include "content/browser/renderer_host/media/video_capture_controller.h" #include "media/base/video_frame.h" +#include "media/capture/video/video_capture_buffer_pool_impl.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -48,6 +49,10 @@ static const int kTestBufferPoolSize = 3; +// Note that this test does not exercise the class VideoCaptureBufferPool +// in isolation. The "unit under test" is an instance of VideoCaptureBufferPool +// with some context that is specific to renderer_host/media, and therefore +// this test must live here and not in media/capture/video. class VideoCaptureBufferPoolTest : public testing::TestWithParam<PixelFormatAndStorage> { protected: @@ -119,8 +124,8 @@ // This is a generic Buffer tracker class Buffer { public: - Buffer(const scoped_refptr<VideoCaptureBufferPool> pool, - std::unique_ptr<VideoCaptureBufferHandle> buffer_handle, + Buffer(const scoped_refptr<media::VideoCaptureBufferPool> pool, + std::unique_ptr<media::VideoCaptureBufferHandle> buffer_handle, int id) : id_(id), pool_(pool), buffer_handle_(std::move(buffer_handle)) {} ~Buffer() { pool_->RelinquishProducerReservation(id()); } @@ -130,13 +135,15 @@ private: const int id_; - const scoped_refptr<VideoCaptureBufferPool> pool_; - const std::unique_ptr<VideoCaptureBufferHandle> buffer_handle_; + const scoped_refptr<media::VideoCaptureBufferPool> pool_; + const std::unique_ptr<media::VideoCaptureBufferHandle> buffer_handle_; }; VideoCaptureBufferPoolTest() : expected_dropped_id_(0), - pool_(new VideoCaptureBufferPoolImpl(kTestBufferPoolSize)) {} + pool_(new media::VideoCaptureBufferPoolImpl( + base::MakeUnique<VideoCaptureBufferTrackerFactoryImpl>(), + kTestBufferPoolSize)) {} #if !defined(OS_ANDROID) void SetUp() override { @@ -161,11 +168,11 @@ const int buffer_id = pool_->ReserveForProducer( dimensions, format_and_storage.pixel_format, format_and_storage.pixel_storage, &buffer_id_to_drop); - if (buffer_id == VideoCaptureBufferPool::kInvalidId) + if (buffer_id == media::VideoCaptureBufferPool::kInvalidId) return std::unique_ptr<Buffer>(); EXPECT_EQ(expected_dropped_id_, buffer_id_to_drop); - std::unique_ptr<VideoCaptureBufferHandle> buffer_handle = + std::unique_ptr<media::VideoCaptureBufferHandle> buffer_handle = pool_->GetBufferHandle(buffer_id); return std::unique_ptr<Buffer>( new Buffer(pool_, std::move(buffer_handle), buffer_id)); @@ -177,7 +184,7 @@ const int buffer_id = pool_->ResurrectLastForProducer( dimensions, format_and_storage.pixel_format, format_and_storage.pixel_storage); - if (buffer_id == VideoCaptureBufferPool::kInvalidId) + if (buffer_id == media::VideoCaptureBufferPool::kInvalidId) return std::unique_ptr<Buffer>(); return std::unique_ptr<Buffer>( new Buffer(pool_, pool_->GetBufferHandle(buffer_id), buffer_id)); @@ -185,7 +192,7 @@ base::MessageLoop loop_; int expected_dropped_id_; - scoped_refptr<VideoCaptureBufferPool> pool_; + scoped_refptr<media::VideoCaptureBufferPool> pool_; private: #if !defined(OS_ANDROID) @@ -204,7 +211,7 @@ size_hi, 0.0, GetParam().pixel_format, GetParam().pixel_storage); // Reallocation won't happen for the first part of the test. - ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); + ExpectDroppedId(media::VideoCaptureBufferPool::kInvalidId); // The buffer pool should have zero utilization before any buffers have been // reserved. @@ -334,7 +341,7 @@ ASSERT_EQ(3.0 / kTestBufferPoolSize, pool_->GetBufferPoolUtilization()); void* const memory_pointer_hi = buffer2->data(); buffer2.reset(); // Frees it. - ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); + ExpectDroppedId(media::VideoCaptureBufferPool::kInvalidId); ASSERT_EQ(2.0 / kTestBufferPoolSize, pool_->GetBufferPoolUtilization()); buffer2 = ReserveBuffer(size_lo, GetParam()); void* const memory_pointer_lo = buffer2->data(); @@ -369,7 +376,7 @@ // Tests that a previously-released buffer can be immediately resurrected under // normal conditions. TEST_P(VideoCaptureBufferPoolTest, ResurrectsLastBuffer) { - ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); + ExpectDroppedId(media::VideoCaptureBufferPool::kInvalidId); // At the start, there should be nothing to resurrect. std::unique_ptr<Buffer> resurrected = @@ -413,7 +420,7 @@ // Tests that a buffer cannot be resurrected if its properties do not match. TEST_P(VideoCaptureBufferPoolTest, DoesNotResurrectIfPropertiesNotMatched) { - ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); + ExpectDroppedId(media::VideoCaptureBufferPool::kInvalidId); // Reserve a 10x10 buffer, fill it with 0xcd values, and release it. std::unique_ptr<Buffer> original = @@ -463,7 +470,7 @@ // Tests that the buffers are managed by the pool such that the last-released // buffer is kept around as long as possible (for successful resurrection). TEST_P(VideoCaptureBufferPoolTest, AvoidsClobberingForResurrectingLastBuffer) { - ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); + ExpectDroppedId(media::VideoCaptureBufferPool::kInvalidId); // Reserve a 10x10 buffer, fill it with 0xde values, and release it. std::unique_ptr<Buffer> original =
diff --git a/content/browser/renderer_host/media/video_capture_buffer_tracker_factory.h b/content/browser/renderer_host/media/video_capture_buffer_tracker_factory.h deleted file mode 100644 index e687471..0000000 --- a/content/browser/renderer_host/media/video_capture_buffer_tracker_factory.h +++ /dev/null
@@ -1,22 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_TRACKER_FACTORY_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_TRACKER_FACTORY_H_ - -#include <memory> - -#include "content/browser/renderer_host/media/video_capture_buffer_tracker.h" - -namespace content { - -class VideoCaptureBufferTrackerFactory { - public: - static std::unique_ptr<VideoCaptureBufferTracker> CreateTracker( - media::VideoPixelStorage storage); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_TRACKER_FACTORY_H_
diff --git a/content/browser/renderer_host/media/video_capture_buffer_tracker_factory.cc b/content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.cc similarity index 79% rename from content/browser/renderer_host/media/video_capture_buffer_tracker_factory.cc rename to content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.cc index c58f564..1d2831b 100644 --- a/content/browser/renderer_host/media/video_capture_buffer_tracker_factory.cc +++ b/content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/renderer_host/media/video_capture_buffer_tracker_factory.h" +#include "content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.h" #include "base/memory/ptr_util.h" @@ -11,9 +11,8 @@ namespace content { -// static -std::unique_ptr<VideoCaptureBufferTracker> -VideoCaptureBufferTrackerFactory::CreateTracker( +std::unique_ptr<media::VideoCaptureBufferTracker> +VideoCaptureBufferTrackerFactoryImpl::CreateTracker( media::VideoPixelStorage storage) { switch (storage) { case media::PIXEL_STORAGE_GPUMEMORYBUFFER: @@ -22,7 +21,7 @@ return base::MakeUnique<SharedMemoryBufferTracker>(); } NOTREACHED(); - return std::unique_ptr<VideoCaptureBufferTracker>(); + return std::unique_ptr<media::VideoCaptureBufferTracker>(); } } // namespace content
diff --git a/content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.h b/content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.h new file mode 100644 index 0000000..a256963 --- /dev/null +++ b/content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.h
@@ -0,0 +1,24 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_TRACKER_FACTORY_IMPL_H_ +#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_TRACKER_FACTORY_IMPL_H_ + +#include <memory> + +#include "content/common/content_export.h" +#include "media/capture/video/video_capture_buffer_tracker_factory.h" + +namespace content { + +class CONTENT_EXPORT VideoCaptureBufferTrackerFactoryImpl + : public media::VideoCaptureBufferTrackerFactory { + public: + std::unique_ptr<media::VideoCaptureBufferTracker> CreateTracker( + media::VideoPixelStorage storage) override; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_TRACKER_FACTORY_IMPL_H_
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc index c922822..c594c3b 100644 --- a/content/browser/renderer_host/media/video_capture_controller.cc +++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -18,13 +18,14 @@ #include "build/build_config.h" #include "components/display_compositor/gl_helper.h" #include "content/browser/renderer_host/media/media_stream_manager.h" -#include "content/browser/renderer_host/media/video_capture_buffer_pool.h" -#include "content/browser/renderer_host/media/video_capture_device_client.h" +#include "content/browser/renderer_host/media/video_capture_buffer_tracker_factory_impl.h" #include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h" #include "content/browser/renderer_host/media/video_capture_manager.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_switches.h" #include "media/base/video_frame.h" +#include "media/capture/video/video_capture_buffer_pool_impl.h" +#include "media/capture/video/video_capture_device_client.h" #if !defined(OS_ANDROID) #include "content/browser/compositor/image_transport_factory.h" @@ -79,14 +80,14 @@ #endif } -std::unique_ptr<VideoCaptureJpegDecoder> CreateGpuJpegDecoder( - const VideoCaptureJpegDecoder::DecodeDoneCB& decode_done_cb) { +std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder( + const media::VideoCaptureJpegDecoder::DecodeDoneCB& decode_done_cb) { return base::MakeUnique<VideoCaptureGpuJpegDecoder>(decode_done_cb); } -// Decorator for VideoFrameReceiver that forwards all incoming calls +// Decorator for media::VideoFrameReceiver that forwards all incoming calls // to the Browser IO thread. -class VideoFrameReceiverOnIOThread : public VideoFrameReceiver { +class VideoFrameReceiverOnIOThread : public media::VideoFrameReceiver { public: explicit VideoFrameReceiverOnIOThread( const base::WeakPtr<VideoFrameReceiver>& receiver) @@ -176,7 +177,9 @@ }; VideoCaptureController::VideoCaptureController(int max_buffers) - : buffer_pool_(new VideoCaptureBufferPoolImpl(max_buffers)), + : buffer_pool_(new media::VideoCaptureBufferPoolImpl( + base::MakeUnique<VideoCaptureBufferTrackerFactoryImpl>(), + max_buffers)), state_(VIDEO_CAPTURE_STATE_STARTED), has_received_frames_(false), weak_ptr_factory_(this) { @@ -191,7 +194,7 @@ std::unique_ptr<media::VideoCaptureDevice::Client> VideoCaptureController::NewDeviceClient() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - return base::MakeUnique<VideoCaptureDeviceClient>( + return base::MakeUnique<media::VideoCaptureDeviceClient>( base::MakeUnique<VideoFrameReceiverOnIOThread>( this->GetWeakPtrForIOThread()), buffer_pool_, @@ -408,7 +411,7 @@ const scoped_refptr<VideoFrame>& frame) { DCHECK_CURRENTLY_ON(BrowserThread::IO); const int buffer_id = buffer->id(); - DCHECK_NE(buffer_id, VideoCaptureBufferPool::kInvalidId); + DCHECK_NE(buffer_id, media::VideoCaptureBufferPool::kInvalidId); int count = 0; if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h index 9674bdd0..25c826b 100644 --- a/content/browser/renderer_host/media/video_capture_controller.h +++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -52,24 +52,15 @@ #include "content/common/content_export.h" #include "content/common/media/video_capture.h" #include "media/base/video_capture_types.h" -#include "media/capture/video/video_capture_device.h" +#include "media/capture/video/video_frame_receiver.h" + +namespace media { +class VideoCaptureBufferPool; +} namespace content { -class VideoCaptureBufferPool; -class CONTENT_EXPORT VideoFrameReceiver { - public: - virtual ~VideoFrameReceiver(){}; - - virtual void OnIncomingCapturedVideoFrame( - std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer, - const scoped_refptr<media::VideoFrame>& frame) = 0; - virtual void OnError() = 0; - virtual void OnLog(const std::string& message) = 0; - virtual void OnBufferDestroyed(int buffer_id_to_drop) = 0; -}; - -class CONTENT_EXPORT VideoCaptureController : public VideoFrameReceiver { +class CONTENT_EXPORT VideoCaptureController : public media::VideoFrameReceiver { public: // |max_buffers| is the maximum number of video frame buffers in-flight at any // one time. This value should be based on the logical capacity of the @@ -137,7 +128,7 @@ bool has_received_frames() const { return has_received_frames_; } - // Implementation of VideoFrameReceiver interface: + // Implementation of media::VideoFrameReceiver interface: void OnIncomingCapturedVideoFrame( std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer, const scoped_refptr<media::VideoFrame>& frame) override; @@ -164,7 +155,7 @@ const ControllerClients& clients); // The pool of shared-memory buffers used for capturing. - const scoped_refptr<VideoCaptureBufferPool> buffer_pool_; + const scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_; // All clients served by this controller. ControllerClients controller_clients_;
diff --git a/content/browser/renderer_host/media/video_capture_device_client_unittest.cc b/content/browser/renderer_host/media/video_capture_device_client_unittest.cc index 2260efb..507af16 100644 --- a/content/browser/renderer_host/media/video_capture_device_client_unittest.cc +++ b/content/browser/renderer_host/media/video_capture_device_client_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/renderer_host/media/video_capture_device_client.h" +#include "media/capture/video/video_capture_device_client.h" #include <stddef.h> @@ -15,10 +15,10 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" -#include "content/browser/renderer_host/media/video_capture_buffer_pool.h" #include "content/browser/renderer_host/media/video_capture_controller.h" #include "content/public/test/test_browser_thread_bundle.h" #include "media/base/limits.h" +#include "media/capture/video/video_capture_buffer_pool.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -49,6 +49,11 @@ } }; +// Note that this test does not exercise the class VideoCaptureDeviceClient +// in isolation. The "unit under test" is an instance of +// VideoCaptureDeviceClient with some context that is specific to +// renderer_host/media, and therefore this test must live here and not in +// media/capture/video. class VideoCaptureDeviceClientTest : public ::testing::Test { public: VideoCaptureDeviceClientTest()
diff --git a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h index 9367df2..672735eb 100644 --- a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h +++ b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h
@@ -18,7 +18,7 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread.h" #include "content/common/content_export.h" -#include "media/capture/video/video_capture_device.h" +#include "media/capture/video/video_capture_jpeg_decoder.h" #include "media/video/jpeg_decode_accelerator.h" namespace gpu { @@ -31,40 +31,6 @@ namespace content { -class CONTENT_EXPORT VideoCaptureJpegDecoder { - public: - // Enumeration of decoder status. The enumeration is published for clients to - // decide the behavior according to STATUS. - enum STATUS { - INIT_PENDING, // Default value while waiting initialization finished. - INIT_PASSED, // Initialization succeed. - FAILED, // JPEG decode is not supported, initialization failed, or - // decode error. - }; - - using DecodeDoneCB = base::Callback<void( - std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>, - const scoped_refptr<media::VideoFrame>&)>; - - virtual ~VideoCaptureJpegDecoder() {} - - // Creates and intializes decoder asynchronously. - virtual void Initialize() = 0; - - // Returns initialization status. - virtual STATUS GetStatus() const = 0; - - // Decodes a JPEG picture. - virtual void DecodeCapturedData( - const uint8_t* data, - size_t in_buffer_size, - const media::VideoCaptureFormat& frame_format, - base::TimeTicks reference_time, - base::TimeDelta timestamp, - std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> - out_buffer) = 0; -}; - // Adapter to GpuJpegDecodeAccelerator for VideoCaptureDevice::Client. It takes // care of GpuJpegDecodeAccelerator creation, shared memory, and threading // issues. @@ -73,7 +39,7 @@ // on the same thread. JpegDecodeAccelerator::Client methods should be called on // the IO thread. class CONTENT_EXPORT VideoCaptureGpuJpegDecoder - : public VideoCaptureJpegDecoder, + : public media::VideoCaptureJpegDecoder, public media::JpegDecodeAccelerator::Client, public base::NonThreadSafe, public base::SupportsWeakPtr<VideoCaptureGpuJpegDecoder> {
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 5fb4834..fe65bc1d0 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -440,6 +440,9 @@ has_composition_text_(false), accept_return_character_(false), begin_frame_source_(nullptr), + needs_begin_frames_(false), + needs_flush_input_(false), + added_frame_observer_(false), synthetic_move_sent_(false), cursor_visibility_state_in_renderer_(UNKNOWN), #if defined(OS_WIN) @@ -712,10 +715,25 @@ } void RenderWidgetHostViewAura::SetNeedsBeginFrames(bool needs_begin_frames) { + needs_begin_frames_ = needs_begin_frames; + UpdateNeedsBeginFramesInternal(); +} + +void RenderWidgetHostViewAura::OnSetNeedsFlushInput() { + needs_flush_input_ = true; + UpdateNeedsBeginFramesInternal(); +} + +void RenderWidgetHostViewAura::UpdateNeedsBeginFramesInternal() { if (!begin_frame_source_) return; - if (needs_begin_frames) + bool needs_frame = needs_begin_frames_ || needs_flush_input_; + if (needs_frame == added_frame_observer_) + return; + + added_frame_observer_ = needs_frame; + if (needs_frame) begin_frame_source_->AddObserver(this); else begin_frame_source_->RemoveObserver(this); @@ -723,6 +741,9 @@ void RenderWidgetHostViewAura::OnBeginFrame( const cc::BeginFrameArgs& args) { + needs_flush_input_ = false; + host_->FlushInput(); + UpdateNeedsBeginFramesInternal(); host_->Send(new ViewMsg_BeginFrame(host_->GetRoutingID(), args)); last_begin_frame_args_ = args; } @@ -2924,12 +2945,12 @@ void RenderWidgetHostViewAura::SetBeginFrameSource( cc::BeginFrameSource* source) { - bool needs_begin_frames = host_->needs_begin_frames(); - if (begin_frame_source_ && needs_begin_frames) + if (begin_frame_source_ && added_frame_observer_) { begin_frame_source_->RemoveObserver(this); + added_frame_observer_ = false; + } begin_frame_source_ = source; - if (begin_frame_source_ && needs_begin_frames) - begin_frame_source_->AddObserver(this); + UpdateNeedsBeginFramesInternal(); } bool RenderWidgetHostViewAura::IsAutoResizeEnabled() const {
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 190d5c9..c465b46e 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -162,6 +162,7 @@ void EndFrameSubscription() override; bool HasAcceleratedSurface(const gfx::Size& desired_size) override; gfx::Rect GetBoundsInRootWindow() override; + void OnSetNeedsFlushInput() override; void WheelEventAck(const blink::WebMouseWheelEvent& event, InputEventAckState ack_result) override; void GestureEventAck(const blink::WebGestureEvent& event, @@ -532,6 +533,9 @@ // Forwards a mouse event to this view's parent window delegate. void ForwardMouseEventToParent(ui::MouseEvent* event); + // Adds/Removes frame observer based on state. + void UpdateNeedsBeginFramesInternal(); + // Returns the RenderViewHostDelegateView instance for this view. Returns // NULL on failure. RenderViewHostDelegateView* GetRenderViewHostDelegateView(); @@ -592,8 +596,16 @@ // The begin frame source being observed. Null if none. cc::BeginFrameSource* begin_frame_source_; cc::BeginFrameArgs last_begin_frame_args_; + + // Whether a request for begin frames has been issued. bool needs_begin_frames_; + // Whether a request to flush input has been issued. + bool needs_flush_input_; + + // Whether we have added ourselves as a frame observer or not. + bool added_frame_observer_; + // Used to record the last position of the mouse. // While the mouse is locked, they store the last known position just as mouse // lock was entered.
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h index 9a2f0da1..75ad48f 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -320,6 +320,7 @@ bool LockMouse() override; void UnlockMouse() override; + void OnSetNeedsFlushInput() override; void GestureEventAck(const blink::WebGestureEvent& event, InputEventAckState ack_result) override; @@ -500,6 +501,9 @@ // Get the focused view that should be used for retrieving the text selection. RenderWidgetHostViewBase* GetFocusedViewForTextSelection(); + // Adds/Removes frame observer based on state. + void UpdateNeedsBeginFramesInternal(); + // The associated view. This is weak and is inserted into the view hierarchy // to own this RenderWidgetHostViewMac object. Set to nil at the start of the // destructor. @@ -545,6 +549,12 @@ gfx::Range composition_range_; std::vector<gfx::Rect> composition_bounds_; + // Whether a request for begin frames has been issued. + bool needs_begin_frames_; + + // Whether a request to flush input has been issued. + bool needs_flush_input_; + // Factory used to safely scope delayed calls to ShutdownHost(). base::WeakPtrFactory<RenderWidgetHostViewMac> weak_factory_;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index b5444dc9..fa22008 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -415,6 +415,9 @@ void RenderWidgetHostViewMac::BrowserCompositorMacSendBeginFrame( const cc::BeginFrameArgs& args) { + needs_flush_input_ = false; + render_widget_host_->FlushInput(); + UpdateNeedsBeginFramesInternal(); render_widget_host_->Send( new ViewMsg_BeginFrame(render_widget_host_->GetRoutingID(), args)); } @@ -451,6 +454,8 @@ allow_pause_for_resize_or_repaint_(true), is_guest_view_hack_(is_guest_view_hack), fullscreen_parent_host_view_(nullptr), + needs_begin_frames_(false), + needs_flush_input_(false), weak_factory_(this) { // |cocoa_view_| owns us and we will be deleted when |cocoa_view_| // goes away. Since we autorelease it, our caller must put @@ -1188,7 +1193,18 @@ } void RenderWidgetHostViewMac::SetNeedsBeginFrames(bool needs_begin_frames) { - browser_compositor_->SetNeedsBeginFrames(needs_begin_frames); + needs_begin_frames_ = needs_begin_frames; + UpdateNeedsBeginFramesInternal(); +} + +void RenderWidgetHostViewMac::OnSetNeedsFlushInput() { + needs_flush_input_ = true; + UpdateNeedsBeginFramesInternal(); +} + +void RenderWidgetHostViewMac::UpdateNeedsBeginFramesInternal() { + browser_compositor_->SetNeedsBeginFrames(needs_begin_frames_ || + needs_flush_input_); } void RenderWidgetHostViewMac::KillSelf() {
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc index 26a2c45..3b534c2 100644 --- a/content/child/child_thread_impl.cc +++ b/content/child/child_thread_impl.cc
@@ -896,11 +896,8 @@ int32_t routing_id = static_cast<int32_t>(reinterpret_cast<uintptr_t>( associated_interface_provider_bindings_.dispatch_context())); Listener* route = router_.GetRoute(routing_id); - base::debug::Alias(&name); - base::debug::Alias(&request); - base::debug::Alias(&routing_id); - base::debug::Alias(&route); - route->OnAssociatedInterfaceRequest(name, request.PassHandle()); + if (route) + route->OnAssociatedInterfaceRequest(name, request.PassHandle()); } bool ChildThreadImpl::IsInBrowserProcess() const {
diff --git a/content/public/browser/resource_dispatcher_host_delegate.cc b/content/public/browser/resource_dispatcher_host_delegate.cc index 229e49f..0928d79 100644 --- a/content/public/browser/resource_dispatcher_host_delegate.cc +++ b/content/public/browser/resource_dispatcher_host_delegate.cc
@@ -32,8 +32,8 @@ ResourceContext* resource_context, bool is_content_initiated, bool must_download, - ScopedVector<ResourceThrottle>* throttles) { -} + bool is_new_request, + ScopedVector<ResourceThrottle>* throttles) {} ResourceDispatcherHostLoginDelegate* ResourceDispatcherHostDelegate::CreateLoginDelegate(
diff --git a/content/public/browser/resource_dispatcher_host_delegate.h b/content/public/browser/resource_dispatcher_host_delegate.h index db515bf0..dfaeacd6 100644 --- a/content/public/browser/resource_dispatcher_host_delegate.h +++ b/content/public/browser/resource_dispatcher_host_delegate.h
@@ -55,10 +55,14 @@ // Allows an embedder to add additional resource handlers for a download. // |must_download| is set if the request must be handled as a download. + // |is_new_request| is true if this is a call for a new, unstarted request + // which also means that RequestBeginning has not been and will not be + // called for this request. virtual void DownloadStarting(net::URLRequest* request, ResourceContext* resource_context, bool is_content_initiated, bool must_download, + bool is_new_request, ScopedVector<ResourceThrottle>* throttles); // Creates a ResourceDispatcherHostLoginDelegate that asks the user for a
diff --git a/content/test/data/navigation_controller/beforeunload_dialog.html b/content/test/data/navigation_controller/beforeunload_dialog.html new file mode 100644 index 0000000..8e3c825e --- /dev/null +++ b/content/test/data/navigation_controller/beforeunload_dialog.html
@@ -0,0 +1,13 @@ +<html> +<head> +<script> +window.addEventListener("beforeunload", function (event) { + setTimeout(function() { + window.alert("hi"); + }, 0); +}); +</script> +</head> +<body> +</body> +</html>
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py index dbf43ef..e5498dc 100644 --- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py +++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -511,7 +511,7 @@ 'tex-2d-rgb-rgb-unsigned_short_5_6_5.html', ['linux'], bug=627525) - # Multi-vendor failures. + # Linux Multi-vendor failures. self.Fail('deqp/data/gles3/shaders/functions.html', ['linux', 'amd', 'intel'], bug=483282) @@ -525,12 +525,6 @@ self.Fail('deqp/data/gles3/shaders/linkage.html', ['linux', 'amd', 'intel'], bug=483282) - # Linux with ANGLE only - self.Fail('conformance2/textures/misc/tex-unpack-params.html', - ['linux', 'opengl'], bug=483282) - self.Fail('conformance2/reading/read-pixels-pack-parameters.html', - ['linux', 'opengl'], bug=483282) - # Linux without ANGLE only self.Flaky('conformance2/buffers/getBufferSubData.html', ['linux', 'no_angle'], bug=650123)
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_video_encoder_svc.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_video_encoder_svc.cc index cf81ae5..8c57b5c 100644 --- a/extensions/renderer/api/display_source/wifi_display/wifi_display_video_encoder_svc.cc +++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_video_encoder_svc.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/task_runner_util.h" +#include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "extensions/renderer/api/display_source/wifi_display/wifi_display_video_encoder.h" #include "third_party/openh264/src/codec/api/svc/codec_api.h"
diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc b/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc index 86855a8..8e84b7d 100644 --- a/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc +++ b/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
@@ -35,8 +35,7 @@ : GpuMemoryBufferImpl(id, size, format, callback), pixmap_(std::move(pixmap)), planes_(planes), - fd_(std::move(fd)), - data_(nullptr) {} + fd_(std::move(fd)) {} GpuMemoryBufferImplOzoneNativePixmap::~GpuMemoryBufferImplOzoneNativePixmap() {} @@ -101,33 +100,24 @@ bool GpuMemoryBufferImplOzoneNativePixmap::Map() { DCHECK(!mapped_); - DCHECK(!data_); - data_ = pixmap_->Map(); - if (!data_) - return false; - mapped_ = true; + mapped_ = pixmap_->Map(); return mapped_; } void* GpuMemoryBufferImplOzoneNativePixmap::memory(size_t plane) { DCHECK(mapped_); - DCHECK_LT(plane, gfx::NumberOfPlanesForBufferFormat(format_)); - return data_; + return pixmap_->GetMemoryAddress(plane); } void GpuMemoryBufferImplOzoneNativePixmap::Unmap() { DCHECK(mapped_); - DCHECK(data_); pixmap_->Unmap(); mapped_ = false; - data_ = nullptr; } int GpuMemoryBufferImplOzoneNativePixmap::stride(size_t plane) const { DCHECK_LT(plane, gfx::NumberOfPlanesForBufferFormat(format_)); - int stride; - pixmap_->GetStride(&stride); - return stride; + return pixmap_->GetStride(plane); } gfx::GpuMemoryBufferHandle GpuMemoryBufferImplOzoneNativePixmap::GetHandle()
diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.h b/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.h index 450d102..50ececf 100644 --- a/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.h +++ b/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.h
@@ -60,7 +60,6 @@ std::unique_ptr<ui::ClientNativePixmap> pixmap_; std::vector<gfx::NativePixmapPlane> planes_; base::ScopedFD fd_; - void* data_; DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferImplOzoneNativePixmap); };
diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg index 8aaba70..675bcd00 100644 --- a/infra/config/recipes.cfg +++ b/infra/config/recipes.cfg
@@ -5,13 +5,13 @@ project_id: "build" url: "https://chromium.googlesource.com/chromium/tools/build.git" branch: "master" - revision: "567475a8f817b58253b6e305bf5eb5c980f04bc6" + revision: "046b3dd73b69f40caec94eddedfbb0221d15cb74" } deps { project_id: "depot_tools" url: "https://chromium.googlesource.com/chromium/tools/depot_tools.git" branch: "master" - revision: "3e1cde7a87d6cb600d6bf3b5860b72da8c121c59" + revision: "3574740929abd37c45db1d2f8a2c3799bdfe77c5" } deps { project_id: "recipe_engine"
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn index 42c306f..7583843 100644 --- a/ios/web/BUILD.gn +++ b/ios/web/BUILD.gn
@@ -112,6 +112,16 @@ "webui/url_data_manager_ios_backend.h", "webui/url_data_manager_ios_backend.mm", "webui/url_data_source_ios.mm", + "webui/url_data_source_ios_impl.cc", + "webui/url_data_source_ios_impl.h", + "webui/url_fetcher_block_adapter.h", + "webui/url_fetcher_block_adapter.mm", + "webui/web_ui_ios_controller_factory_registry.cc", + "webui/web_ui_ios_controller_factory_registry.h", + "webui/web_ui_ios_data_source_impl.h", + "webui/web_ui_ios_data_source_impl.mm", + "webui/web_ui_ios_impl.h", + "webui/web_ui_ios_impl.mm", ] libs = [ "WebKit.framework" ] @@ -282,16 +292,6 @@ "web_state/web_view_internal_creation_util.mm", "web_state/wk_web_view_security_util.h", "web_state/wk_web_view_security_util.mm", - "webui/url_data_source_ios_impl.cc", - "webui/url_data_source_ios_impl.h", - "webui/url_fetcher_block_adapter.h", - "webui/url_fetcher_block_adapter.mm", - "webui/web_ui_ios_controller_factory_registry.cc", - "webui/web_ui_ios_controller_factory_registry.h", - "webui/web_ui_ios_data_source_impl.h", - "webui/web_ui_ios_data_source_impl.mm", - "webui/web_ui_ios_impl.h", - "webui/web_ui_ios_impl.mm", ] libs = [ "WebKit.framework" ]
diff --git a/ios/web/webui/url_fetcher_block_adapter.mm b/ios/web/webui/url_fetcher_block_adapter.mm index deb6178a..df7e1fa 100644 --- a/ios/web/webui/url_fetcher_block_adapter.mm +++ b/ios/web/webui/url_fetcher_block_adapter.mm
@@ -8,6 +8,10 @@ #include "net/url_request/url_fetcher.h" #include "net/url_request/url_request_context_getter.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace web { URLFetcherBlockAdapter::URLFetcherBlockAdapter(
diff --git a/ios/web/webui/web_ui_ios_data_source_impl.mm b/ios/web/webui/web_ui_ios_data_source_impl.mm index 1f44034..efb9e793 100644 --- a/ios/web/webui/web_ui_ios_data_source_impl.mm +++ b/ios/web/webui/web_ui_ios_data_source_impl.mm
@@ -13,6 +13,10 @@ #include "ui/base/webui/jstemplate_builder.h" #include "ui/base/webui/web_ui_util.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace web { // static
diff --git a/ios/web/webui/web_ui_ios_impl.mm b/ios/web/webui/web_ui_ios_impl.mm index 619b2e6..9123ed3 100644 --- a/ios/web/webui/web_ui_ios_impl.mm +++ b/ios/web/webui/web_ui_ios_impl.mm
@@ -15,6 +15,10 @@ #include "ios/web/public/webui/web_ui_ios_message_handler.h" #include "ios/web/web_state/web_state_impl.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + using web::WebUIIOSController; namespace web {
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn index a6340f5..d5ebd5c6 100644 --- a/media/capture/BUILD.gn +++ b/media/capture/BUILD.gn
@@ -47,12 +47,22 @@ "video/mac/video_capture_device_mac.h", "video/mac/video_capture_device_mac.mm", "video/scoped_result_callback.h", + "video/video_capture_buffer_handle.h", + "video/video_capture_buffer_pool.h", + "video/video_capture_buffer_pool_impl.cc", + "video/video_capture_buffer_pool_impl.h", + "video/video_capture_buffer_tracker.h", + "video/video_capture_buffer_tracker_factory.h", "video/video_capture_device.cc", "video/video_capture_device.h", + "video/video_capture_device_client.cc", + "video/video_capture_device_client.h", "video/video_capture_device_descriptor.cc", "video/video_capture_device_descriptor.h", "video/video_capture_device_factory.cc", "video/video_capture_device_factory.h", + "video/video_capture_jpeg_decoder.h", + "video/video_frame_receiver.h", "video/win/capability_list_win.cc", "video/win/capability_list_win.h", "video/win/filter_base_win.cc", @@ -79,6 +89,7 @@ "//media", "//media/mojo/interfaces:image_capture", "//skia", + "//third_party/libyuv", "//ui/display", "//ui/gfx", ] @@ -138,7 +149,6 @@ "video/blob_utils.cc", "video/blob_utils.h", ] - deps += [ "//third_party/libyuv" ] } }
diff --git a/media/capture/video/DEPS b/media/capture/video/DEPS index 896f509..6ed765f7 100644 --- a/media/capture/video/DEPS +++ b/media/capture/video/DEPS
@@ -1,3 +1,4 @@ include_rules = [ "+mojo/public/cpp/bindings", -] \ No newline at end of file + "+third_party/libyuv", +]
diff --git a/content/browser/renderer_host/media/video_capture_buffer_handle.h b/media/capture/video/video_capture_buffer_handle.h similarity index 68% rename from content/browser/renderer_host/media/video_capture_buffer_handle.h rename to media/capture/video/video_capture_buffer_handle.h index 1e17004..d9c4cc62 100644 --- a/content/browser/renderer_host/media/video_capture_buffer_handle.h +++ b/media/capture/video/video_capture_buffer_handle.h
@@ -2,17 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_HANDLE_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_HANDLE_H_ +#ifndef MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_BUFFER_HANDLE_H_ +#define MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_BUFFER_HANDLE_H_ #include "base/files/file.h" +#include "media/capture/capture_export.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/gpu_memory_buffer.h" -namespace content { +namespace media { // Abstraction of a pool's buffer data buffer and size for clients. -class VideoCaptureBufferHandle { +class CAPTURE_EXPORT VideoCaptureBufferHandle { public: virtual ~VideoCaptureBufferHandle() {} virtual gfx::Size dimensions() const = 0; @@ -24,6 +25,6 @@ #endif }; -} // namespace content +} // namespace media -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_HANDLE_H_ +#endif // MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_BUFFER_HANDLE_H_
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool.h b/media/capture/video/video_capture_buffer_pool.h similarity index 62% rename from content/browser/renderer_host/media/video_capture_buffer_pool.h rename to media/capture/video/video_capture_buffer_pool.h index 4dc59ac..0f0a4920 100644 --- a/content/browser/renderer_host/media/video_capture_buffer_pool.h +++ b/media/capture/video/video_capture_buffer_pool.h
@@ -1,33 +1,41 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_POOL_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_POOL_H_ +#ifndef MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_POOL_H_ +#define MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_POOL_H_ -#include <stddef.h> - -#include <map> - -#include "base/files/file.h" -#include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/memory/shared_memory.h" -#include "base/process/process.h" -#include "base/synchronization/lock.h" -#include "build/build_config.h" -#include "content/common/content_export.h" #include "media/base/video_capture_types.h" -#include "media/base/video_frame.h" +#include "media/capture/capture_export.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/gpu_memory_buffer.h" -namespace content { +namespace media { class VideoCaptureBufferHandle; -class VideoCaptureBufferTracker; -class CONTENT_EXPORT VideoCaptureBufferPool +// A thread-safe class that does the bookkeeping and lifetime management for a +// pool of pixel buffers cycled between an in-process producer (e.g. a +// VideoCaptureDevice) and a set of out-of-process consumers. The pool is +// intended to be orchestrated by a VideoCaptureDevice::Client, but is designed +// to outlive the controller if necessary. The pixel buffers may be backed by a +// SharedMemory, but this is not compulsory. +// +// Producers get a buffer by calling ReserveForProducer(), and may pass on their +// ownership to the consumer by calling HoldForConsumers(), or drop the buffer +// (without further processing) by calling RelinquishProducerReservation(). +// Consumers signal that they are done with the buffer by calling +// RelinquishConsumerHold(). +// +// Buffers are allocated on demand, but there will never be more than |count| +// buffers in existence at any time. Buffers are identified by an int value +// called |buffer_id|. -1 (kInvalidId) is never a valid ID, and is returned by +// some methods to indicate failure. The active set of buffer ids may change +// over the lifetime of the buffer pool, as existing buffers are freed and +// reallocated at larger size. When reallocation occurs, new buffer IDs will +// circulate. +class CAPTURE_EXPORT VideoCaptureBufferPool : public base::RefCountedThreadSafe<VideoCaptureBufferPool> { public: static constexpr int kInvalidId = -1; @@ -102,84 +110,6 @@ friend class base::RefCountedThreadSafe<VideoCaptureBufferPool>; }; -// A thread-safe class that does the bookkeeping and lifetime management for a -// pool of pixel buffers cycled between an in-process producer (e.g. a -// VideoCaptureDevice) and a set of out-of-process consumers. The pool is -// intended to be orchestrated by a VideoCaptureDevice::Client, but is designed -// to outlive the controller if necessary. The pixel buffers may be backed by a -// SharedMemory, but this is not compulsory. -// -// Producers get a buffer by calling ReserveForProducer(), and may pass on their -// ownership to the consumer by calling HoldForConsumers(), or drop the buffer -// (without further processing) by calling RelinquishProducerReservation(). -// Consumers signal that they are done with the buffer by calling -// RelinquishConsumerHold(). -// -// Buffers are allocated on demand, but there will never be more than |count| -// buffers in existence at any time. Buffers are identified by an int value -// called |buffer_id|. -1 (kInvalidId) is never a valid ID, and is returned by -// some methods to indicate failure. The active set of buffer ids may change -// over the lifetime of the buffer pool, as existing buffers are freed and -// reallocated at larger size. When reallocation occurs, new buffer IDs will -// circulate. -class CONTENT_EXPORT VideoCaptureBufferPoolImpl - : public VideoCaptureBufferPool { - public: - explicit VideoCaptureBufferPoolImpl(int count); +} // namespace media - // Implementation of VideoCaptureBufferPool interface: - bool ShareToProcess(int buffer_id, - base::ProcessHandle process_handle, - base::SharedMemoryHandle* new_handle) override; - bool ShareToProcess2(int buffer_id, - int plane, - base::ProcessHandle process_handle, - gfx::GpuMemoryBufferHandle* new_handle) override; - std::unique_ptr<VideoCaptureBufferHandle> GetBufferHandle( - int buffer_id) override; - int ReserveForProducer(const gfx::Size& dimensions, - media::VideoPixelFormat format, - media::VideoPixelStorage storage, - int* buffer_id_to_drop) override; - void RelinquishProducerReservation(int buffer_id) override; - int ResurrectLastForProducer(const gfx::Size& dimensions, - media::VideoPixelFormat format, - media::VideoPixelStorage storage) override; - double GetBufferPoolUtilization() const override; - void HoldForConsumers(int buffer_id, int num_clients) override; - void RelinquishConsumerHold(int buffer_id, int num_clients) override; - - private: - friend class base::RefCountedThreadSafe<VideoCaptureBufferPoolImpl>; - ~VideoCaptureBufferPoolImpl() override; - - int ReserveForProducerInternal(const gfx::Size& dimensions, - media::VideoPixelFormat format, - media::VideoPixelStorage storage, - int* tracker_id_to_drop); - - VideoCaptureBufferTracker* GetTracker(int buffer_id); - - // The max number of buffers that the pool is allowed to have at any moment. - const int count_; - - // Protects everything below it. - mutable base::Lock lock_; - - // The ID of the next buffer. - int next_buffer_id_; - - // The ID of the buffer last relinquished by the producer (a candidate for - // resurrection). - int last_relinquished_buffer_id_; - - // The buffers, indexed by the first parameter, a buffer id. - using TrackerMap = std::map<int, VideoCaptureBufferTracker*>; - TrackerMap trackers_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureBufferPoolImpl); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_POOL_H_ +#endif // MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_POOL_H_
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool.cc b/media/capture/video/video_capture_buffer_pool_impl.cc similarity index 93% rename from content/browser/renderer_host/media/video_capture_buffer_pool.cc rename to media/capture/video/video_capture_buffer_pool_impl.cc index bc335a8c..cce7ef0 100644 --- a/content/browser/renderer_host/media/video_capture_buffer_pool.cc +++ b/media/capture/video/video_capture_buffer_pool_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/renderer_host/media/video_capture_buffer_pool.h" +#include "media/capture/video/video_capture_buffer_pool_impl.h" #include <memory> @@ -10,17 +10,19 @@ #include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "build/build_config.h" -#include "content/browser/renderer_host/media/video_capture_buffer_handle.h" -#include "content/browser/renderer_host/media/video_capture_buffer_tracker.h" -#include "content/browser/renderer_host/media/video_capture_buffer_tracker_factory.h" +#include "media/capture/video/video_capture_buffer_handle.h" +#include "media/capture/video/video_capture_buffer_tracker.h" #include "ui/gfx/buffer_format_util.h" -namespace content { +namespace media { -VideoCaptureBufferPoolImpl::VideoCaptureBufferPoolImpl(int count) +VideoCaptureBufferPoolImpl::VideoCaptureBufferPoolImpl( + std::unique_ptr<VideoCaptureBufferTrackerFactory> buffer_tracker_factory, + int count) : count_(count), next_buffer_id_(0), - last_relinquished_buffer_id_(kInvalidId) { + last_relinquished_buffer_id_(kInvalidId), + buffer_tracker_factory_(std::move(buffer_tracker_factory)) { DCHECK_GT(count, 0); } @@ -236,7 +238,7 @@ const int buffer_id = next_buffer_id_++; std::unique_ptr<VideoCaptureBufferTracker> tracker = - VideoCaptureBufferTrackerFactory::CreateTracker(storage_type); + buffer_tracker_factory_->CreateTracker(storage_type); // TODO(emircan): We pass the lock here to solve GMB allocation issue, see // crbug.com/545238. if (!tracker->Init(dimensions, pixel_format, storage_type, &lock_)) { @@ -256,4 +258,4 @@ return (it == trackers_.end()) ? NULL : it->second; } -} // namespace content +} // namespace media
diff --git a/media/capture/video/video_capture_buffer_pool_impl.h b/media/capture/video/video_capture_buffer_pool_impl.h new file mode 100644 index 0000000..aa94367 --- /dev/null +++ b/media/capture/video/video_capture_buffer_pool_impl.h
@@ -0,0 +1,95 @@ +// Copyright (c) 2013 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 MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_POOL_IMPL_H_ +#define MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_POOL_IMPL_H_ + +#include <stddef.h> + +#include <map> + +#include "base/files/file.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/shared_memory.h" +#include "base/process/process.h" +#include "base/synchronization/lock.h" +#include "build/build_config.h" +#include "media/base/video_capture_types.h" +#include "media/base/video_frame.h" +#include "media/capture/capture_export.h" +#include "media/capture/video/video_capture_buffer_handle.h" +#include "media/capture/video/video_capture_buffer_pool.h" +#include "media/capture/video/video_capture_buffer_tracker_factory.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/gpu_memory_buffer.h" + +namespace media { + +class CAPTURE_EXPORT VideoCaptureBufferPoolImpl + : public VideoCaptureBufferPool { + public: + explicit VideoCaptureBufferPoolImpl( + std::unique_ptr<VideoCaptureBufferTrackerFactory> buffer_tracker_factory, + int count); + + // Implementation of VideoCaptureBufferPool interface: + bool ShareToProcess(int buffer_id, + base::ProcessHandle process_handle, + base::SharedMemoryHandle* new_handle) override; + bool ShareToProcess2(int buffer_id, + int plane, + base::ProcessHandle process_handle, + gfx::GpuMemoryBufferHandle* new_handle) override; + std::unique_ptr<VideoCaptureBufferHandle> GetBufferHandle( + int buffer_id) override; + int ReserveForProducer(const gfx::Size& dimensions, + media::VideoPixelFormat format, + media::VideoPixelStorage storage, + int* buffer_id_to_drop) override; + void RelinquishProducerReservation(int buffer_id) override; + int ResurrectLastForProducer(const gfx::Size& dimensions, + media::VideoPixelFormat format, + media::VideoPixelStorage storage) override; + double GetBufferPoolUtilization() const override; + void HoldForConsumers(int buffer_id, int num_clients) override; + void RelinquishConsumerHold(int buffer_id, int num_clients) override; + + private: + friend class base::RefCountedThreadSafe<VideoCaptureBufferPoolImpl>; + ~VideoCaptureBufferPoolImpl() override; + + int ReserveForProducerInternal(const gfx::Size& dimensions, + media::VideoPixelFormat format, + media::VideoPixelStorage storage, + int* tracker_id_to_drop); + + VideoCaptureBufferTracker* GetTracker(int buffer_id); + + // The max number of buffers that the pool is allowed to have at any moment. + const int count_; + + // Protects everything below it. + mutable base::Lock lock_; + + // The ID of the next buffer. + int next_buffer_id_; + + // The ID of the buffer last relinquished by the producer (a candidate for + // resurrection). + int last_relinquished_buffer_id_; + + // The buffers, indexed by the first parameter, a buffer id. + using TrackerMap = std::map<int, VideoCaptureBufferTracker*>; + TrackerMap trackers_; + + const std::unique_ptr<VideoCaptureBufferTrackerFactory> + buffer_tracker_factory_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureBufferPoolImpl); +}; + +} // namespace media + +#endif // MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_POOL_IMPL_H_
diff --git a/content/browser/renderer_host/media/video_capture_buffer_tracker.h b/media/capture/video/video_capture_buffer_tracker.h similarity index 88% rename from content/browser/renderer_host/media/video_capture_buffer_tracker.h rename to media/capture/video/video_capture_buffer_tracker.h index 182d42f..778459209 100644 --- a/content/browser/renderer_host/media/video_capture_buffer_tracker.h +++ b/media/capture/video/video_capture_buffer_tracker.h
@@ -2,22 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_TRACKER_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_TRACKER_H_ +#ifndef MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_TRACKER_H_ +#define MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_TRACKER_H_ #include <memory> #include "base/synchronization/lock.h" -#include "content/browser/renderer_host/media/video_capture_buffer_handle.h" #include "media/base/video_capture_types.h" +#include "media/capture/video/video_capture_buffer_handle.h" -namespace content { +namespace media { // Keeps track of the state of a given mappable resource. Each // VideoCaptureBufferTracker carries indication of pixel format and storage // type. This is a base class for implementations using different kinds of // storage. -class VideoCaptureBufferTracker { +class CAPTURE_EXPORT VideoCaptureBufferTracker { public: VideoCaptureBufferTracker() : max_pixel_count_(0), @@ -75,4 +75,4 @@ } // namespace content -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_BUFFER_TRACKER_H_ +#endif // MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_TRACKER_H_
diff --git a/media/capture/video/video_capture_buffer_tracker_factory.h b/media/capture/video/video_capture_buffer_tracker_factory.h new file mode 100644 index 0000000..2828a5b --- /dev/null +++ b/media/capture/video/video_capture_buffer_tracker_factory.h
@@ -0,0 +1,26 @@ +// Copyright (c) 2013 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 MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_TRACKER_FACTORY_H_ +#define MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_TRACKER_FACTORY_H_ + +#include <memory> + +#include "media/base/video_capture_types.h" +#include "media/capture/capture_export.h" + +namespace media { + +class VideoCaptureBufferTracker; + +class CAPTURE_EXPORT VideoCaptureBufferTrackerFactory { + public: + virtual ~VideoCaptureBufferTrackerFactory() {} + virtual std::unique_ptr<VideoCaptureBufferTracker> CreateTracker( + VideoPixelStorage storage_type) = 0; +}; + +} // namespace media + +#endif // MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_BUFFER_TRACKER_FACTORY_H_
diff --git a/content/browser/renderer_host/media/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc similarity index 88% rename from content/browser/renderer_host/media/video_capture_device_client.cc rename to media/capture/video/video_capture_device_client.cc index a70f5992..368af81 100644 --- a/content/browser/renderer_host/media/video_capture_device_client.cc +++ b/media/capture/video/video_capture_device_client.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/renderer_host/media/video_capture_device_client.h" +#include "media/capture/video/video_capture_device_client.h" #include <algorithm> #include <utility> @@ -14,21 +14,21 @@ #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" -#include "content/browser/renderer_host/media/video_capture_buffer_handle.h" -#include "content/browser/renderer_host/media/video_capture_buffer_pool.h" -#include "content/browser/renderer_host/media/video_capture_controller.h" -#include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h" #include "media/base/bind_to_current_loop.h" #include "media/base/media_switches.h" #include "media/base/video_capture_types.h" #include "media/base/video_frame.h" +#include "media/capture/video/video_capture_buffer_handle.h" +#include "media/capture/video/video_capture_buffer_pool.h" +#include "media/capture/video/video_capture_jpeg_decoder.h" +#include "media/capture/video/video_frame_receiver.h" #include "third_party/libyuv/include/libyuv.h" using media::VideoCaptureFormat; using media::VideoFrame; using media::VideoFrameMetadata; -namespace content { +namespace media { // Class combining a Client::Buffer interface implementation and a pool buffer // implementation to guarantee proper cleanup on destruction on our side. @@ -118,8 +118,8 @@ if (rotation == 90 || rotation == 270) std::swap(destination_width, destination_height); - DCHECK_EQ(0, rotation % 90) - << " Rotation must be a multiple of 90, now: " << rotation; + DCHECK_EQ(0, rotation % 90) << " Rotation must be a multiple of 90, now: " + << rotation; libyuv::RotationMode rotation_mode = libyuv::kRotate0; if (rotation == 90) rotation_mode = libyuv::kRotate90; @@ -180,10 +180,10 @@ origin_colorspace = libyuv::FOURCC_UYVY; break; case media::PIXEL_FORMAT_RGB24: - // Linux RGB24 defines red at lowest byte address, - // see http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html. - // Windows RGB24 defines blue at lowest byte, - // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd407253 +// Linux RGB24 defines red at lowest byte address, +// see http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html. +// Windows RGB24 defines blue at lowest byte, +// see https://msdn.microsoft.com/en-us/library/windows/desktop/dd407253 #if defined(OS_LINUX) origin_colorspace = libyuv::FOURCC_RAW; #elif defined(OS_WIN) @@ -220,13 +220,13 @@ DCHECK_GE(static_cast<size_t>(length), frame_format.ImageAllocationSize()); if (external_jpeg_decoder_) { - const VideoCaptureGpuJpegDecoder::STATUS status = + const VideoCaptureJpegDecoder::STATUS status = external_jpeg_decoder_->GetStatus(); - if (status == VideoCaptureGpuJpegDecoder::FAILED) { + if (status == VideoCaptureJpegDecoder::FAILED) { external_jpeg_decoder_.reset(); - } else if (status == VideoCaptureGpuJpegDecoder::INIT_PASSED && - frame_format.pixel_format == media::PIXEL_FORMAT_MJPEG && - rotation == 0 && !flip) { + } else if (status == VideoCaptureJpegDecoder::INIT_PASSED && + frame_format.pixel_format == media::PIXEL_FORMAT_MJPEG && + rotation == 0 && !flip) { external_jpeg_decoder_->DecodeCapturedData(data, length, frame_format, reference_time, timestamp, std::move(buffer)); @@ -234,30 +234,21 @@ } } - if (libyuv::ConvertToI420(data, - length, - y_plane_data, - yplane_stride, - u_plane_data, - uv_plane_stride, - v_plane_data, - uv_plane_stride, - crop_x, - crop_y, + if (libyuv::ConvertToI420(data, length, y_plane_data, yplane_stride, + u_plane_data, uv_plane_stride, v_plane_data, + uv_plane_stride, crop_x, crop_y, frame_format.frame_size.width(), (flip ? -1 : 1) * frame_format.frame_size.height(), - new_unrotated_width, - new_unrotated_height, - rotation_mode, - origin_colorspace) != 0) { + new_unrotated_width, new_unrotated_height, + rotation_mode, origin_colorspace) != 0) { DLOG(WARNING) << "Failed to convert buffer's pixel format to I420 from " << media::VideoPixelFormatToString(frame_format.pixel_format); return; } - const VideoCaptureFormat output_format = VideoCaptureFormat( - dimensions, frame_format.frame_rate, - media::PIXEL_FORMAT_I420, output_pixel_storage); + const VideoCaptureFormat output_format = + VideoCaptureFormat(dimensions, frame_format.frame_rate, + media::PIXEL_FORMAT_I420, output_pixel_storage); OnIncomingCapturedBuffer(std::move(buffer), output_format, reference_time, timestamp); } @@ -358,8 +349,7 @@ receiver_->OnError(); } -void VideoCaptureDeviceClient::OnLog( - const std::string& message) { +void VideoCaptureDeviceClient::OnLog(const std::string& message) { receiver_->OnLog(message); } @@ -412,4 +402,4 @@ return std::unique_ptr<Buffer>(); } -} // namespace content +} // namespace media
diff --git a/content/browser/renderer_host/media/video_capture_device_client.h b/media/capture/video/video_capture_device_client.h similarity index 92% rename from content/browser/renderer_host/media/video_capture_device_client.h rename to media/capture/video/video_capture_device_client.h index ac63f8b..821c58d 100644 --- a/content/browser/renderer_host/media/video_capture_device_client.h +++ b/media/capture/video/video_capture_device_client.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_DEVICE_CLIENT_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_DEVICE_CLIENT_H_ +#ifndef MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_DEVICE_CLIENT_H_ +#define MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_DEVICE_CLIENT_H_ #include <stddef.h> #include <stdint.h> @@ -13,10 +13,10 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "content/common/content_export.h" +#include "media/capture/capture_export.h" #include "media/capture/video/video_capture_device.h" -namespace content { +namespace media { class VideoCaptureBufferPool; class VideoFrameReceiver; class VideoCaptureJpegDecoder; @@ -39,7 +39,7 @@ // GpuMemoryBuffers into Texture backed VideoFrames. This class creates and // manages the necessary entities to interact with the GPU process, notably an // offscreen Context to avoid janking the UI thread. -class CONTENT_EXPORT VideoCaptureDeviceClient +class CAPTURE_EXPORT VideoCaptureDeviceClient : public media::VideoCaptureDevice::Client, public base::SupportsWeakPtr<VideoCaptureDeviceClient> { public: @@ -125,7 +125,6 @@ DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceClient); }; +} // namespace media -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_DEVICE_CLIENT_H_ +#endif // MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_DEVICE_CLIENT_H_
diff --git a/media/capture/video/video_capture_jpeg_decoder.h b/media/capture/video/video_capture_jpeg_decoder.h new file mode 100644 index 0000000..e9d91dc --- /dev/null +++ b/media/capture/video/video_capture_jpeg_decoder.h
@@ -0,0 +1,45 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/callback.h" +#include "media/capture/capture_export.h" +#include "media/capture/video/video_capture_device.h" + +namespace media { + +class CAPTURE_EXPORT VideoCaptureJpegDecoder { + public: + // Enumeration of decoder status. The enumeration is published for clients to + // decide the behavior according to STATUS. + enum STATUS { + INIT_PENDING, // Default value while waiting initialization finished. + INIT_PASSED, // Initialization succeed. + FAILED, // JPEG decode is not supported, initialization failed, or + // decode error. + }; + + using DecodeDoneCB = base::Callback<void( + std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>, + const scoped_refptr<media::VideoFrame>&)>; + + virtual ~VideoCaptureJpegDecoder() {} + + // Creates and intializes decoder asynchronously. + virtual void Initialize() = 0; + + // Returns initialization status. + virtual STATUS GetStatus() const = 0; + + // Decodes a JPEG picture. + virtual void DecodeCapturedData( + const uint8_t* data, + size_t in_buffer_size, + const media::VideoCaptureFormat& frame_format, + base::TimeTicks reference_time, + base::TimeDelta timestamp, + std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> + out_buffer) = 0; +}; + +} // namespace media
diff --git a/media/capture/video/video_frame_receiver.h b/media/capture/video/video_frame_receiver.h new file mode 100644 index 0000000..df2dabc --- /dev/null +++ b/media/capture/video/video_frame_receiver.h
@@ -0,0 +1,24 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/capture/capture_export.h" +#include "media/capture/video/video_capture_device.h" + +namespace media { + +// Callback interface for VideoCaptureDeviceClient to communicate with its +// clients. +class CAPTURE_EXPORT VideoFrameReceiver { + public: + virtual ~VideoFrameReceiver(){}; + + virtual void OnIncomingCapturedVideoFrame( + std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer, + const scoped_refptr<media::VideoFrame>& frame) = 0; + virtual void OnError() = 0; + virtual void OnLog(const std::string& message) = 0; + virtual void OnBufferDestroyed(int buffer_id_to_drop) = 0; +}; + +} // namespace media
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc index 70bc4399..aa905e71 100644 --- a/media/gpu/video_decode_accelerator_unittest.cc +++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -372,7 +372,7 @@ handle.native_pixmap_handle.fds.emplace_back( base::FileDescriptor(duped_fd, true)); handle.native_pixmap_handle.planes.emplace_back( - pixmap_->GetDmaBufPitch(0), pixmap_->GetDmaBufOffset(0), + pixmap_->GetDmaBufPitch(0), pixmap_->GetDmaBufOffset(0), 0, pixmap_->GetDmaBufModifier(0)); #endif return handle;
diff --git a/net/quic/core/congestion_control/general_loss_algorithm.cc b/net/quic/core/congestion_control/general_loss_algorithm.cc index 6415f2c..6c82329a 100644 --- a/net/quic/core/congestion_control/general_loss_algorithm.cc +++ b/net/quic/core/congestion_control/general_loss_algorithm.cc
@@ -87,7 +87,10 @@ // there are retransmittable packets in flight. // This also implements a timer-protected variant of FACK. if ((!it->retransmittable_frames.empty() && - unacked_packets.largest_sent_packet() == largest_newly_acked) || + (FLAGS_quic_largest_sent_retransmittable + ? unacked_packets.largest_sent_retransmittable_packet() + : unacked_packets.largest_sent_packet()) <= + largest_newly_acked) || (loss_type_ == kTime || loss_type_ == kAdaptiveTime)) { QuicTime when_lost = it->sent_time + loss_delay; if (time < when_lost) {
diff --git a/net/quic/core/congestion_control/general_loss_algorithm_test.cc b/net/quic/core/congestion_control/general_loss_algorithm_test.cc index fef1ccb..107674d 100644 --- a/net/quic/core/congestion_control/general_loss_algorithm_test.cc +++ b/net/quic/core/congestion_control/general_loss_algorithm_test.cc
@@ -44,6 +44,14 @@ true); } + void SendAckPacket(QuicPacketNumber packet_number) { + SerializedPacket packet(kDefaultPathId, packet_number, + PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength, + 0, true, false); + unacked_packets_.AddSentPacket(&packet, 0, NOT_RETRANSMISSION, clock_.Now(), + false); + } + void VerifyLosses(QuicPacketNumber largest_newly_acked, QuicPacketNumber* losses_expected, size_t num_losses) { @@ -184,6 +192,28 @@ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); } +TEST_F(GeneralLossAlgorithmTest, EarlyRetransmitWithLargerUnackablePackets) { + FLAGS_quic_largest_sent_retransmittable = true; + // Transmit 2 data packets and one ack. + SendDataPacket(1); + SendDataPacket(2); + SendAckPacket(3); + clock_.AdvanceTime(rtt_stats_.smoothed_rtt()); + + // Early retransmit when the final packet gets acked and the first is nacked. + unacked_packets_.IncreaseLargestObserved(2); + unacked_packets_.RemoveFromInFlight(2); + VerifyLosses(2, nullptr, 0); + EXPECT_EQ(clock_.Now() + 0.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout()); + + // The packet should be lost once the loss timeout is reached. + clock_.AdvanceTime(0.25 * rtt_stats_.latest_rtt()); + QuicPacketNumber lost[] = {1}; + VerifyLosses(2, lost, arraysize(lost)); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + TEST_F(GeneralLossAlgorithmTest, AlwaysLosePacketSent1RTTEarlier) { // Transmit 1 packet and then wait an rtt plus 1ms. SendDataPacket(1);
diff --git a/net/quic/core/congestion_control/simulation/quic_endpoint.cc b/net/quic/core/congestion_control/simulation/quic_endpoint.cc index 9e3dde1..bbf35328 100644 --- a/net/quic/core/congestion_control/simulation/quic_endpoint.cc +++ b/net/quic/core/congestion_control/simulation/quic_endpoint.cc
@@ -165,7 +165,7 @@ } QuicEndpoint::Writer::Writer(QuicEndpoint* endpoint) - : QuicDefaultPacketWriter(0), endpoint_(endpoint) {} + : endpoint_(endpoint), is_blocked_(false) {} QuicEndpoint::Writer::~Writer() {} @@ -184,7 +184,7 @@ // Instead of losing a packet, become write-blocked when the egress queue is // full. if (endpoint_->nic_tx_queue_.packets_queued() > kTxQueueSize) { - set_write_blocked(true); + is_blocked_ = true; return WriteResult(WRITE_STATUS_BLOCKED, 0); }
diff --git a/net/quic/core/congestion_control/simulation/quic_endpoint.h b/net/quic/core/congestion_control/simulation/quic_endpoint.h index d12e11ea..0ce4b3d 100644 --- a/net/quic/core/congestion_control/simulation/quic_endpoint.h +++ b/net/quic/core/congestion_control/simulation/quic_endpoint.h
@@ -92,7 +92,7 @@ private: // A Writer object that writes into the |nic_tx_queue_|. - class Writer : public QuicDefaultPacketWriter { + class Writer : public QuicPacketWriter { public: explicit Writer(QuicEndpoint* endpoint); ~Writer() override; @@ -102,9 +102,18 @@ const IPAddress& self_address, const IPEndPoint& peer_address, PerPacketOptions* options) override; + bool IsWriteBlockedDataBuffered() const override { return false; } + bool IsWriteBlocked() const override { return is_blocked_; } + void SetWritable() override { is_blocked_ = false; } + QuicByteCount GetMaxPacketSize( + const IPEndPoint& /*peer_address*/) const override { + return kMaxPacketSize; + } private: QuicEndpoint* endpoint_; + + bool is_blocked_; }; // Write stream data until |bytes_to_transfer_| is zero or the connection is
diff --git a/net/quic/core/crypto/crypto_server_test.cc b/net/quic/core/crypto/crypto_server_test.cc index c148e9b..43fad918 100644 --- a/net/quic/core/crypto/crypto_server_test.cc +++ b/net/quic/core/crypto/crypto_server_test.cc
@@ -480,7 +480,6 @@ } TEST_P(CryptoServerTest, RejectNotTooLarge) { - FLAGS_quic_use_chlo_packet_size = true; // When the CHLO packet is large enough, ensure that a full REJ is sent. chlo_packet_size_ *= 2;
diff --git a/net/quic/core/crypto/quic_crypto_server_config.cc b/net/quic/core/crypto/quic_crypto_server_config.cc index 66860e7..0853835 100644 --- a/net/quic/core/crypto/quic_crypto_server_config.cc +++ b/net/quic/core/crypto/quic_crypto_server_config.cc
@@ -231,7 +231,8 @@ source_address_token_lifetime_secs_(86400), server_nonce_strike_register_max_entries_(1 << 10), server_nonce_strike_register_window_secs_(120), - enable_serving_sct_(false) { + enable_serving_sct_(false), + rejection_observer_(nullptr) { DCHECK(proof_source_.get()); source_address_token_boxer_.SetKeys( {DeriveSourceAddressTokenKey(source_address_token_secret)}); @@ -645,6 +646,10 @@ use_stateless_rejects, server_designated_connection_id, rand, compressed_certs_cache, params, *crypto_proof, total_framing_overhead, chlo_packet_size, out); + if (FLAGS_quic_export_rej_for_all_rejects && + rejection_observer_ != nullptr) { + rejection_observer_->OnRejectionBuilt(info.reject_reasons, out); + } return QUIC_NO_ERROR; } @@ -1518,14 +1523,9 @@ // max_unverified_size is the number of bytes that the certificate chain, // signature, and (optionally) signed certificate timestamp can consume before // we will demand a valid source-address token. - const size_t old_max_unverified_size = - client_hello.size() * chlo_multiplier_ - kREJOverheadBytes; - const size_t new_max_unverified_size = + const size_t max_unverified_size = chlo_multiplier_ * (chlo_packet_size - total_framing_overhead) - kREJOverheadBytes; - const size_t max_unverified_size = FLAGS_quic_use_chlo_packet_size - ? new_max_unverified_size - : old_max_unverified_size; static_assert(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes, "overhead calculation may underflow"); bool should_return_sct = @@ -1544,13 +1544,11 @@ } } } else { - if (FLAGS_quic_use_chlo_packet_size) { - DLOG(WARNING) << "Sending inchoate REJ for hostname: " << info.sni - << " signature: " << crypto_proof.signature.size() - << " cert: " << compressed.size() << " sct:" << sct_size - << " total: " << total_size - << " max: " << max_unverified_size; - } + DLOG(WARNING) << "Sending inchoate REJ for hostname: " << info.sni + << " signature: " << crypto_proof.signature.size() + << " cert: " << compressed.size() << " sct:" << sct_size + << " total: " << total_size + << " max: " << max_unverified_size; } }
diff --git a/net/quic/core/crypto/quic_crypto_server_config.h b/net/quic/core/crypto/quic_crypto_server_config.h index 48449ad..12316b1b 100644 --- a/net/quic/core/crypto/quic_crypto_server_config.h +++ b/net/quic/core/crypto/quic_crypto_server_config.h
@@ -128,6 +128,20 @@ DISALLOW_COPY_AND_ASSIGN(BuildServerConfigUpdateMessageResultCallback); }; +// Object that is interested in built rejections (which include REJ, SREJ and +// cheap SREJ). +class RejectionObserver { + public: + RejectionObserver() = default; + virtual ~RejectionObserver() {} + // Called after a rejection is built. + virtual void OnRejectionBuilt(const std::vector<uint32_t>& reasons, + CryptoHandshakeMessage* out) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(RejectionObserver); +}; + // QuicCryptoServerConfig contains the crypto configuration of a QUIC server. // Unlike a client, a QUIC server can have multiple configurations active in // order to support clients resuming with a previous configuration. @@ -427,6 +441,12 @@ // Returns the number of configs this object owns. int NumberOfConfigs() const; + // Callers retain the ownership of |rejection_observer| which must outlive the + // config. + void set_rejection_observer(RejectionObserver* rejection_observer) { + rejection_observer_ = rejection_observer; + } + private: friend class test::QuicCryptoServerConfigPeer; friend struct QuicCryptoProof; @@ -784,6 +804,9 @@ // Enable serving SCT or not. bool enable_serving_sct_; + // Does not own this observer. + RejectionObserver* rejection_observer_; + DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerConfig); };
diff --git a/net/quic/core/quic_connection.cc b/net/quic/core/quic_connection.cc index 6985d7d..c93735b 100644 --- a/net/quic/core/quic_connection.cc +++ b/net/quic/core/quic_connection.cc
@@ -1298,6 +1298,15 @@ stats_.bytes_received += packet.length(); ++stats_.packets_received; + // Ensure the time coming from the packet reader is within a minute of now. + if (FLAGS_quic_allow_large_send_deltas && + std::abs((packet.receipt_time() - clock_->ApproximateNow()).ToSeconds()) > + 60) { + QUIC_BUG << "Packet receipt time:" + << packet.receipt_time().ToDebuggingValue() + << " too far from current time:" + << clock_->ApproximateNow().ToDebuggingValue(); + } time_of_last_received_packet_ = packet.receipt_time(); DVLOG(1) << ENDPOINT << "time of last received packet: " << time_of_last_received_packet_.ToDebuggingValue();
diff --git a/net/quic/core/quic_flags_list.h b/net/quic/core/quic_flags_list.h index ffda3d9..a2859c6 100644 --- a/net/quic/core/quic_flags_list.h +++ b/net/quic/core/quic_flags_list.h
@@ -72,10 +72,6 @@ // versions less than 33. QUIC_FLAG(bool, FLAGS_quic_require_handshake_confirmation_pre33, false) -// If true, use the CHLO packet size, not message size when determining how -// large a REJ can be. -QUIC_FLAG(bool, FLAGS_quic_use_chlo_packet_size, true) - // If true, defer creation of new connection till its CHLO arrives. QUIC_FLAG(bool, FLAGS_quic_buffer_packet_till_chlo, true) @@ -102,7 +98,7 @@ // If true, only open limited number of quic sessions per epoll event. Leave the // rest to next event. This flag can be turned on only if // --quic_buffer_packet_till_chlo is true. -QUIC_FLAG(bool, FLAGS_quic_limit_num_new_sessions_per_epoll_loop, false) +QUIC_FLAG(bool, FLAGS_quic_limit_num_new_sessions_per_epoll_loop, true) // If true, lazy allocate and early release memeory used in // QuicStreamSequencerBuffer to buffer incoming data. @@ -136,3 +132,17 @@ // As the Linux kernel does, limit QUIC's Cubic congestion control to // only increase the CWND 1 packet for every two packets acked. QUIC_FLAG(bool, FLAGS_quic_limit_cubic_cwnd_increase, true) + +// If true, export reject reasons for all rejects, i.e., rejects, +// stateless rejects and cheap stateless rejects. +QUIC_FLAG(bool, FLAGS_quic_export_rej_for_all_rejects, true) + +// Allow large send deltas to be used as RTT samples. +QUIC_FLAG(bool, FLAGS_quic_allow_large_send_deltas, true) + +// Engage early retransmit anytime the largest acked is greater than +// or equal to the largest retransmittable packet. +QUIC_FLAG(bool, FLAGS_quic_largest_sent_retransmittable, true) + +// If true, close connection when sequencer buffer enter into unexpected state. +QUIC_FLAG(bool, FLAGS_quic_stream_sequencer_buffer_debug, true)
diff --git a/net/quic/core/quic_headers_stream.h b/net/quic/core/quic_headers_stream.h index 89a8c3f..603dd45 100644 --- a/net/quic/core/quic_headers_stream.h +++ b/net/quic/core/quic_headers_stream.h
@@ -167,9 +167,6 @@ SpdyFramer spdy_framer_; std::unique_ptr<SpdyFramerVisitor> spdy_framer_visitor_; - // Either empty, or contains the complete list of headers. - QuicHeaderList header_list_; - DISALLOW_COPY_AND_ASSIGN(QuicHeadersStream); };
diff --git a/net/quic/core/quic_multipath_sent_packet_manager.cc b/net/quic/core/quic_multipath_sent_packet_manager.cc index 0800a6a..5d46ec10 100644 --- a/net/quic/core/quic_multipath_sent_packet_manager.cc +++ b/net/quic/core/quic_multipath_sent_packet_manager.cc
@@ -354,12 +354,6 @@ path_manager->OnConnectionMigration(path_id, type); } -bool QuicMultipathSentPacketManager::IsHandshakeConfirmed() const { - QuicSentPacketManagerInterface* path_manager = - MaybeGetSentPacketManagerForActivePath(kDefaultPathId); - return path_manager != nullptr && path_manager->IsHandshakeConfirmed(); -} - void QuicMultipathSentPacketManager::SetDebugDelegate( DebugDelegate* debug_delegate) { for (PathSentPacketManagerInfo path_manager_info : path_managers_info_) {
diff --git a/net/quic/core/quic_multipath_sent_packet_manager.h b/net/quic/core/quic_multipath_sent_packet_manager.h index c805280..75b35dd 100644 --- a/net/quic/core/quic_multipath_sent_packet_manager.h +++ b/net/quic/core/quic_multipath_sent_packet_manager.h
@@ -143,8 +143,6 @@ void OnConnectionMigration(QuicPathId path_id, PeerAddressChangeType type) override; - bool IsHandshakeConfirmed() const override; - // Sets debug delegate for all active paths. void SetDebugDelegate(DebugDelegate* debug_delegate) override;
diff --git a/net/quic/core/quic_multipath_sent_packet_manager_test.cc b/net/quic/core/quic_multipath_sent_packet_manager_test.cc index 62177aa..bcff44d 100644 --- a/net/quic/core/quic_multipath_sent_packet_manager_test.cc +++ b/net/quic/core/quic_multipath_sent_packet_manager_test.cc
@@ -275,11 +275,6 @@ multipath_manager_.OnConnectionMigration(kTestPathId3, PORT_CHANGE), ""); } -TEST_F(QuicMultipathSentPacketManagerTest, IsHandshakeConfirmed) { - EXPECT_CALL(*manager_0_, IsHandshakeConfirmed()).WillOnce(Return(true)); - EXPECT_TRUE(multipath_manager_.IsHandshakeConfirmed()); -} - TEST_F(QuicMultipathSentPacketManagerTest, SetDebugDelegate) { EXPECT_CALL(*manager_0_, SetDebugDelegate(nullptr)); EXPECT_CALL(*manager_1_, SetDebugDelegate(nullptr));
diff --git a/net/quic/core/quic_protocol.h b/net/quic/core/quic_protocol.h index 72a5b69c..8906879 100644 --- a/net/quic/core/quic_protocol.h +++ b/net/quic/core/quic_protocol.h
@@ -724,8 +724,12 @@ // maintains too many gaps. QUIC_TOO_MANY_FRAME_GAPS = 93, + // Sequencer buffer get into weird state where continuing read/write will lead + // to crash. + QUIC_STREAM_SEQUENCER_INVALID_STATE = 95, + // No error. Used as bound while iterating. - QUIC_LAST_ERROR = 95, + QUIC_LAST_ERROR = 96, }; typedef std::array<char, 32> DiversificationNonce;
diff --git a/net/quic/core/quic_sent_packet_manager.cc b/net/quic/core/quic_sent_packet_manager.cc index 5a97b1e..7738e34 100644 --- a/net/quic/core/quic_sent_packet_manager.cc +++ b/net/quic/core/quic_sent_packet_manager.cc
@@ -632,9 +632,6 @@ if (!it->in_flight || it->retransmittable_frames.empty()) { continue; } - if (!handshake_confirmed_) { - DCHECK(!it->has_crypto_handshake); - } MarkForRetransmission(packet_number, TLP_RETRANSMISSION); return true; } @@ -743,7 +740,8 @@ QuicTime::Delta send_delta = ack_receive_time - transmission_info.sent_time; const int kMaxSendDeltaSeconds = 30; - if (send_delta.ToSeconds() > kMaxSendDeltaSeconds) { + if (!FLAGS_quic_allow_large_send_deltas && + send_delta.ToSeconds() > kMaxSendDeltaSeconds) { // send_delta can be very high if local clock is changed mid-connection. LOG(WARNING) << "Excessive send delta: " << send_delta.ToSeconds() << ", setting to: " << kMaxSendDeltaSeconds @@ -946,9 +944,6 @@ send_algorithm_->OnConnectionMigration(); } -bool QuicSentPacketManager::IsHandshakeConfirmed() const { - return handshake_confirmed_; -} void QuicSentPacketManager::SetDebugDelegate(DebugDelegate* debug_delegate) { debug_delegate_ = debug_delegate;
diff --git a/net/quic/core/quic_sent_packet_manager.h b/net/quic/core/quic_sent_packet_manager.h index 17e2203..b442951 100644 --- a/net/quic/core/quic_sent_packet_manager.h +++ b/net/quic/core/quic_sent_packet_manager.h
@@ -189,8 +189,6 @@ // Called when peer address changes and the connection migrates. void OnConnectionMigration(QuicPathId, PeerAddressChangeType type) override; - bool IsHandshakeConfirmed() const override; - void SetDebugDelegate(DebugDelegate* debug_delegate) override; QuicPacketNumber GetLargestObserved(QuicPathId) const override;
diff --git a/net/quic/core/quic_sent_packet_manager_interface.h b/net/quic/core/quic_sent_packet_manager_interface.h index c366ed6..7d80e6f 100644 --- a/net/quic/core/quic_sent_packet_manager_interface.h +++ b/net/quic/core/quic_sent_packet_manager_interface.h
@@ -165,8 +165,6 @@ virtual void OnConnectionMigration(QuicPathId path_id, PeerAddressChangeType type) = 0; - virtual bool IsHandshakeConfirmed() const = 0; - virtual void SetDebugDelegate(DebugDelegate* debug_delegate) = 0; virtual QuicPacketNumber GetLargestObserved(QuicPathId path_id) const = 0;
diff --git a/net/quic/core/quic_stream_sequencer.cc b/net/quic/core/quic_stream_sequencer.cc index 4a6ea83..45f1e88c 100644 --- a/net/quic/core/quic_stream_sequencer.cc +++ b/net/quic/core/quic_stream_sequencer.cc
@@ -9,8 +9,10 @@ #include <string> #include <utility> +#include "base/format_macros.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" #include "net/quic/core/quic_bug_tracker.h" #include "net/quic/core/quic_clock.h" #include "net/quic/core/quic_flags.h" @@ -21,6 +23,7 @@ using base::IntToString; using base::StringPiece; +using base::StringPrintf; using std::min; using std::numeric_limits; using std::string; @@ -135,7 +138,17 @@ int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) { DCHECK(!blocked_); - size_t bytes_read = buffered_frames_.Readv(iov, iov_len); + string error_details; + size_t bytes_read; + QuicErrorCode read_error = + buffered_frames_.Readv(iov, iov_len, &bytes_read, &error_details); + if (FLAGS_quic_stream_sequencer_buffer_debug && read_error != QUIC_NO_ERROR) { + string details = StringPrintf("Stream %" PRIu32 ": %s", stream_->id(), + error_details.c_str()); + stream_->CloseConnectionWithDetails(read_error, details); + return static_cast<int>(bytes_read); + } + stream_->AddBytesConsumed(bytes_read); return static_cast<int>(bytes_read); }
diff --git a/net/quic/core/quic_stream_sequencer_buffer.cc b/net/quic/core/quic_stream_sequencer_buffer.cc index 0c55fe75..fcb617fb6 100644 --- a/net/quic/core/quic_stream_sequencer_buffer.cc +++ b/net/quic/core/quic_stream_sequencer_buffer.cc
@@ -4,11 +4,14 @@ #include "net/quic/core/quic_stream_sequencer_buffer.h" +#include "base/format_macros.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" #include "net/quic/core/quic_bug_tracker.h" #include "net/quic/core/quic_flags.h" +using base::StringPrintf; using std::min; using std::string; @@ -76,11 +79,15 @@ frame_arrival_time_map_.clear(); } -void QuicStreamSequencerBuffer::RetireBlock(size_t idx) { - DCHECK(blocks_[idx] != nullptr); +bool QuicStreamSequencerBuffer::RetireBlock(size_t idx) { + if (FLAGS_quic_stream_sequencer_buffer_debug && blocks_[idx] == nullptr) { + QUIC_BUG << "Try to retire block twice"; + return false; + } delete blocks_[idx]; blocks_[idx] = nullptr; DVLOG(1) << "Retired block with index: " << idx; + return true; } QuicErrorCode QuicStreamSequencerBuffer::OnStreamData( @@ -181,6 +188,20 @@ } } + if (FLAGS_quic_stream_sequencer_buffer_debug && + write_block_num >= blocks_count_) { + *error_details = StringPrintf( + "QuicStreamSequencerBuffer error: OnStreamData() exceed array bounds." + "write offset = %" PRIu64 " write_block_num = %" PRIuS + " blocks_count_ = %" PRIuS, + offset, write_block_num, blocks_count_); + return QUIC_STREAM_SEQUENCER_INVALID_STATE; + } + if (blocks_ == nullptr) { + *error_details = + "QuicStreamSequencerBuffer error: OnStreamData() blocks_ is null"; + return QUIC_STREAM_SEQUENCER_INVALID_STATE; + } if (blocks_[write_block_num] == nullptr) { // TODO(danzh): Investigate if using a freelist would improve performance. // Same as RetireBlock(). @@ -190,6 +211,23 @@ const size_t bytes_to_copy = min<size_t>(bytes_avail, source_remaining); char* dest = blocks_[write_block_num]->buffer + write_block_offset; DVLOG(1) << "Write at offset: " << offset << " length: " << bytes_to_copy; + + if (FLAGS_quic_stream_sequencer_buffer_debug && + (dest == nullptr || source == nullptr)) { + *error_details = StringPrintf( + "QuicStreamSequencerBuffer error: OnStreamData()" + " dest == nullptr: %s" + " source == nullptr: %s" + " Writing at offset %" PRIu64 + " Gaps: %s" + " Remaining frames: %s" + " total_bytes_read_ = %" PRIu64, + (dest == nullptr ? "true" : "false"), + (source == nullptr ? "true" : "false"), offset, + GapsDebugString().c_str(), ReceivedFramesDebugString().c_str(), + total_bytes_read_); + return QUIC_STREAM_SEQUENCER_INVALID_STATE; + } memcpy(dest, source, bytes_to_copy); source += bytes_to_copy; source_remaining -= bytes_to_copy; @@ -237,9 +275,11 @@ } } -size_t QuicStreamSequencerBuffer::Readv(const iovec* dest_iov, - size_t dest_count) { - size_t bytes_read = 0; +QuicErrorCode QuicStreamSequencerBuffer::Readv(const iovec* dest_iov, + size_t dest_count, + size_t* bytes_read, + string* error_details) { + *bytes_read = 0; for (size_t i = 0; i < dest_count && ReadableBytes() > 0; ++i) { char* dest = reinterpret_cast<char*>(dest_iov[i].iov_base); size_t dest_remaining = dest_iov[i].iov_len; @@ -251,28 +291,47 @@ min<size_t>(ReadableBytes(), block_capacity - start_offset_in_block); size_t bytes_to_copy = min<size_t>(bytes_available_in_block, dest_remaining); - DCHECK_GT(bytes_to_copy, 0u); - DCHECK_NE(static_cast<BufferBlock*>(nullptr), blocks_[block_idx]); + DCHECK_GT(bytes_to_copy, 0UL); + if (FLAGS_quic_stream_sequencer_buffer_debug && + (blocks_[block_idx] == nullptr || dest == nullptr)) { + *error_details = StringPrintf( + "QuicStreamSequencerBuffer error:" + " Readv() dest == nullptr: %s" + " blocks_[%" PRIuS "] == nullptr: %s", + (dest == nullptr ? "true" : "false"), block_idx, + (blocks_[block_idx] == nullptr ? "true" : "false")); + return QUIC_STREAM_SEQUENCER_INVALID_STATE; + } memcpy(dest, blocks_[block_idx]->buffer + start_offset_in_block, bytes_to_copy); dest += bytes_to_copy; dest_remaining -= bytes_to_copy; num_bytes_buffered_ -= bytes_to_copy; total_bytes_read_ += bytes_to_copy; - bytes_read += bytes_to_copy; + *bytes_read += bytes_to_copy; - // Retire the block if all the data is read out - // and no other data is stored in this block. + // Retire the block if all the data is read out and no other data is + // stored in this block. + // In case of failing to retire a block which is ready to retire, return + // immediately. if (bytes_to_copy == bytes_available_in_block) { - RetireBlockIfEmpty(block_idx); + bool retire_successfully = RetireBlockIfEmpty(block_idx); + if (FLAGS_quic_stream_sequencer_buffer_debug && !retire_successfully) { + *error_details = StringPrintf( + "QuicStreamSequencerBuffer error: fail to retire block %" PRIuS + " as the block is already released + total_bytes_read_ = %" PRIu64 + " Gaps: %s", + block_idx, total_bytes_read_, GapsDebugString().c_str()); + return QUIC_STREAM_SEQUENCER_INVALID_STATE; + } } } } - if (bytes_read > 0) { + if (*bytes_read > 0) { UpdateFrameArrivalMap(total_bytes_read_); } - return bytes_read; + return QUIC_NO_ERROR; } int QuicStreamSequencerBuffer::GetReadableRegions(struct iovec* iov, @@ -447,21 +506,20 @@ return GetBlockIndex(total_bytes_read_); } -void QuicStreamSequencerBuffer::RetireBlockIfEmpty(size_t block_index) { +bool QuicStreamSequencerBuffer::RetireBlockIfEmpty(size_t block_index) { DCHECK(ReadableBytes() == 0 || GetInBlockOffset(total_bytes_read_) == 0) << "RetireBlockIfEmpty() should only be called when advancing to next " "block" " or a gap has been reached."; // If the whole buffer becomes empty, the last piece of data has been read. if (Empty()) { - RetireBlock(block_index); - return; + return RetireBlock(block_index); } // Check where the logical end of this buffer is. // Not empty if the end of circular buffer has been wrapped to this block. if (GetBlockIndex(gaps_.back().begin_offset - 1) == block_index) { - return; + return true; } // Read index remains in this block, which means a gap has been reached. @@ -473,10 +531,10 @@ bool gap_ends_in_this_block = (GetBlockIndex(first_gap.end_offset) == block_index); if (gap_ends_in_this_block) { - return; + return true; } } - RetireBlock(block_index); + return RetireBlock(block_index); } bool QuicStreamSequencerBuffer::Empty() const { @@ -534,8 +592,16 @@ QuicStreamOffset current_frame_begin_offset = it.first; QuicStreamOffset current_frame_end_offset = it.second.length + current_frame_begin_offset; - current_frames_string += - RangeDebugString(current_frame_begin_offset, current_frame_end_offset); + if (FLAGS_quic_stream_sequencer_buffer_debug) { + current_frames_string = string(StringPrintf( + "%s[%" PRIu64 ", %" PRIu64 ") receiving time %" PRId64 " ", + current_frames_string.c_str(), current_frame_begin_offset, + current_frame_end_offset, it.second.timestamp.ToDebuggingValue())); + } else { + current_frames_string = string(StringPrintf( + "%s[%" PRIu64 ", %" PRIu64 ") ", current_frames_string.c_str(), + current_frame_begin_offset, current_frame_end_offset)); + } } return current_frames_string; }
diff --git a/net/quic/core/quic_stream_sequencer_buffer.h b/net/quic/core/quic_stream_sequencer_buffer.h index 135e7d1..a79d8b4 100644 --- a/net/quic/core/quic_stream_sequencer_buffer.h +++ b/net/quic/core/quic_stream_sequencer_buffer.h
@@ -125,7 +125,10 @@ // Reads from this buffer into given iovec array, up to number of iov_len // iovec objects and returns the number of bytes read. - size_t Readv(const struct iovec* dest_iov, size_t dest_count); + QuicErrorCode Readv(const struct iovec* dest_iov, + size_t dest_count, + size_t* bytes_read, + std::string* error_details); // Returns the readable region of valid data in iovec format. The readable // region is the buffer region where there is valid data not yet read by @@ -174,13 +177,15 @@ // Dispose the given buffer block. // After calling this method, blocks_[index] is set to nullptr // in order to indicate that no memory set is allocated for that block. - void RetireBlock(size_t index); + // Returns true on success, false otherwise. + bool RetireBlock(size_t index); // Should only be called after the indexed block is read till the end of the // block or a gap has been reached. - // If the block at |block_index| contains no buffered data, then the block is - // retired. - void RetireBlockIfEmpty(size_t block_index); + // If the block at |block_index| contains no buffered data, the block + // should be retired. + // Return false on success, or false otherwise. + bool RetireBlockIfEmpty(size_t block_index); // Called within OnStreamData() to update the gap OnStreamData() writes into // (remove, split or change begin/end offset).
diff --git a/net/quic/core/quic_stream_sequencer_buffer_test.cc b/net/quic/core/quic_stream_sequencer_buffer_test.cc index f4ae84c..944b41a 100644 --- a/net/quic/core/quic_stream_sequencer_buffer_test.cc +++ b/net/quic/core/quic_stream_sequencer_buffer_test.cc
@@ -66,7 +66,11 @@ size_t Read(char* dest_buffer, size_t size) { iovec dest; dest.iov_base = dest_buffer, dest.iov_len = size; - return buffer_->Readv(&dest, 1); + size_t bytes_read; + string error_details; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->Readv(&dest, 1, &bytes_read, &error_details)); + return bytes_read; } // If buffer is empty, the blocks_ array must be empty, which means all @@ -186,6 +190,7 @@ MockClock clock_; std::unique_ptr<QuicStreamSequencerBuffer> buffer_; std::unique_ptr<QuicStreamSequencerBufferPeer> helper_; + QuicFlagSaver flag_saver_; string error_details_; }; @@ -240,6 +245,22 @@ EXPECT_TRUE(helper_->IsBufferAllocated()); } +TEST_F(QuicStreamSequencerBufferTest, OnStreamDataInvalidSource) { + FLAGS_quic_stream_sequencer_buffer_debug = true; + // Pass in an invalid source, expects to return error. + StringPiece source; + source.set(nullptr, 1024); + size_t written; + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + QuicTime t = clock_.ApproximateNow(); + EXPECT_EQ(QUIC_STREAM_SEQUENCER_INVALID_STATE, + buffer_->OnStreamData(800, source, t, &written, &error_details_)); + EXPECT_EQ( + 0u, error_details_.find("QuicStreamSequencerBuffer error: OnStreamData()" + " dest == nullptr: false" + " source == nullptr: true")); +} + TEST_F(QuicStreamSequencerBufferTest, OnStreamDataWithOverlap) { string source(1024, 'a'); // Write something into [800, 1824) @@ -411,7 +432,9 @@ // Read into a iovec array with total capacity of 120 bytes. char dest[120]; iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40}, iovec{dest + 80, 40}}; - size_t read = buffer_->Readv(iovecs, 3); + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(iovecs, 3, &read, &error_details_)); + LOG(ERROR) << error_details_; EXPECT_EQ(100u, read); EXPECT_EQ(100u, buffer_->BytesConsumed()); EXPECT_EQ(source, string(dest, read)); @@ -421,6 +444,27 @@ EXPECT_TRUE(helper_->CheckBufferInvariants()); } +TEST_F(QuicStreamSequencerBufferTest, ReadvError) { + FLAGS_quic_stream_sequencer_buffer_debug = true; + string source = string(100, 'b'); + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + QuicTime t1 = clock_.ApproximateNow(); + // Write something into [0, 100). + size_t written; + buffer_->OnStreamData(0, source, t1, &written, &error_details_); + EXPECT_TRUE(helper_->GetBlock(0) != nullptr); + EXPECT_TRUE(buffer_->HasBytesToRead()); + // Read into a iovec array with total capacity of 120 bytes. + iovec iov{nullptr, 120}; + size_t read; + EXPECT_EQ(QUIC_STREAM_SEQUENCER_INVALID_STATE, + buffer_->Readv(&iov, 1, &read, &error_details_)); + EXPECT_EQ(0u, + error_details_.find( + "QuicStreamSequencerBuffer error: Readv() dest == nullptr: true" + " blocks_[0] == nullptr: false")); +} + TEST_F(QuicStreamSequencerBufferTest, ReadvAcrossBlocks) { string source(kBlockSizeBytes + 50, 'a'); // Write 1st block to full and extand 50 bytes to next block. @@ -433,7 +477,8 @@ while (helper_->ReadableBytes()) { std::fill(dest, dest + 512, 0); iovec iovecs[2]{iovec{dest, 256}, iovec{dest + 256, 256}}; - buffer_->Readv(iovecs, 2); + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(iovecs, 2, &read, &error_details_)); } // The last read only reads the rest 50 bytes in 2nd block. EXPECT_EQ(string(50, 'a'), string(dest, 50)); @@ -452,7 +497,8 @@ // Read first 512 bytes from buffer to make space at the beginning. char dest[512]{0}; const iovec iov{dest, 512}; - buffer_->Readv(&iov, 1); + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); // Clear() should make buffer empty while preserving BytesConsumed() buffer_->Clear(); EXPECT_TRUE(buffer_->Empty()); @@ -469,7 +515,8 @@ // Read first 512 bytes from buffer to make space at the beginning. char dest[512]{0}; const iovec iov{dest, 512}; - buffer_->Readv(&iov, 1); + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); EXPECT_EQ(source.size(), written); // Write more than half block size of bytes in the last block with 'b', which @@ -492,7 +539,8 @@ // Read first 512 bytes from buffer to make space at the beginning. char dest[512]{0}; const iovec iov{dest, 512}; - buffer_->Readv(&iov, 1); + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); // Try to write from [max_capacity_bytes_ - 0.5 * kBlockSizeBytes, // max_capacity_bytes_ + 512 + 1). But last bytes exceeds current capacity. @@ -514,7 +562,8 @@ buffer_->OnStreamData(0, source, t, &written, &error_details_); char dest[512]{0}; const iovec iov{dest, 512}; - buffer_->Readv(&iov, 1); + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); source = string(256, 'b'); clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); QuicTime t2 = clock_.ApproximateNow(); @@ -527,7 +576,8 @@ std::unique_ptr<char[]> dest1{new char[max_capacity_bytes_]}; dest1[0] = 0; const iovec iov1{dest1.get(), max_capacity_bytes_}; - EXPECT_EQ(max_capacity_bytes_ - 512 + 256, buffer_->Readv(&iov1, 1)); + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov1, 1, &read, &error_details_)); + EXPECT_EQ(max_capacity_bytes_ - 512 + 256, read); EXPECT_EQ(max_capacity_bytes_ + 256, buffer_->BytesConsumed()); EXPECT_TRUE(buffer_->Empty()); EXPECT_TRUE(helper_->CheckBufferInvariants()); @@ -537,7 +587,8 @@ TEST_F(QuicStreamSequencerBufferTest, ReadvEmpty) { char dest[512]{0}; iovec iov{dest, 512}; - size_t read = buffer_->Readv(&iov, 1); + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); EXPECT_EQ(0u, read); EXPECT_TRUE(helper_->CheckBufferInvariants()); } @@ -566,7 +617,8 @@ EXPECT_TRUE(buffer_->HasBytesToRead()); char dest[120]; iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40}, iovec{dest + 80, 40}}; - size_t read = buffer_->Readv(iovecs, 3); + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(iovecs, 3, &read, &error_details_)); EXPECT_EQ(100u, read); EXPECT_EQ(100u, buffer_->BytesConsumed()); EXPECT_TRUE(helper_->CheckBufferInvariants()); @@ -1013,7 +1065,10 @@ dest_iov[i].iov_len = rng_.RandUint64() % kMaxReadSize; num_to_read += dest_iov[i].iov_len; } - size_t actually_read = buffer_->Readv(dest_iov, kNumReads); + size_t actually_read; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->Readv(dest_iov, kNumReads, &actually_read, + &error_details_)); ASSERT_LE(actually_read, num_to_read); DVLOG(1) << " read from offset: " << total_bytes_read_ << " size: " << num_to_read
diff --git a/net/quic/core/quic_stream_sequencer_test.cc b/net/quic/core/quic_stream_sequencer_test.cc index 0960f03..b3b473f0 100644 --- a/net/quic/core/quic_stream_sequencer_test.cc +++ b/net/quic/core/quic_stream_sequencer_test.cc
@@ -662,6 +662,30 @@ EXPECT_EQ(0u, sequencer_->NumBytesBuffered()); } +TEST_F(QuicStreamSequencerTest, OnStreamFrameWithNullSource) { + FLAGS_quic_stream_sequencer_buffer_debug = true; + // Pass in a frame with data pointing to null address, expect to close + // connection with error. + StringPiece source(nullptr, 5u); + QuicStreamFrame frame(kClientDataStreamId1, false, 1, source); + EXPECT_CALL(stream_, CloseConnectionWithDetails( + QUIC_STREAM_SEQUENCER_INVALID_STATE, _)); + sequencer_->OnStreamFrame(frame); +} + +TEST_F(QuicStreamSequencerTest, ReadvError) { + FLAGS_quic_stream_sequencer_buffer_debug = true; + EXPECT_CALL(stream_, OnDataAvailable()); + string source(100, 'a'); + OnFrame(0u, source.data()); + EXPECT_EQ(source.length(), sequencer_->NumBytesBuffered()); + // Pass in a null iovec, expect to tear down connection. + EXPECT_CALL(stream_, CloseConnectionWithDetails( + QUIC_STREAM_SEQUENCER_INVALID_STATE, _)); + iovec iov{nullptr, 512}; + sequencer_->Readv(&iov, 1u); +} + } // namespace } // namespace test } // namespace net
diff --git a/net/quic/core/quic_unacked_packet_map.cc b/net/quic/core/quic_unacked_packet_map.cc index 0c70d14..adc4a653 100644 --- a/net/quic/core/quic_unacked_packet_map.cc +++ b/net/quic/core/quic_unacked_packet_map.cc
@@ -18,6 +18,7 @@ QuicUnackedPacketMap::QuicUnackedPacketMap() : largest_sent_packet_(0), + largest_sent_retransmittable_packet_(0), largest_observed_(0), least_unacked_(1), bytes_in_flight_(0), @@ -59,6 +60,7 @@ if (set_in_flight) { bytes_in_flight_ += bytes_sent; info.in_flight = true; + largest_sent_retransmittable_packet_ = packet_number; } unacked_packets_.push_back(info); // Swap the ack listeners and retransmittable frames to avoid allocations.
diff --git a/net/quic/core/quic_unacked_packet_map.h b/net/quic/core/quic_unacked_packet_map.h index 73969fd6..2357779 100644 --- a/net/quic/core/quic_unacked_packet_map.h +++ b/net/quic/core/quic_unacked_packet_map.h
@@ -80,6 +80,11 @@ // Returns the largest packet number that has been sent. QuicPacketNumber largest_sent_packet() const { return largest_sent_packet_; } + // Returns the largest retransmittable packet number that has been sent. + QuicPacketNumber largest_sent_retransmittable_packet() const { + return largest_sent_retransmittable_packet_; + } + // Returns the largest packet number that has been acked. QuicPacketNumber largest_observed() const { return largest_observed_; } @@ -169,6 +174,8 @@ const TransmissionInfo& info) const; QuicPacketNumber largest_sent_packet_; + // The largest sent packet we expect to receive an ack for. + QuicPacketNumber largest_sent_retransmittable_packet_; QuicPacketNumber largest_observed_; // Newly serialized retransmittable packets are added to this map, which
diff --git a/net/quic/core/quic_utils.cc b/net/quic/core/quic_utils.cc index 52c0def6..f944dd1 100644 --- a/net/quic/core/quic_utils.cc +++ b/net/quic/core/quic_utils.cc
@@ -303,6 +303,7 @@ RETURN_STRING_LITERAL(QUIC_MULTIPATH_PATH_DOES_NOT_EXIST); RETURN_STRING_LITERAL(QUIC_MULTIPATH_PATH_NOT_ACTIVE); RETURN_STRING_LITERAL(QUIC_TOO_MANY_FRAME_GAPS); + RETURN_STRING_LITERAL(QUIC_STREAM_SEQUENCER_INVALID_STATE); RETURN_STRING_LITERAL(QUIC_LAST_ERROR); // Intentionally have no default case, so we'll break the build // if we add errors and don't put them here.
diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc index a88a2ead..1d7343f5 100644 --- a/net/tools/quic/quic_dispatcher_test.cc +++ b/net/tools/quic/quic_dispatcher_test.cc
@@ -427,8 +427,8 @@ packet.nonce_proof = 132232; std::unique_ptr<QuicEncryptedPacket> encrypted( QuicFramer::BuildPublicResetPacket(packet)); - std::unique_ptr<QuicReceivedPacket> received( - ConstructReceivedPacket(*encrypted, helper_.GetClock()->Now())); + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + *encrypted, session1_->connection()->clock()->Now())); EXPECT_CALL(*session1_, OnConnectionClosed(QUIC_PUBLIC_RESET, _, ConnectionCloseSource::FROM_PEER)) .Times(1)
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn index 7457c326..f919ec3 100644 --- a/testing/libfuzzer/fuzzers/BUILD.gn +++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -419,3 +419,14 @@ ] libfuzzer_options = [ "max_len=1000" ] } + +fuzzer_test("openssl_bio_string_fuzzer") { + sources = [ + "openssl_bio_string_fuzzer.cc", + ] + deps = [ + "//crypto", + "//third_party/boringssl", + ] + libfuzzer_options = [ "max_len=512" ] +}
diff --git a/testing/libfuzzer/fuzzers/openssl_bio_string_fuzzer.cc b/testing/libfuzzer/fuzzers/openssl_bio_string_fuzzer.cc new file mode 100644 index 0000000..4eec5d8c --- /dev/null +++ b/testing/libfuzzer/fuzzers/openssl_bio_string_fuzzer.cc
@@ -0,0 +1,32 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdint.h> + +#include "crypto/openssl_bio_string.h" +#include "crypto/scoped_openssl_types.h" + + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size == 0) { return 0; } + + std::string buffer; + std::string input(reinterpret_cast<const char*>(data), size); + + std::size_t data_hash = std::hash<std::string>()(input); + uint8_t choice = data_hash % 3; + + crypto::ScopedBIO bio(crypto::BIO_new_string(&buffer)); + if (choice == 0) { + BIO_printf(bio.get(), "%s", input.c_str()); + } else if (choice == 1) { + BIO_write(bio.get(), input.c_str(), size); + } else { + BIO_puts(bio.get(), input.c_str()); + } + BIO_flush(bio.get()); + + return 0; +} +
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 825e47f9..19a1348 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -264,36 +264,6 @@ crbug.com/567837 virtual/scalefactor150/fast/hidpi/static/mousewheel-scroll-amount.html [ Skip ] crbug.com/567837 virtual/scalefactor150/fast/hidpi/static/gesture-scroll-amount.html [ Skip ] -crbug.com/646176 paint/invalidation/4776765.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/caret-subpixel.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/caret-with-composited-scroll.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/compositing/iframe-inside-squashed-layer.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/delete-into-nested-block.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/inline-outline-repaint.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/invalidate-caret-in-composited-scrolling-container.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/invalidate-caret-in-non-composited-scrolling-container.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/line-flow-with-floats-2.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/line-flow-with-floats-8.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/line-flow-with-floats-9.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/overflow-scroll-body-appear.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/repaint-during-scroll-with-zoom.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/scrolled-iframe-scrollbar-change.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/selection-after-delete.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/shift-relative-positioned-container-with-image-addition.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-1.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-2.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-1.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-2.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/text-match-document-change.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/textarea-caret.html [ NeedsRebaseline ] -crbug.com/646176 paint/invalidation/window-resize-vertical-writing-mode.html [ NeedsRebaseline ] - -crbug.com/640256 [ Linux Win ] fast/forms/color/color-suggestion-picker-appearance-zoom125.html [ NeedsRebaseline ] -crbug.com/640256 [ Linux Win ] fast/forms/color/color-suggestion-picker-appearance-zoom200.html [ NeedsRebaseline ] -crbug.com/640256 [ Linux Win ] fast/forms/color/color-suggestion-picker-appearance.html [ NeedsRebaseline ] -crbug.com/640256 [ Linux Win ] fast/forms/color/color-suggestion-picker-one-row-appearance.html [ NeedsRebaseline ] -crbug.com/640256 [ Linux Win ] fast/forms/color/color-suggestion-picker-two-row-appearance.html [ NeedsRebaseline ] - # TODO(ojan): These tests aren't flaky. See crbug.com/517144. # Release trybots run asserts, but the main waterfall ones don't. So, even # though this is a non-flaky assert failure, we need to mark it [ Pass Crash ]. @@ -370,33 +340,6 @@ crbug.com/525889 imported/wpt/html/webappapis/scripting/processing-model-2/runtime-error-in-setInterval.html [ Failure ] crbug.com/525889 imported/wpt/html/webappapis/scripting/processing-model-2/runtime-error-in-setTimeout.html [ Failure ] -crbug.com/648963 compositing/contents-opaque/overflow-hidden-child-layers.html [ NeedsRebaseline ] -crbug.com/648963 compositing/culling/scrolled-within-boxshadow.html [ NeedsRebaseline ] -crbug.com/648963 compositing/culling/translated-boxshadow.html [ NeedsRebaseline ] -crbug.com/648963 compositing/culling/unscrolled-within-boxshadow.html [ NeedsRebaseline ] -crbug.com/648963 compositing/geometry/foreground-layer.html [ NeedsRebaseline ] -crbug.com/648963 compositing/layer-creation/rotate3d-overlap.html [ NeedsRebaseline ] -crbug.com/648963 compositing/masks/masked-ancestor.html [ NeedsRebaseline ] -crbug.com/648963 paint/invalidation/box-shadow-add-repaint.html [ NeedsRebaseline ] -crbug.com/648963 paint/invalidation/box-shadow-change-repaint.html [ NeedsRebaseline ] -crbug.com/648963 paint/invalidation/transform-replaced-shadows.html [ NeedsRebaseline ] -crbug.com/648963 svg/css/text-gradient-shadow.svg [ NeedsRebaseline ] -crbug.com/648963 svg/css/text-shadow-multiple.xhtml [ NeedsRebaseline ] -crbug.com/648963 svg/zoom/page/zoom-background-images.html [ NeedsRebaseline ] -crbug.com/648963 compositing/masks/multiple-masks.html [ NeedsRebaseline ] -crbug.com/648963 compositing/masks/simple-composited-mask.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/scrolling-iframe.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/connect-compositing-iframe3.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/overlapped-iframe.html [ NeedsRebaseline ] -crbug.com/648963 fast/borders/border-radius-mask-video-shadow.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/enter-compositing-iframe.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/iframe-resize.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/composited-parent-iframe.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/invisible-nested-iframe-show.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/connect-compositing-iframe-delayed.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/connect-compositing-iframe2.html [ NeedsRebaseline ] -crbug.com/648963 compositing/iframes/connect-compositing-iframe.html [ NeedsRebaseline ] - crbug.com/636961 [ Linux Debug ] virtual/threaded/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-vertical-rl-anchors.html [ Pass Failure ] crbug.com/552532 [ Win10 ] fast/replaced/no-focus-ring-embed.html [ Pass Crash ] @@ -701,11 +644,6 @@ crbug.com/306222 fast/hidpi/image-srcset-relative-svg-canvas.html [ Skip ] crbug.com/306222 fast/hidpi/image-srcset-relative-svg-canvas-2x.html [ Skip ] -crbug.com/647922 svg/zoom/page/zoom-img-preserveAspectRatio-support-1.html [ NeedsRebaseline ] -crbug.com/647922 fast/borders/webkit-border-radius.html [ NeedsRebaseline ] -crbug.com/647922 fast/borders/borderRadiusDotted04.html [ NeedsRebaseline ] -crbug.com/647922 fast/borders/border-radius-different-width-001-double.html [ NeedsRebaseline ] - # The failure in the next line is due to virtual/gpu bots using osmesa # Under osmesa, skia's filter quality doesn't work correctly. This test is expected to pass # when we swith to swiftshader. @@ -1141,8 +1079,6 @@ crbug.com/587779 [ Linux Mac10.9 Mac10.10 Mac10.11 Retina ] fast/dynamic/window-resize-scrollbars-test.html [ Timeout Failure Pass ] crbug.com/588103 fast/xmlhttprequest/xmlhttprequest-responsetype-arraybuffer.html [ Pass Failure ] -crbug.com/649320 fast/forms/select/menulist-appearance-basic.html [ NeedsRebaseline ] -crbug.com/649320 http/tests/webfont/popup-menu-load-webfont-after-open.html [ NeedsRebaseline ] crbug.com/594672 fast/events/iframe-object-onload.html [ Failure Pass ] crbug.com/594672 fast/events/scale-and-scroll-iframe-body.html [ Failure Pass ] crbug.com/594672 fast/events/updateLayoutForHitTest.html [ Failure Pass ] @@ -1254,6 +1190,9 @@ crbug.com/399507 [ Mac Linux Win10 ] virtual/threaded/inspector/tracing/timeline-paint/layer-tree.html [ Skip ] +# Skip this test because it causes the next text to fail though the test itself passes. +crbug.com/648785 paint/invalidation/fixed-right-bottom-in-page-scale.html [ Skip ] + crbug.com/637245 [ Mac10.11 Retina ] fast/html/details-add-summary-5-and-click.html [ Pass Failure ] crbug.com/637245 [ Mac10.11 Retina ] fast/html/details-add-summary-8-and-click.html [ Pass Failure ] crbug.com/637245 [ Mac10.11 Retina ] fast/html/details-add-summary-1-and-click.html [ Pass Failure ] @@ -1296,10 +1235,6 @@ crbug.com/645640 inspector/extensions/extensions-eval.html [ NeedsManualRebaseline ] -crbug.com/649631 [ Win7 Win10 ] fast/borders/border-styles-split.html [ NeedsRebaseline ] -crbug.com/649631 [ Win7 Win10 ] fast/backgrounds/border-radius-split-background.html [ NeedsRebaseline ] -crbug.com/649631 [ Win7 Win10 ] fast/backgrounds/border-radius-split-background-image.html [ NeedsRebaseline ] - crbug.com/650655 virtual/enable_wasm/http/tests/wasm/wasm_serialization_tests.html [ NeedsManualRebaseline ] crbug.com/649046 fast/forms/calendar-picker/calendar-picker-appearance-zoom125.html [ NeedsRebaseline ]
diff --git a/third_party/WebKit/LayoutTests/accessibility/bounds-calc.html b/third_party/WebKit/LayoutTests/accessibility/bounds-calc.html index 2af241f..3f2a7813 100644 --- a/third_party/WebKit/LayoutTests/accessibility/bounds-calc.html +++ b/third_party/WebKit/LayoutTests/accessibility/bounds-calc.html
@@ -51,6 +51,7 @@ <svg id="svg" width="60" height="60"> <circle role="button" id="svg_circle" r="25" cx="30" cy="30" stroke="blue" stroke-width="1"/> </svg> + <p id="twolines">First line<br>Second line</p> </div> <script> @@ -71,22 +72,23 @@ assert_approx_equals(axObject.boundsHeight, bounds.height, epsilon, id + " height"); } -function assertStaticTextChildDOMRectSameAsAXRect(id) { +function assertStaticTextChildDOMRectSameAsAXRect(id, index) { var element = document.getElementById(id); var axObject = accessibilityController.accessibleElementById(id); var text = element.firstChild; - var axText = axObject.childAtIndex(0); - assert_equals(text.nodeType, Node.TEXT_NODE, id + " firstChild nodeType"); - assert_equals(axText.role, "AXRole: AXStaticText", id + " AX first child role"); + for (var i = 0; i < index; i++) + text = text.nextSibling; + var axText = axObject.childAtIndex(index); + assert_equals(text.nodeType, Node.TEXT_NODE, id + " child " + index + " nodeType"); + assert_equals(axText.role, "AXRole: AXStaticText", id + " AX child " + index + " role"); var range = document.createRange(); range.selectNode(text); var bounds = range.getBoundingClientRect(); - var axObject = accessibilityController.accessibleElementById(id); var epsilon = 1; - assert_approx_equals(axText.boundsX, bounds.left, epsilon, id + " left"); - assert_approx_equals(axText.boundsY, bounds.top, epsilon, id + " left"); - assert_approx_equals(axText.boundsWidth, bounds.width, epsilon, id + " left"); - assert_approx_equals(axText.boundsHeight, bounds.height, epsilon, id + " left"); + assert_approx_equals(axText.boundsX, bounds.left, epsilon, id + " child " + index + " left"); + assert_approx_equals(axText.boundsY, bounds.top, epsilon, id + " child " + index + " top"); + assert_approx_equals(axText.boundsWidth, bounds.width, epsilon, id + " child " + index + " width"); + assert_approx_equals(axText.boundsHeight, bounds.height, epsilon, id + " child " + index + " height"); } function assertOffsetContainerIs(id, containerId, opt_expectedResult) { @@ -111,26 +113,31 @@ assertDOMRectSameAsAXRect("radio"); assertDOMRectSameAsAXRect("button"); assertDOMRectSameAsAXRect("heading"); - assertStaticTextChildDOMRectSameAsAXRect("heading"); + assertStaticTextChildDOMRectSameAsAXRect("heading", 0); assertDOMRectSameAsAXRect("para"); - assertStaticTextChildDOMRectSameAsAXRect("para"); + assertStaticTextChildDOMRectSameAsAXRect("para", 0); assertDOMRectSameAsAXRect("span"); - assertStaticTextChildDOMRectSameAsAXRect("span"); + assertStaticTextChildDOMRectSameAsAXRect("span", 0); assertDOMRectSameAsAXRect("ul"); assertDOMRectSameAsAXRect("li1"); assertDOMRectSameAsAXRect("li2"); assertDOMRectSameAsAXRect("div"); - assertStaticTextChildDOMRectSameAsAXRect("div"); + assertStaticTextChildDOMRectSameAsAXRect("div", 0); assertDOMRectSameAsAXRect("border"); - assertStaticTextChildDOMRectSameAsAXRect("border"); + assertStaticTextChildDOMRectSameAsAXRect("border", 0); assertDOMRectSameAsAXRect("padding"); - assertStaticTextChildDOMRectSameAsAXRect("padding"); + assertStaticTextChildDOMRectSameAsAXRect("padding", 0); assertDOMRectSameAsAXRect("margin"); - assertStaticTextChildDOMRectSameAsAXRect("margin"); - assertDOMRectSameAsAXRect("border_padding_margin"); - assertStaticTextChildDOMRectSameAsAXRect("border_padding_margin"); + assertStaticTextChildDOMRectSameAsAXRect("margin", 0); + assertDOMRectSameAsAXRect("border_padding_margin", 0); + assertStaticTextChildDOMRectSameAsAXRect("border_padding_margin", 0); assertDOMRectSameAsAXRect("svg"); assertDOMRectSameAsAXRect("svg_circle"); + + // Test both static text elements in <p>First line<br>Second line</p>. + // We don't care about the bounding box of the <br> element in-between. + assertStaticTextChildDOMRectSameAsAXRect("twolines", 0); + assertStaticTextChildDOMRectSameAsAXRect("twolines", 2); }, "Test computed AX rect for some common objects"); </script>
diff --git a/third_party/WebKit/LayoutTests/accessibility/inline-text-bounds-for-range-br.html b/third_party/WebKit/LayoutTests/accessibility/inline-text-bounds-for-range-br.html new file mode 100644 index 0000000..843b49e6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/accessibility/inline-text-bounds-for-range-br.html
@@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> + +<p id="paragraph">Line 1<br>Line 2</p> + +<script> +test(function(t) { + // Due to rounding we won't get identical bounds as getBoundingClientRect(), + // so allow the test to pass if we're within 1 pixel. + var epsilon = 1; + + var axParagraph = accessibilityController.accessibleElementById("paragraph"); + var axStaticText1 = axParagraph.childAtIndex(0); + eval("var axTextBounds1 = " + axStaticText1.boundsForRange(0, 6)); + + var paragraph = document.getElementById("paragraph"); + var range = new Range(); + range.setStart(paragraph.firstChild, 0); + range.setEnd(paragraph.firstChild, 6); + var rangeBounds = range.getBoundingClientRect(); + + assert_approx_equals(axTextBounds1.x, rangeBounds.left, epsilon, "Line 1 left"); + assert_approx_equals(axTextBounds1.y, rangeBounds.top, epsilon, "Line 1 top"); + assert_approx_equals(axTextBounds1.width, rangeBounds.width, epsilon, "Line 1 width"); + assert_approx_equals(axTextBounds1.height, rangeBounds.height, epsilon, "Line 1 height"); + + var axStaticText2 = axParagraph.childAtIndex(2); + eval("var axTextBounds2 = " + axStaticText2.boundsForRange(0, 6)); + range.setStart(paragraph.lastChild, 0); + range.setEnd(paragraph.lastChild, 6); + rangeBounds = range.getBoundingClientRect(); + + assert_approx_equals(axTextBounds2.x, rangeBounds.left, epsilon, "Line 2 left"); + assert_approx_equals(axTextBounds2.y, rangeBounds.top, epsilon, "Line 2 top"); + assert_approx_equals(axTextBounds2.width, rangeBounds.width, epsilon, "Line 2 width"); + assert_approx_equals(axTextBounds2.height, rangeBounds.height, epsilon, "Line 2 height"); +}, "Check bounds of inline text boxes after line breaks"); +</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/overflow-hidden-child-layers-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/overflow-hidden-child-layers-expected.txt index c6a81d6..42fe6fb 100644 --- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/overflow-hidden-child-layers-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/overflow-hidden-child-layers-expected.txt
@@ -10,8 +10,8 @@ "children": [ { "name": "LayoutBlockFlow DIV class='box'", - "position": [38, 30], - "bounds": [140, 140], + "position": [39, 31], + "bounds": [138, 138], "drawsContent": true, "backgroundColor": "#0000FF" }
diff --git a/third_party/WebKit/LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.png b/third_party/WebKit/LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.png index 5d81bdc8..35d9b26 100644 --- a/third_party/WebKit/LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.png +++ b/third_party/WebKit/LayoutTests/compositing/culling/scrolled-within-boxshadow-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/culling/translated-boxshadow-expected.png b/third_party/WebKit/LayoutTests/compositing/culling/translated-boxshadow-expected.png index e1fb5a2..0b7ed078 100644 --- a/third_party/WebKit/LayoutTests/compositing/culling/translated-boxshadow-expected.png +++ b/third_party/WebKit/LayoutTests/compositing/culling/translated-boxshadow-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/culling/unscrolled-within-boxshadow-expected.png b/third_party/WebKit/LayoutTests/compositing/culling/unscrolled-within-boxshadow-expected.png index 5d81bdc8..35d9b26 100644 --- a/third_party/WebKit/LayoutTests/compositing/culling/unscrolled-within-boxshadow-expected.png +++ b/third_party/WebKit/LayoutTests/compositing/culling/unscrolled-within-boxshadow-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/foreground-layer-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/foreground-layer-expected.txt index ea7e791f..e5bf5d70 100644 --- a/third_party/WebKit/LayoutTests/compositing/geometry/foreground-layer-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/geometry/foreground-layer-expected.txt
@@ -11,14 +11,14 @@ "children": [ { "name": "LayoutBlockFlow (relative positioned) DIV class='main box'", - "position": [18, 88], - "bounds": [320, 320], + "position": [19, 89], + "bounds": [318, 318], "drawsContent": true, "backgroundColor": "#FF0000", "children": [ { "name": "LayoutBlockFlow (positioned) DIV class='negative child'", - "position": [60, 60], + "position": [59, 59], "bounds": [50, 50], "transform": [ [1, 0, 0, 0], @@ -29,21 +29,21 @@ }, { "name": "LayoutBlockFlow (relative positioned) DIV class='main box' (foreground) Layer", - "bounds": [320, 320], + "bounds": [318, 318], "drawsContent": true } ] }, { "name": "LayoutBlockFlow (relative positioned) DIV class='main box'", - "position": [362, 18], - "bounds": [320, 320], + "position": [363, 19], + "bounds": [318, 318], "drawsContent": true, "backgroundColor": "#FF0000", "children": [ { "name": "Child Containment Layer", - "position": [60, 60], + "position": [59, 59], "bounds": [200, 200], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/composited-parent-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/composited-parent-iframe-expected.txt index 0353e22a..98f85369 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/composited-parent-iframe-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/composited-parent-iframe-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME", - "position": [-12, -12], - "bounds": [370, 220], + "position": [-11, -11], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-delayed-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-delayed-expected.txt index 255528bb..81be0a1 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-delayed-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-delayed-expected.txt
@@ -13,13 +13,13 @@ "children": [ { "name": "LayoutIFrame IFRAME id='iframe'", - "position": [8, 108], - "bounds": [370, 220], + "position": [9, 109], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-expected.txt index 8d96c672..5eb9222 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME id='parent-iframe'", - "position": [8, 8], - "bounds": [370, 220], + "position": [9, 9], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe2-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe2-expected.txt index 7543194..5651940 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe2-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe2-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME id='iframe' class='composited'", - "position": [8, 8], - "bounds": [370, 220], + "position": [9, 9], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe3-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe3-expected.txt index 5839791..13d3e635 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe3-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe3-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME id='iframe' class='composited'", - "position": [8, 8], - "bounds": [370, 220], + "position": [9, 9], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/enter-compositing-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/enter-compositing-iframe-expected.txt index 8d96c672..5eb9222 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/enter-compositing-iframe-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/enter-compositing-iframe-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME id='parent-iframe'", - "position": [8, 8], - "bounds": [370, 220], + "position": [9, 9], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/iframe-resize-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/iframe-resize-expected.txt index 3ae65e99..29b412e 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/iframe-resize-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/iframe-resize-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME id='parent-iframe' class='bigger'", - "position": [8, 8], - "bounds": [470, 190], + "position": [9, 9], + "bounds": [468, 188], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [400, 120], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-show-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-show-expected.txt index 3fd5ca4b..71b75af 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-show-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-show-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME", - "position": [-12, -12], - "bounds": [370, 220], + "position": [-11, -11], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-expected.txt index 509057ba..86bc611d 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME id='parent-iframe'", - "position": [8, 8], - "bounds": [370, 220], + "position": [9, 9], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/scrolling-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/scrolling-iframe-expected.txt index 225aa06d..301b11b 100644 --- a/third_party/WebKit/LayoutTests/compositing/iframes/scrolling-iframe-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/iframes/scrolling-iframe-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME id='parent-iframe'", - "position": [8, 8], - "bounds": [370, 220], + "position": [9, 9], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/rotate3d-overlap-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/rotate3d-overlap-expected.txt index 2adfdeb..4b65745 100644 --- a/third_party/WebKit/LayoutTests/compositing/layer-creation/rotate3d-overlap-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/rotate3d-overlap-expected.txt
@@ -12,22 +12,34 @@ "children": [ { "name": "LayoutBlockFlow (relative positioned) DIV class='box translateZ'", - "position": [25, 25], - "bounds": [106, 106], + "position": [23, 23], + "bounds": [110, 110], "drawsContent": true, "backgroundColor": "#0000FF" }, { - "name": "LayoutBlockFlow (relative positioned) DIV class='composited box rotate15'", - "position": [169, 25], - "bounds": [106, 106], - "drawsContent": true, - "backgroundColor": "#0000FF", - "transform": [ - [0.965925826289068, 0.258819045102521, 0, 0], - [-0.258819045102521, 0.965925826289068, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] + "name": "Squashing Containment Layer", + "shouldFlattenTransform": false, + "children": [ + { + "name": "LayoutBlockFlow (relative positioned) DIV class='composited box rotate15'", + "position": [167, 23], + "bounds": [110, 110], + "drawsContent": true, + "backgroundColor": "#0000FF", + "transform": [ + [0.965925826289068, 0.258819045102521, 0, 0], + [-0.258819045102521, 0.965925826289068, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ] + }, + { + "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV class='box')", + "position": [167, 143], + "bounds": [110, 110], + "drawsContent": true + } ] }, { @@ -36,8 +48,8 @@ "children": [ { "name": "LayoutBlockFlow (relative positioned) DIV class='composited box rotate45'", - "position": [313, 25], - "bounds": [106, 106], + "position": [311, 23], + "bounds": [110, 110], "drawsContent": true, "backgroundColor": "#0000FF", "transform": [ @@ -49,8 +61,8 @@ }, { "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV class='box')", - "position": [313, 145], - "bounds": [106, 106], + "position": [311, 143], + "bounds": [110, 110], "drawsContent": true } ]
diff --git a/third_party/WebKit/LayoutTests/fast/borders/border-radius-different-width-001-double-expected.png b/third_party/WebKit/LayoutTests/fast/borders/border-radius-different-width-001-double-expected.png index 103d4e3..eec840d3 100644 --- a/third_party/WebKit/LayoutTests/fast/borders/border-radius-different-width-001-double-expected.png +++ b/third_party/WebKit/LayoutTests/fast/borders/border-radius-different-width-001-double-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/borders/borderRadiusDotted04-expected.png b/third_party/WebKit/LayoutTests/fast/borders/borderRadiusDotted04-expected.png index 36476cb..d335492 100644 --- a/third_party/WebKit/LayoutTests/fast/borders/borderRadiusDotted04-expected.png +++ b/third_party/WebKit/LayoutTests/fast/borders/borderRadiusDotted04-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/borders/webkit-border-radius-expected.png b/third_party/WebKit/LayoutTests/fast/borders/webkit-border-radius-expected.png index 2c885e6..05e118e 100644 --- a/third_party/WebKit/LayoutTests/fast/borders/webkit-border-radius-expected.png +++ b/third_party/WebKit/LayoutTests/fast/borders/webkit-border-radius-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-fetch-failure-output-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-fetch-failure-output-expected.txt index 9cc9f97..b8be91b 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-fetch-failure-output-expected.txt +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-fetch-failure-output-expected.txt
@@ -1,6 +1,7 @@ -CONSOLE MESSAGE: line 12: If a Suborigin makes a request, a response without an Access-Control-Allow-Suborigin header should fail and should output a reasonable error message. CONSOLE ERROR: Fetch API cannot load http://127.0.0.1:8000/security/resources/cors-script.php?cors=false. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http-so://foobar.127.0.0.1:8000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. -CONSOLE MESSAGE: line 18: PASS: Fetch correctly failed CONSOLE ERROR: Fetch API cannot load http://127.0.0.1:8000/security/resources/cors-script.php?cors=false. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http-so://foobar.127.0.0.1:8000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. -CONSOLE MESSAGE: line 18: PASS: Fetch correctly failed +This is a testharness.js-based test. +PASS Custom headers causes preflight failure +PASS Lack of Access-Control-Allow-Suborigin on response causes failure +Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-fetch-failure-output.php b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-fetch-failure-output.php index ddee8738..32ed050 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-fetch-failure-output.php +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-fetch-failure-output.php
@@ -5,32 +5,13 @@ <html> <head> <meta charset="utf-8"> +<title>Fetches from suborigins require responses with valid Access-Control-Allow-Suborigin header</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> <body> <script> -if (window.testRunner) { - testRunner.waitUntilDone(); - testRunner.dumpAsText(); -} -console.log( - 'If a Suborigin makes a request, a response without an ' + - 'Access-Control-Allow-Suborigin header should fail and should output a ' + - 'reasonable error message.'); - -function success() { - console.log("PASS: Fetch correctly failed"); - next(); -} - -function failure() { - console.log("FAIL: Fetch incorrectly succeeded"); - next(); -} - -// First one should fail with preflight failure. Second one should -// fail with access control header failure. -var tests = [ - function() { +async_test(t => { var headers = new Headers(); headers.append("x-custom-header", "foobar"); var options = { @@ -39,26 +20,16 @@ fetch( "http://127.0.0.1:8000/security/resources/cors-script.php?cors=false", options) - .then(failure) - .catch(success); - }, - function() { + .then(t.unreached_func('Fetch succeeded')) + .catch(t.step_func_done()); + }, 'Custom headers causes preflight failure'); + +async_test(t => { fetch( "http://127.0.0.1:8000/security/resources/cors-script.php?cors=false") - .then(failure) - .catch(success); - } -]; - -function next() { - if (tests.length !== 0) { - tests.shift()(); - } else if (window.testRunner) { - testRunner.notifyDone(); - } -} - -next(); + .then(t.unreached_func('Fetch succeeded')) + .catch(t.step_func_done()); + }, 'Lack of Access-Control-Allow-Suborigin on response causes failure'); </script> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-failure-output-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-failure-output-expected.txt index 6937603..ed840e3b 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-failure-output-expected.txt +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-failure-output-expected.txt
@@ -1,6 +1,7 @@ -CONSOLE MESSAGE: line 12: If a Suborigin makes a request, a response without an Access-Control-Allow-Suborigin header should fail and should output a reasonable error message. CONSOLE ERROR: XMLHttpRequest cannot load http://127.0.0.1:8000/security/resources/cors-script.php?cors=false. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http-so://foobar.127.0.0.1:8000' is therefore not allowed access. -ALERT: PASS: XHR correctly failed CONSOLE ERROR: XMLHttpRequest cannot load http://127.0.0.1:8000/security/resources/cors-script.php?cors=false. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http-so://foobar.127.0.0.1:8000' is therefore not allowed access. -ALERT: PASS: XHR correctly failed +This is a testharness.js-based test. +PASS Custom headers causes preflight failure +PASS Lack of Access-Control-Allow-Suborigin on response causes failure +Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-failure-output.php b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-failure-output.php index bae8eb3..7a4f9ad 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-failure-output.php +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-failure-output.php
@@ -5,59 +5,30 @@ <html> <head> <meta charset="utf-8"> +<title>XHRs from suborigins require responses with valid Access-Control-Allow-Suborigin header</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> <body> <script> -if (window.testRunner) { - testRunner.waitUntilDone(); - testRunner.dumpAsText(); -} -console.log( - 'If a Suborigin makes a request, a response without an ' + - 'Access-Control-Allow-Suborigin header should fail and should ' + - 'output a reasonable error message.'); - -function success() { - alert('PASS: XHR correctly failed'); - next(); -} - -function failure() { - alert('FAIL: XHR incorrectly succeeded'); - next(); -} - -// First one should fail with preflight failure. Second one should -// fail with access control header failure. -var tests = [ - function() { +async_test(t => { var xhr = new XMLHttpRequest(); - xhr.onerror = success; - xhr.onload = failure; + xhr.onerror = t.step_func_done(); + xhr.onload = t.unreached_func('XHR succeeded'); xhr.open('GET', 'http://127.0.0.1:8000/security/resources/' + 'cors-script.php?cors=false'); xhr.setRequestHeader('x-custom-header', 'foobar'); xhr.send(); - }, - function() { + }, 'Custom headers causes preflight failure'); + +async_test(t => { var xhr = new XMLHttpRequest(); - xhr.onerror = success; - xhr.onload = failure; + xhr.onerror = t.step_func_done(); + xhr.onload = t.unreached_func(); xhr.open('GET', 'http://127.0.0.1:8000/security/resources/' + 'cors-script.php?cors=false'); xhr.send(); - } -]; - -function next() { - if (tests.length !== 0) { - tests.shift()(); - } else if (window.testRunner) { - testRunner.notifyDone(); - } -} - -next(); + }, 'Lack of Access-Control-Allow-Suborigin on response causes failure'); </script> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-preflight.php b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-preflight.php index c7958f6..76ec18c 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-preflight.php +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cors-xhr-preflight.php
@@ -19,28 +19,28 @@ } SuboriginXHRTest.prototype.execute = function() { - var settings = this; - async_test(test => { - var xhr = new XMLHttpRequest(); + var settings = this; + async_test(test => { + var xhr = new XMLHttpRequest(); - if (settings.crossorigin_value === 'use-credentials') { - xhr.withCredentials = true; - } + if (settings.crossorigin_value === 'use-credentials') { + xhr.withCredentials = true; + } - if (settings.pass) { - xhr.onload = test.step_func_done(); - xhr.onerror = test.unreached_func('Good XHR fired error handler.'); - } else { - xhr.onload = test.unreached_func('Bad XHR successful.'); - xhr.onerror = test.step_func_done(); - } + if (settings.pass) { + xhr.onload = test.step_func_done(); + xhr.onerror = test.unreached_func('Good XHR fired error handler.'); + } else { + xhr.onload = test.unreached_func('Bad XHR successful.'); + xhr.onerror = test.step_func_done(); + } - xhr.open('GET', settings.src); - // Set a custom header to force a preflight. Even though the - // scheme/host/port of the source and destination origins are the same, the - // Suborigin should cause the request to be treated as cross-origin. - xhr.setRequestHeader('x-custom-header', 'foobar'); - xhr.send(); + xhr.open('GET', settings.src); + // Set a custom header to force a preflight. Even though the + // scheme/host/port of the source and destination origins are the same, the + // Suborigin should cause the request to be treated as cross-origin. + xhr.setRequestHeader('x-custom-header', 'foobar'); + xhr.send(); }, settings.name); };
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-cors-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-cors-expected.txt index f95bb99..313696c 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-cors-expected.txt +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-cors-expected.txt
@@ -1,15 +1,5 @@ CONSOLE ERROR: line 1: Uncaught SomeError -The Test passes if 'window.onerror' gets unsanitized information about an exception thrown in a script loaded with a 'crossorigin' attribute, and delivered with valid CORS headers. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS /SomeError/.test(msg) is true -PASS url is "http://127.0.0.1:8000/security/resources/cors-script.php?fail=true&cors=http-so://foobar.127.0.0.1:8000" -PASS line is 1 -PASS column is 1 -PASS window.errorObject is not null -PASS successfullyParsed is true - -TEST COMPLETE +This is a testharness.js-based test. +PASS Verify that thrown exception is unsanitized when crossorigin script loaded with crossorigin attribute +Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-cors.php b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-cors.php index cbe183e..fde9583 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-cors.php +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-cors.php
@@ -4,31 +4,24 @@ <!DOCTYPE html> <head> <meta charset="utf-8"> +<title>Verify that thrown exception is unsanitized when crossorigin script loaded with crossorigin attribute</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> <body> -<script src="/js-test-resources/js-test.js"></script> <script> -window.jsTestIsAsync = true; -description( - 'The Test passes if \'window.onerror\' gets unsanitized information about ' + - 'an exception thrown in a script loaded with a \'crossorigin\' attribute, ' + - 'and delivered with valid CORS headers.'); +setup({ allow_uncaught_exception: true }); window.onerror = function(msg, url, line, column, error) { - window.msg = msg; - window.url = url; - window.line = line; - window.column = column; - window.errorObject = error; - shouldBeTrue('/SomeError/.test(msg)'); - shouldBeEqualToString( - 'url', + assert_true(/SomeError/.test(msg)); + assert_equals( + url, 'http://127.0.0.1:8000/security/resources/cors-script.php?' + - 'fail=true&cors=http-so://foobar.127.0.0.1:8000'); - shouldBe('line', '1'); - shouldBe('column', '1'); - shouldNotBe('window.errorObject', 'null'); - finishJSTest(); + 'fail=true&cors=http-so://foobar.127.0.0.1:8000') + assert_equals(line, 1) + assert_equals(column, 1); + assert_not_equals(error, null); + done(); } </script> <script crossorigin src="/security/resources/cors-script.php?fail=true&cors=http-so://foobar.127.0.0.1:8000"></script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-no-cors-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-no-cors-expected.txt index 2886f0e..3bd5f8ae 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-no-cors-expected.txt +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-no-cors-expected.txt
@@ -1,15 +1,5 @@ CONSOLE ERROR: line 1: Uncaught SomeError -The test passes if 'window.onerror' gets sanitized information about an exception thrown in a script loaded from a suborigin. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS /SomeError/.test(msg) is false -PASS url is "" -PASS line is 0 -PASS column is 0 -PASS window.errorObject is null -PASS successfullyParsed is true - -TEST COMPLETE +This is a testharness.js-based test. +PASS Verify that thrown exception is sanitized when crossorigin script loaded without crossorigin attribute +Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-no-cors.php b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-no-cors.php index 7fac8ca1..901f8e42 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-no-cors.php +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-script-window-onerror-no-cors.php
@@ -4,27 +4,21 @@ <!DOCTYPE html> <head> <meta charset="utf-8"> +<title>Verify that thrown exception is sanitized when crossorigin script loaded without crossorigin attribute</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> <body> -<script src="/js-test-resources/js-test.js"></script> <script> -window.jsTestIsAsync = true; -description( - 'The test passes if \'window.onerror\' gets sanitized information about ' + - 'an exception thrown in a script loaded from a suborigin.'); +setup({ allow_uncaught_exception: true }); window.onerror = function(msg, url, line, column, error) { - window.msg = msg; - window.url = url; - window.line = line; - window.column = column; - window.errorObject = error; - shouldBeFalse('/SomeError/.test(msg)'); - shouldBeEqualToString('url', ''); - shouldBe('line', '0'); - shouldBe('column', '0'); - shouldBe('window.errorObject', 'null'); - finishJSTest(); + assert_false(/SomeError/.test(msg)); + assert_equals(url, ''); + assert_equals(line, 0); + assert_equals(column, 0); + assert_equals(error, null); + done(); } </script> <script src="/security/resources/cors-script.php?fail=true&cors=false"></script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-event-exception-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-event-exception-expected.txt deleted file mode 100644 index 8edc5066..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-event-exception-expected.txt +++ /dev/null
@@ -1,11 +0,0 @@ -Cross-origin access to 'window.event' should throw a SecurityError. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS frame.contentWindow.event threw exception SecurityError: Blocked a frame with origin "http-so://foobar.127.0.0.1:8000" from accessing a cross-origin frame.. -PASS frame.contentWindow.event = 1; threw exception SecurityError: Blocked a frame with origin "http-so://foobar.127.0.0.1:8000" from accessing a cross-origin frame.. -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-event-exception.php b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-event-exception.php index 4406e3d4..59f462f 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-event-exception.php +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-event-exception.php
@@ -5,29 +5,24 @@ <html> <head> <meta charset="utf-8"> +<title>Crossorigin access to window.event should throw a SecurityError</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> <body> -<iframe src="http://127.0.0.1:8000/"></iframe> -<script src="/js-test-resources/js-test.js"></script> <script> -window.jsTestIsAsync = true; -description( - 'Cross-origin access to \'window.event\' should throw a SecurityError.'); - -var frame = document.querySelector('iframe'); -window.onload = function () { - shouldThrow( - 'frame.contentWindow.event', - '"SecurityError: Blocked a frame with origin ' + - '\\"http-so://foobar.127.0.0.1:8000\\" from accessing a ' + - 'cross-origin frame."'); - shouldThrow( - 'frame.contentWindow.event = 1;', - '"SecurityError: Blocked a frame with origin ' + - '\\"http-so://foobar.127.0.0.1:8000\\" from accessing a ' + - 'cross-origin frame."'); - finishJSTest(); +var iframe = document.createElement('iframe'); +iframe.src = 'http://127.0.0.1:8000/'; +iframe.onload = function() { + assert_throws('SecurityError', function() { + var e = iframe.contentWindow.event; + }); + assert_throws('SecurityError', function() { + iframe.contentWindow.event = 1; + }); + done(); }; +document.body.appendChild(iframe); </script> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-open-exception-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-open-exception-expected.txt deleted file mode 100644 index f116532..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-open-exception-expected.txt +++ /dev/null
@@ -1,11 +0,0 @@ -Cross-origin access to 'window.open' and 'window.opener' should throw a SecurityError. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS frame.contentWindow.open() threw exception SecurityError: Blocked a frame with origin "http-so://foobar.127.0.0.1:8000" from accessing a cross-origin frame.. -PASS frame.contentWindow.opener = 1; threw exception SecurityError: Failed to set the 'opener' property on 'Window': Blocked a frame with origin "http-so://foobar.127.0.0.1:8000" from accessing a cross-origin frame.. -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-open-exception.php b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-open-exception.php index c711b62..2e66a68 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-open-exception.php +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-window-open-exception.php
@@ -5,30 +5,24 @@ <html> <head> <meta charset="utf-8"> +<title>Crossorigin access to window.open and window.opener should throw a SecurityError</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> <body> -<iframe src="/"></iframe> -<script src="/js-test-resources/js-test.js"></script> <script> -window.jsTestIsAsync = true; -description( - 'Cross-origin access to \'window.open\' and \'window.opener\' should ' + - 'throw a SecurityError.'); -var frame = document.querySelector('iframe'); -window.onload = function () { - shouldThrow( - 'frame.contentWindow.open()', - '"SecurityError: Blocked a frame with origin ' + - '\\"http-so://foobar.127.0.0.1:8000\\" from accessing a ' + - 'cross-origin frame."'); - shouldThrow( - 'frame.contentWindow.opener = 1;', - '"SecurityError: Failed to set the \'opener\' property on ' + - '\'Window\': Blocked a frame with origin ' + - '\\"http-so://foobar.127.0.0.1:8000\\" from accessing a ' + - 'cross-origin frame."'); - finishJSTest(); +var iframe = document.createElement('iframe'); +iframe.src = '/'; +iframe.onload = function() { + assert_throws('SecurityError', function() { + iframe.contentWindow.open(); + }); + assert_throws('SecurityError', function() { + iframe.contentWindow.opener = 1; + }); + done(); }; +document.body.appendChild(iframe); </script> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-worker-onerror-no-cors-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-worker-onerror-no-cors-expected.txt deleted file mode 100644 index 2c71315..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-worker-onerror-no-cors-expected.txt +++ /dev/null
@@ -1,10 +0,0 @@ -Ensure that scripts imported into a Worker from cross-origin hosts trigger sanitized onerror messages. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS worker = new Worker("/workers/resources/worker-importscripts-onerror-sameorigin.js") threw exception SecurityError: Failed to construct 'Worker': Script at 'http://127.0.0.1:8000/workers/resources/worker-importscripts-onerror-sameorigin.js' cannot be accessed from origin 'http-so://foobar.127.0.0.1:8000'.. -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-worker-onerror-no-cors.php b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-worker-onerror-no-cors.php index 43d0704..98eea6c 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-worker-onerror-no-cors.php +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/crossorigin/suborigin-cross-origin-worker-onerror-no-cors.php
@@ -5,25 +5,19 @@ <html> <head> <meta charset="utf-8"> -<script> -window.jsTestIsAsync = true; -window.isOnErrorTest = true; -</script> -<script src="/js-test-resources/js-test.js"></script> +<title>Scripts imported into a worker from crossorigin hosts throw SecurityError</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> <body> <script> -description('Ensure that scripts imported into a Worker from cross-origin hosts trigger sanitized onerror messages.'); - var worker; var worker_path = - '/workers/resources/worker-importscripts-onerror-sameorigin.js'; -shouldThrow( - 'worker = new Worker("' + worker_path + '")', - '"SecurityError: Failed to construct \'Worker\': Script at ' + - '\'http://127.0.0.1:8000' + worker_path + - '\' cannot be accessed from origin \'http-so://foobar.127.0.0.1:8000\'."'); -finishJSTest(); + '/workers/resources/worker-importscripts-onerror-sameorigin.js'; +assert_throws('SecurityError', function() { + worker = new Worker(worker_path); + }); +done(); </script> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-allow-same-suborigin-access.html b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-allow-same-suborigin-access.html index 4ad57b3f..7cbaebb 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-allow-same-suborigin-access.html +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-allow-same-suborigin-access.html
@@ -15,33 +15,29 @@ test(); } -var tests = [ - function() { - async_test(function(t) { - window.onmessage = t.step_func(function(event) { - assert_equals(event.data, 'I am a secret'); - t.done(); - next_test(); - }); - var iframe = document.createElement('iframe'); - iframe.src = 'resources/iframe-reaches-into-parent.php?' + - 'suborigin=foobar&childsuborigin=foobar'; - document.body.appendChild(iframe); - }, 'iframe reaches into parent') - }, - function() { - async_test(function(t) { - window.onmessage = t.step_func(function(event) { - assert_equals(event.data, 'I am a secret'); - t.done(); - next_test(); - }); - var iframe = document.createElement('iframe'); - iframe.src = 'resources/reach-into-iframe.php?' + - 'suborigin=foobar&childsuborigin=foobar'; - document.body.appendChild(iframe); - }, 'Parent reaches into iframe') +function make_iframe_test(test_name, resource) { + return function() { + async_test(t => { + window.onmessage = t.step_func(function(event) { + assert_equals(event.data, 'I am a secret'); + // next_test() must be called before t.done() so that the next test, + // if one exists, is setup and the overall test doesn't end. + next_test(); + t.done(); + }); + var iframe = document.createElement('iframe'); + iframe.src = 'resources/' + resource + '?' + + 'suborigin=foobar&childsuborigin=foobar'; + document.body.appendChild(iframe); + }, test_name); } +} + +var tests = [ + make_iframe_test( + 'iframe reaches into parent', 'iframe-reaches-into-parent.php'), + make_iframe_test( + 'parent reaches into iframe', 'reach-into-iframe.php') ]; next_test();
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-in-meta-disallowed-console-warning-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-in-meta-disallowed-console-warning-expected.txt deleted file mode 100644 index 86f98e4..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-in-meta-disallowed-console-warning-expected.txt +++ /dev/null
@@ -1,2 +0,0 @@ -CONSOLE ERROR: line 9: Error with Suborigin header: Suborigin header with value 'foobar' was delivered via a <meta> element and not an HTTP header, which is disallowed. The Suborigin has been ignored. -
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-in-meta-disallowed-console-warning.html b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-in-meta-disallowed-console-warning.html deleted file mode 100644 index f910829..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-in-meta-disallowed-console-warning.html +++ /dev/null
@@ -1,11 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<meta charset="utf-8"> -<script> -if (window.testRunner) - testRunner.dumpAsText(); -</script> -<meta http-equiv="Suborigin" content="foobar"> -</head> -</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-in-meta-disallowed-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-in-meta-disallowed-expected.txt new file mode 100644 index 0000000..910c60e --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-in-meta-disallowed-expected.txt
@@ -0,0 +1,5 @@ +CONSOLE ERROR: line 5: Error with Suborigin header: Suborigin header with value 'foobar' was delivered via a <meta> element and not an HTTP header, which is disallowed. The Suborigin has been ignored. +This is a testharness.js-based test. +PASS The <meta> tag does not allow a page to enter a suborigin. +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-invalid-names-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-invalid-names-expected.txt index 8bc72a6..d390006 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-invalid-names-expected.txt +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-invalid-names-expected.txt
@@ -1,18 +1,12 @@ -ALERT: I am a secret CONSOLE ERROR: Error with Suborigin header: Invalid character ''' in suborigin. -ALERT: I am a secret CONSOLE ERROR: Error with Suborigin header: Invalid character ''' in suborigin. -ALERT: I am a secret CONSOLE ERROR: Error with Suborigin header: Invalid character ''' in suborigin. -ALERT: I am a secret CONSOLE ERROR: Error with Suborigin header: Invalid character ''' in suborigin. -ALERT: I am a secret CONSOLE ERROR: Error with Suborigin header: Invalid character '@' in suborigin. -ALERT: I am a secret CONSOLE ERROR: Error with Suborigin header: Invalid character 'b' in suborigin policy. Suborigin policy options must start and end with a single quote. -ALERT: I am a secret CONSOLE ERROR: Error with Suborigin header: Invalid character 'F' in suborigin. -ALERT: I am a secret CONSOLE ERROR: Error with Suborigin header: Invalid character 'F' in suborigin. -ALERT: I am a secret +This is a testharness.js-based test. +PASS Invalid suborigin names +Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-invalid-names.html b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-invalid-names.html index 70c5a27..397484d0 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-invalid-names.html +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-invalid-names.html
@@ -3,19 +3,12 @@ <head> <meta charset="utf-8"> <title>Invalid suborigin names</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> +<body> <script> -if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); -} - -function finish() { - if (window.testRunner) - testRunner.notifyDone(); -} - var test_suborigin_names = [ '', '\'foobar\'', @@ -28,24 +21,31 @@ 'FOOBAR', ]; -var iframe; -var i = 0; -function next() { - if (i >= test_suborigin_names.length) - finish(); - document.getElementById('iframe').src = 'resources/reach-into-iframe.php?' + - 'childsuborigin=' + - test_suborigin_names[i]; - i++; -} +async_test(t => { + var i = 0; -window.onmessage = function(event) { - alert(event.data); - next(); -}; + window.onmessage = t.step_func(function(event) { + assert_equals(event.data, 'I am a secret'); + next(); + }); -window.onload = function() { - next(); -}; + function next() { + if (i >= test_suborigin_names.length) { + t.done(); + return; + } + + var iframe = document.createElement('iframe'); + iframe.src = + 'resources/reach-into-iframe.php?childsuborigin=' + + test_suborigin_names[i]; + document.body.appendChild(iframe); + + i++; + } + + next(); + }, 'Invalid suborigin names'); </script> -<iframe id="iframe"></iframe> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-multiple-suborigins-disallowed-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-multiple-suborigins-disallowed-expected.txt index b6f792d..f0f5ffd 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-multiple-suborigins-disallowed-expected.txt +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-multiple-suborigins-disallowed-expected.txt
@@ -1,2 +1,7 @@ CONSOLE ERROR: Error with Suborigin header: Multiple Suborigin headers found. Ignoring all but the first. +This is a testharness.js-based test. +PASS 'foo' is the assigned suborigin +PASS 'bar' is not a valid suborigin +PASS Cannot access frame without suborigin +Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-multiple-suborigins-disallowed.php b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-multiple-suborigins-disallowed.php index 1f61f72..166bc43 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-multiple-suborigins-disallowed.php +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-multiple-suborigins-disallowed.php
@@ -5,11 +5,58 @@ <html> <head> <meta charset="utf-8"> +<title>Only first of multiple suborigins assigned</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> <body> <script> -if (window.testRunner) - testRunner.dumpAsText(); +async_test(t => { + window.secret = 'unchanged'; + var iframe = document.createElement('iframe'); + iframe.src = 'resources/post-to-parent.php?suborigin=foo'; + iframe.onload = t.step_func(function() { + try { + var secret = iframe.contentWindow.secret; + t.done(); + } catch (e) { + assert_unreached( + 'Unable to read secret from iframe in same suborigin'); + } + }); + document.body.appendChild(iframe); + }, '\'foo\' is the assigned suborigin'); + +async_test(t => { + window.secret = 'unchanged'; + var iframe = document.createElement('iframe'); + iframe.src = 'resources/post-to-parent.php?suborigin=bar'; + iframe.onload = t.step_func(function() { + try { + var secret = iframe.contentWindow.secret; + assert_unreached( + 'Successfully read secret from iframe in a suborigin'); + } catch (e) { + t.done(); + } + }); + document.body.appendChild(iframe); + }, '\'bar\' is not a valid suborigin'); + +async_test(t => { + window.secret = 'unchanged'; + var iframe = document.createElement('iframe'); + iframe.src = 'resources/post-to-parent.php'; + iframe.onload = t.step_func(function() { + try { + var secret = iframe.contentWindow.secret; + assert_unreached('Able to read secret from iframe without suborigin'); + } catch (e) { + t.done(); + } + }); + document.body.appendChild(iframe); + }, 'Cannot access frame without suborigin'); </script> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-valid-names-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-valid-names-expected.txt deleted file mode 100644 index 7f0c9b9..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-valid-names-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -ALERT: SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. -ALERT: SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. -ALERT: SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. -ALERT: SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. -ALERT: SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. -ALERT: SecurityError: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a cross-origin frame. -
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-valid-names.html b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-valid-names.html index 6572af7..fb264ab 100644 --- a/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-valid-names.html +++ b/third_party/WebKit/LayoutTests/http/tests/security/suborigins/suborigin-valid-names.html
@@ -3,19 +3,11 @@ <head> <meta charset="utf-8"> <title>Valid suborigin names</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> </head> - +<body> <script> -if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); -} - -function finish() { - if (window.testRunner) - testRunner.notifyDone(); -} - var test_suborigin_names = [ 'foobar', 'foob4r', @@ -25,23 +17,33 @@ 'foobar-' ]; -var iframe; -var i = 0; -function next() { - if (i >= test_suborigin_names.length) - finish(); - document.getElementById('iframe').src = - 'resources/reach-into-iframe.php?childsuborigin=' + test_suborigin_names[i]; - i++; -} +async_test(t => { + var i = 0; -window.onmessage = function(event) { - alert(event.data); - next(); -}; + window.onmessage = t.step_func(function(event) { + assert_equals( + event.data, + 'SecurityError: Blocked a frame with origin ' + + '"http://127.0.0.1:8000" from accessing a cross-origin frame.'); + next(); + }); -window.onload = function() { - next(); -}; + function next() { + if (i >= test_suborigin_names.length) { + t.done(); + return; + } + + var iframe = document.createElement('iframe'); + iframe.src = + 'resources/reach-into-iframe.php?childsuborigin=' + + test_suborigin_names[i]; + document.body.appendChild(iframe); + + i++; + } + + next(); + }, 'Valid suborigin names'); </script> -<iframe id="iframe"></iframe> +</body>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/box-shadow-add-repaint-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/box-shadow-add-repaint-expected.txt index 9c13d858..8ec7014f 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/box-shadow-add-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/box-shadow-add-repaint-expected.txt
@@ -10,7 +10,7 @@ "paintInvalidations": [ { "object": "LayoutBlockFlow DIV id='test'", - "rect": [8, 8, 110, 110], + "rect": [7, 7, 112, 112], "reason": "style change" } ]
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/box-shadow-change-repaint-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/box-shadow-change-repaint-expected.txt index 999c7fc..39056a8 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/box-shadow-change-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/box-shadow-change-repaint-expected.txt
@@ -10,7 +10,7 @@ "paintInvalidations": [ { "object": "LayoutBlockFlow DIV id='test'", - "rect": [8, 8, 120, 120], + "rect": [7, 7, 122, 122], "reason": "style change" } ]
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt index c09e9b57..ae41289 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt
@@ -6,6 +6,10 @@ }, { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "style change" }, { @@ -23,10 +27,6 @@ { "object": "LayoutBlockFlow (positioned) DIV id='overlay'", "reason": "layoutObject insertion" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt index e460773d..8f194db3 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
@@ -86,6 +86,10 @@ }, { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "location change" }, { @@ -95,10 +99,6 @@ { "object": "LayoutBlockFlow BODY", "reason": "location change" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-1-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-1-expected.txt index 6eea390..3c1302f4 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-1-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-1-expected.txt
@@ -21,6 +21,14 @@ "reason": "scroll" }, { + "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "location change" }, @@ -42,14 +50,6 @@ }, { "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", "reason": "location change" }, {
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-2-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-2-expected.txt index 6eea390..3c1302f4 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-2-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-2-expected.txt
@@ -21,6 +21,14 @@ "reason": "scroll" }, { + "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "location change" }, @@ -42,14 +50,6 @@ }, { "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", "reason": "location change" }, {
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-1-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-1-expected.txt index 02dab6c..ffbf191 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-1-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-1-expected.txt
@@ -108,6 +108,14 @@ "reason": "scroll" }, { + "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "location change" }, @@ -129,14 +137,6 @@ }, { "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", "reason": "location change" }, {
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-2-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-2-expected.txt index 02dab6c..ffbf191 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-2-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-2-expected.txt
@@ -108,6 +108,14 @@ "reason": "scroll" }, { + "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "location change" }, @@ -129,14 +137,6 @@ }, { "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", "reason": "location change" }, {
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/transform-replaced-shadows-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/transform-replaced-shadows-expected.txt index 6edfb4b..b0dece1 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/transform-replaced-shadows-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/transform-replaced-shadows-expected.txt
@@ -10,7 +10,7 @@ "paintInvalidations": [ { "object": "LayoutImage IMG id='box' class='smaller'", - "rect": [-12, 28, 280, 280], + "rect": [-10, 28, 276, 278], "reason": "subtree" } ]
diff --git a/third_party/WebKit/LayoutTests/platform/android/compositing/masks/masked-ancestor-expected.png b/third_party/WebKit/LayoutTests/platform/android/compositing/masks/masked-ancestor-expected.png new file mode 100644 index 0000000..ce2c0e82 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/compositing/masks/masked-ancestor-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/android/fast/borders/border-radius-different-width-001-double-expected.png b/third_party/WebKit/LayoutTests/platform/android/fast/borders/border-radius-different-width-001-double-expected.png new file mode 100644 index 0000000..103d4e3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/fast/borders/border-radius-different-width-001-double-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/android/fast/borders/webkit-border-radius-expected.png b/third_party/WebKit/LayoutTests/platform/android/fast/borders/webkit-border-radius-expected.png new file mode 100644 index 0000000..2c885e6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/fast/borders/webkit-border-radius-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/caret-subpixel-expected.txt b/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/caret-subpixel-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/paint/invalidation/caret-subpixel-expected.txt rename to third_party/WebKit/LayoutTests/platform/android/paint/invalidation/caret-subpixel-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt b/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt new file mode 100644 index 0000000..c09e9b57 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt
@@ -0,0 +1,33 @@ +{ + "objectPaintInvalidations": [ + { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { + "object": "LayoutView #document", + "reason": "style change" + }, + { + "object": "LayoutBlockFlow HTML", + "reason": "forced by layout" + }, + { + "object": "LayoutBlockFlow BODY class='noScroll'", + "reason": "style change" + }, + { + "object": "LayoutBlockFlow DIV id='container'", + "reason": "incremental" + }, + { + "object": "LayoutBlockFlow (positioned) DIV id='overlay'", + "reason": "layoutObject insertion" + }, + { + "object": "LayoutView #document", + "reason": "scroll" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/css/text-gradient-shadow-expected.txt b/third_party/WebKit/LayoutTests/platform/android/svg/css/text-gradient-shadow-expected.txt new file mode 100644 index 0000000..a861e517 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/css/text-gradient-shadow-expected.txt
@@ -0,0 +1,11 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x600 + LayoutSVGRoot {svg} at (200,20) size 439x265 + LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 + LayoutSVGResourceLinearGradient {linearGradient} [id="gradient"] [gradientUnits=objectBoundingBox] [start=(0,0)] [end=(1,0)] + LayoutSVGGradientStop {stop} [offset=0.00] [color=#FF0000] + LayoutSVGGradientStop {stop} [offset=1.00] [color=#0000FF] + LayoutSVGText {text} at (200,20) size 399x225 contains 1 chunk(s) + LayoutSVGInlineText {#text} at (0,0) size 399x225 + chunk 1 text run 1 at (200.00,200.00) startOffset 0 endOffset 3 width 399.00: "SVG"
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/css/text-shadow-multiple-expected.txt b/third_party/WebKit/LayoutTests/platform/android/svg/css/text-shadow-multiple-expected.txt new file mode 100644 index 0000000..30527e7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/css/text-shadow-multiple-expected.txt
@@ -0,0 +1,87 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x414 + LayoutBlockFlow {html} at (0,0) size 800x414 + LayoutBlockFlow {body} at (0,16) size 800x398 + LayoutBlockFlow {p} at (0,0) size 800x20 + LayoutText {#text} at (0,0) size 425x19 + text run at (0,0) width 425: "The first two texts should look identical, as there is no shadow applied." + LayoutBlockFlow (anonymous) at (0,36) size 800x155 + LayoutSVGRoot {svg} at (20,122) size 720x58 + LayoutSVGText {text} at (20,20) size 720x58 contains 1 chunk(s) + LayoutSVGTSpan {tspan} at (0,0) size 96x57 + LayoutSVGInlineText {#text} at (0,0) size 96x57 + chunk 1 text run 1 at (20.00,66.80) startOffset 0 endOffset 4 width 96.00: "This" + LayoutSVGInlineText {#text} at (96,0) size 14x57 + chunk 1 text run 1 at (116.00,66.80) startOffset 0 endOffset 1 width 14.00: " " + LayoutSVGTSpan {tspan} at (0,0) size 80x57 + LayoutSVGInlineText {#text} at (110,0) size 80x57 + chunk 1 text run 1 at (130.00,66.80) startOffset 0 endOffset 4 width 80.00: "text" + LayoutSVGInlineText {#text} at (190,0) size 14x57 + chunk 1 text run 1 at (210.00,66.80) startOffset 0 endOffset 1 width 14.00: " " + LayoutSVGTSpan {tspan} at (0,0) size 305x57 + LayoutSVGInlineText {#text} at (204,0) size 305x57 + chunk 1 text run 1 at (224.00,66.80) startOffset 0 endOffset 14 width 305.00: "casts multiple" + LayoutSVGInlineText {#text} at (509,0) size 211x57 + chunk 1 text run 1 at (529.00,66.80) startOffset 0 endOffset 1 width 14.00: " " + chunk 1 text run 1 at (543.00,66.80) startOffset 0 endOffset 7 width 197.00: "shadows" + LayoutText {#text} at (0,0) size 0x0 + LayoutBlockFlow {p} at (0,207) size 800x20 + LayoutText {#text} at (0,0) size 530x19 + text run at (0,0) width 530: "The next two texts have subtle differences, as the stroke/fill is painted seperated in SVG." + LayoutBlockFlow (anonymous) at (0,243) size 800x155 + LayoutSVGRoot {svg} at (0,312) size 747x91 + LayoutSVGText {text} at (20,20) size 720x58 contains 1 chunk(s) + LayoutSVGTSpan {tspan} at (0,0) size 96x57 + LayoutSVGInlineText {#text} at (0,0) size 96x57 + chunk 1 text run 1 at (20.00,66.80) startOffset 0 endOffset 4 width 96.00: "This" + LayoutSVGInlineText {#text} at (96,0) size 14x57 + chunk 1 text run 1 at (116.00,66.80) startOffset 0 endOffset 1 width 14.00: " " + LayoutSVGTSpan {tspan} at (0,0) size 80x57 + LayoutSVGInlineText {#text} at (110,0) size 80x57 + chunk 1 text run 1 at (130.00,66.80) startOffset 0 endOffset 4 width 80.00: "text" + LayoutSVGInlineText {#text} at (190,0) size 14x57 + chunk 1 text run 1 at (210.00,66.80) startOffset 0 endOffset 1 width 14.00: " " + LayoutSVGTSpan {tspan} at (0,0) size 305x57 + LayoutSVGInlineText {#text} at (204,0) size 305x57 + chunk 1 text run 1 at (224.00,66.80) startOffset 0 endOffset 14 width 305.00: "casts multiple" + LayoutSVGInlineText {#text} at (509,0) size 211x57 + chunk 1 text run 1 at (529.00,66.80) startOffset 0 endOffset 1 width 14.00: " " + chunk 1 text run 1 at (543.00,66.80) startOffset 0 endOffset 7 width 197.00: "shadows" + LayoutText {#text} at (0,0) size 0x0 +layer at (20,52) size 720x59 + LayoutBlockFlow (positioned) {div} at (20,52) size 720x59 + LayoutInline {span} at (0,0) size 96x57 [textStrokeWidth=1.00] + LayoutText {#text} at (0,1) size 96x57 + text run at (0,1) width 96: "This" + LayoutText {#text} at (96,1) size 14x57 + text run at (96,1) width 14: " " + LayoutInline {span} at (0,0) size 80x57 [textFillColor=#FFFFFF] [textStrokeWidth=1.00] + LayoutText {#text} at (110,1) size 80x57 + text run at (110,1) width 80: "text" + LayoutText {#text} at (190,1) size 14x57 + text run at (190,1) width 14: " " + LayoutInline {span} at (0,0) size 305x57 + LayoutText {#text} at (204,1) size 305x57 + text run at (204,1) width 305: "casts multiple" + LayoutText {#text} at (509,1) size 211x57 + text run at (509,1) width 14: " " + text run at (523,1) width 197: "shadows" +layer at (20,259) size 720x59 + LayoutBlockFlow (positioned) {div} at (20,259) size 720x59 + LayoutInline {span} at (0,0) size 96x57 [textStrokeWidth=1.00] + LayoutText {#text} at (0,1) size 96x57 + text run at (0,1) width 96: "This" + LayoutText {#text} at (96,1) size 14x57 + text run at (96,1) width 14: " " + LayoutInline {span} at (0,0) size 80x57 [textFillColor=#FFFFFF] [textStrokeWidth=1.00] + LayoutText {#text} at (110,1) size 80x57 + text run at (110,1) width 80: "text" + LayoutText {#text} at (190,1) size 14x57 + text run at (190,1) width 14: " " + LayoutInline {span} at (0,0) size 305x57 + LayoutText {#text} at (204,1) size 305x57 + text run at (204,1) width 305: "casts multiple" + LayoutText {#text} at (509,1) size 211x57 + text run at (509,1) width 14: " " + text run at (523,1) width 197: "shadows"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/iframes/invisible-nested-iframe-show-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/compositing/iframes/invisible-nested-iframe-show-expected.txt index 21fa972..6c009c83 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/compositing/iframes/invisible-nested-iframe-show-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/iframes/invisible-nested-iframe-show-expected.txt
@@ -11,13 +11,13 @@ "children": [ { "name": "LayoutIFrame IFRAME", - "position": [-12, -12], - "bounds": [370, 220], + "position": [-11, -11], + "bounds": [368, 218], "drawsContent": true, "children": [ { "name": "Frame Overflow Controls Host Layer", - "position": [35, 35], + "position": [34, 34], "bounds": [300, 150], "children": [ {
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/masks/masked-ancestor-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/masks/masked-ancestor-expected.png index ce2c0e82..809c0e4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/compositing/masks/masked-ancestor-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/masks/masked-ancestor-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-video-shadow-expected.png index 757dc64..fe36d178 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-video-shadow-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-video-shadow-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-expected.png index 8b5411c..4f76bae 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-zoom125-expected.png index 9714eab..aae43f1 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-zoom200-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-zoom200-expected.png index 853a1c3f..b15258f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-zoom200-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-one-row-appearance-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-one-row-appearance-expected.png index adbd3b5..7037fe4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-one-row-appearance-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-one-row-appearance-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-two-row-appearance-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-two-row-appearance-expected.png index 75c079b..09e0a08 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-two-row-appearance-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/color/color-suggestion-picker-two-row-appearance-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/menulist-appearance-basic-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/menulist-appearance-basic-expected.png index fe30a20..78158860 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/menulist-appearance-basic-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/menulist-appearance-basic-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/menulist-appearance-basic-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/menulist-appearance-basic-expected.txt index 1f99f133..708312da 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/menulist-appearance-basic-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select/menulist-appearance-basic-expected.txt
@@ -87,7 +87,13 @@ text run at (4,1) width 33: "foo" LayoutText {#text} at (187,186) size 4x19 text run at (187,186) width 4: " " - LayoutBR {BR} at (191,186) size 0x19 + LayoutMenuList {SELECT} at (195,186) size 126x20 [bgcolor=#DDDDDD] [border: (1px solid #A9A9A9)] + LayoutBlockFlow (anonymous) at (1,1) size 124x18 + LayoutText (anonymous) at (4,1) size 102x16 + text run at (4,1) width 102: "September 2016" + LayoutText {#text} at (325,186) size 4x19 + text run at (325,186) width 4: " " + LayoutBR {BR} at (329,186) size 0x19 LayoutMenuList {SELECT} at (6,230) size 63x27 [bgcolor=#DDDDDD] [border: (1px solid #A9A9A9)] LayoutBlockFlow (anonymous) at (1,1) size 61x26 LayoutText (anonymous) at (6,1) size 28x23
diff --git a/third_party/WebKit/LayoutTests/platform/linux/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png b/third_party/WebKit/LayoutTests/platform/linux/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png index ce087b8..9a997a4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/4776765-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/4776765-expected.txt index eba788b..be5fc6e 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/4776765-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/4776765-expected.txt
@@ -25,6 +25,16 @@ }, { "object": "LayoutBR BR", + "rect": [7, 83, 3, 21], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", + "rect": [7, 83, 3, 21], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", "rect": [7, 63, 3, 21], "reason": "invalidate paint rectangle" } @@ -33,6 +43,14 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV", "reason": "outline" },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/caret-with-composited-scroll-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/caret-with-composited-scroll-expected.txt index 0b45783..d8d9c51 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/caret-with-composited-scroll-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/caret-with-composited-scroll-expected.txt
@@ -39,6 +39,16 @@ "object": "LayoutBlockFlow DIV id='inner-editor'", "rect": [2, 1002, 3, 18], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [2, 1002, 3, 18], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [2, 1002, 3, 18], + "reason": "invalidate paint rectangle" } ] } @@ -69,6 +79,10 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl INPUT id='text'", "reason": "subtree" }, @@ -79,6 +93,10 @@ { "object": "Caret", "reason": "invalidate paint rectangle" + }, + { + "object": "Caret", + "reason": "invalidate paint rectangle" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt index 40f794a..1cc6316 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
@@ -71,6 +71,14 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "style change" }, { @@ -92,14 +100,6 @@ { "object": "InlineTextBox 'test1'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/delete-into-nested-block-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/delete-into-nested-block-expected.txt index 81a9c37..1524af3 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/delete-into-nested-block-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/delete-into-nested-block-expected.txt
@@ -57,6 +57,16 @@ "object": "LayoutText #text", "rect": [7, 126, 3, 22], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [7, 126, 3, 22], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [7, 126, 3, 22], + "reason": "invalidate paint rectangle" } ] } @@ -103,6 +113,10 @@ "reason": "layoutObject removal" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV", "reason": "outline" }, @@ -119,6 +133,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "layoutObject insertion" },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/inline-outline-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/inline-outline-repaint-expected.txt index f00316f..9930e77 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/inline-outline-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/inline-outline-repaint-expected.txt
@@ -27,6 +27,16 @@ "object": "LayoutText #text", "rect": [43, 174, 3, 22], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [43, 174, 3, 22], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [43, 174, 3, 22], + "reason": "invalidate paint rectangle" } ] } @@ -37,6 +47,10 @@ "reason": "layoutObject removal" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV", "reason": "forced by layout" }, @@ -49,6 +63,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutInline SPAN id='test'", "reason": "forced by layout" },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt index c513249..926b8ea 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -80,12 +80,12 @@ ], "objectPaintInvalidations": [ { - "object": "LayoutTextControl INPUT id='root'", - "reason": "subtree" - }, - { "object": "Caret", "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutTextControl INPUT id='root'", + "reason": "subtree" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt index cf039665..9d07cff7 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -39,6 +39,16 @@ "object": "LayoutText #text", "rect": [60, 4, 3, 16], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [60, 4, 3, 16], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [60, 4, 3, 16], + "reason": "invalidate paint rectangle" } ] } @@ -47,6 +57,10 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl INPUT id='root'", "reason": "subtree" }, @@ -67,6 +81,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-2-expected.txt index 509cf12a..367df1f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-2-expected.txt
@@ -133,6 +133,10 @@ ], "objectPaintInvalidations": [ { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "forced by layout" }, @@ -387,10 +391,6 @@ { "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'", "reason": "forced by layout" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-8-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-8-expected.txt index 09598361..d0c8fc8 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-8-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-8-expected.txt
@@ -117,6 +117,10 @@ "reason": "layoutObject removal" }, { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "forced by layout" }, @@ -355,10 +359,6 @@ { "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'", "reason": "forced by layout" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/overflow-scroll-body-appear-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/overflow-scroll-body-appear-expected.txt index 0d733d16..7b67112 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/overflow-scroll-body-appear-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/overflow-scroll-body-appear-expected.txt
@@ -69,6 +69,14 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "style change" }, { @@ -122,14 +130,6 @@ { "object": "InlineTextBox '.'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt index caf3769..bd7c8bb0 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt
@@ -73,6 +73,14 @@ "reason": "invalidate paint rectangle" }, { + "object": "HorizontalScrollbar", + "reason": "scroll" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutView #document", "reason": "subtree" }, @@ -95,14 +103,6 @@ { "object": "InlineTextBox 'scroll me'", "reason": "subtree" - }, - { - "object": "HorizontalScrollbar", - "reason": "scroll" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection-after-delete-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection-after-delete-expected.txt index f96b17e7..705bdb43 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection-after-delete-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection-after-delete-expected.txt
@@ -22,6 +22,16 @@ "object": "LayoutBR BR", "rect": [38, 78, 3, 21], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", + "rect": [38, 78, 3, 21], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", + "rect": [38, 78, 3, 21], + "reason": "invalidate paint rectangle" } ] } @@ -32,6 +42,10 @@ "reason": "layoutObject removal" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV id='test'", "reason": "forced by layout" }, @@ -44,6 +58,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBR BR", "reason": "forced by layout" },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt index 9b6e9c6..3ca55d73 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
@@ -86,6 +86,10 @@ }, { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "location change" }, { @@ -95,10 +99,6 @@ { "object": "LayoutBlockFlow BODY", "reason": "location change" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/text-match-document-change-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/text-match-document-change-expected.txt index e6eb689..70f4942e 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/text-match-document-change-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/text-match-document-change-expected.txt
@@ -42,6 +42,10 @@ "reason": "layoutObject removal" }, { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "forced by layout" }, @@ -60,10 +64,6 @@ { "object": "InlineTextBox 'After change'", "reason": "layoutObject insertion" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt index da7216ee..327dde1 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
@@ -65,6 +65,10 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "bounds change" }, { @@ -134,10 +138,6 @@ { "object": "InlineTextBox 'NNNN'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] } @@ -203,6 +203,10 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "bounds change" }, { @@ -268,10 +272,6 @@ { "object": "InlineTextBox 'NNNN'", "reason": "bounds change" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] } @@ -341,6 +341,10 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "bounds change" }, { @@ -378,10 +382,6 @@ { "object": "InlineTextBox 'NNNN'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/css/text-gradient-shadow-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/css/text-gradient-shadow-expected.txt index a861e517..a77f2e55 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/css/text-gradient-shadow-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/css/text-gradient-shadow-expected.txt
@@ -1,7 +1,7 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 layer at (0,0) size 800x600 - LayoutSVGRoot {svg} at (200,20) size 439x265 + LayoutSVGRoot {svg} at (200,20) size 438x264 LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGResourceLinearGradient {linearGradient} [id="gradient"] [gradientUnits=objectBoundingBox] [start=(0,0)] [end=(1,0)] LayoutSVGGradientStop {stop} [offset=0.00] [color=#FF0000]
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/css/text-shadow-multiple-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/css/text-shadow-multiple-expected.txt index 30527e7..f261d344 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/css/text-shadow-multiple-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/css/text-shadow-multiple-expected.txt
@@ -30,7 +30,7 @@ LayoutText {#text} at (0,0) size 530x19 text run at (0,0) width 530: "The next two texts have subtle differences, as the stroke/fill is painted seperated in SVG." LayoutBlockFlow (anonymous) at (0,243) size 800x155 - LayoutSVGRoot {svg} at (0,312) size 747x91 + LayoutSVGRoot {svg} at (0,311) size 747x94 LayoutSVGText {text} at (20,20) size 720x58 contains 1 chunk(s) LayoutSVGTSpan {tspan} at (0,0) size 96x57 LayoutSVGInlineText {#text} at (0,0) size 96x57
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png index 2d9e85a..365566b 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/menulist-appearance-basic-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/menulist-appearance-basic-expected.png index 0f690f79..b98822b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/menulist-appearance-basic-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/menulist-appearance-basic-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/menulist-appearance-basic-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/menulist-appearance-basic-expected.txt index 330b364..abf5fdf 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/menulist-appearance-basic-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/select/menulist-appearance-basic-expected.txt
@@ -87,7 +87,13 @@ text run at (8,1) width 36: "foo" LayoutText {#text} at (205,163) size 4x18 text run at (205,163) width 4: " " - LayoutBR {BR} at (209,163) size 0x18 + LayoutMenuList {SELECT} at (213,164) size 110x18 [bgcolor=#F8F8F8] [border: (1px solid #A6A6A6)] + LayoutBlockFlow (anonymous) at (1,1) size 108x16 + LayoutText (anonymous) at (8,1) size 87x13 + text run at (8,1) width 87: "September 2016" + LayoutText {#text} at (327,163) size 4x18 + text run at (327,163) width 4: " " + LayoutBR {BR} at (331,163) size 0x18 LayoutMenuList {SELECT} at (6,204) size 59x25 [bgcolor=#F8F8F8] [border: (1px solid #A6A6A6)] LayoutBlockFlow (anonymous) at (1,1) size 57x23 LayoutText (anonymous) at (12,1) size 25x19
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png index f8c9704..38a1039 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt index 0fa2451..ec8a0c3 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -80,12 +80,12 @@ ], "objectPaintInvalidations": [ { - "object": "LayoutTextControl INPUT id='root'", - "reason": "subtree" - }, - { "object": "Caret", "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutTextControl INPUT id='root'", + "reason": "subtree" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/menulist-appearance-basic-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/menulist-appearance-basic-expected.png index 67ae39d..9757171 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/menulist-appearance-basic-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/menulist-appearance-basic-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/menulist-appearance-basic-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/menulist-appearance-basic-expected.txt index 1125f17..68a3095 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/menulist-appearance-basic-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/select/menulist-appearance-basic-expected.txt
@@ -87,7 +87,13 @@ text run at (8,1) width 39: "foo" LayoutText {#text} at (212,163) size 4x18 text run at (212,163) width 4: " " - LayoutBR {BR} at (216,163) size 0x18 + LayoutMenuList {SELECT} at (220,164) size 116x18 [bgcolor=#F8F8F8] [border: (1px solid #A6A6A6)] + LayoutBlockFlow (anonymous) at (1,1) size 114x16 + LayoutText (anonymous) at (8,1) size 93x13 + text run at (8,1) width 93: "September 2016" + LayoutText {#text} at (340,163) size 4x18 + text run at (340,163) width 4: " " + LayoutBR {BR} at (344,163) size 0x18 LayoutMenuList {SELECT} at (6,204) size 61x25 [bgcolor=#F8F8F8] [border: (1px solid #A6A6A6)] LayoutBlockFlow (anonymous) at (1,1) size 59x23 LayoutText (anonymous) at (12,1) size 27x19
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png index c2540d5a..308f881 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt index c9535f2a..90b7233 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -80,12 +80,12 @@ ], "objectPaintInvalidations": [ { - "object": "LayoutTextControl INPUT id='root'", - "reason": "subtree" - }, - { "object": "Caret", "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutTextControl INPUT id='root'", + "reason": "subtree" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt index 967c98b..29b3fc345 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -39,6 +39,16 @@ "object": "LayoutText #text", "rect": [45, 6, 3, 13], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [45, 6, 3, 13], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [45, 6, 3, 13], + "reason": "invalidate paint rectangle" } ] } @@ -47,6 +57,10 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl INPUT id='root'", "reason": "subtree" }, @@ -67,6 +81,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/textarea-caret-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/textarea-caret-expected.txt index 5805d97e..964bd06 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/textarea-caret-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/textarea-caret-expected.txt
@@ -42,12 +42,26 @@ "object": "LayoutText #text", "rect": [151, 10, 2, 14], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [151, 10, 2, 14], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [151, 10, 2, 14], + "reason": "invalidate paint rectangle" } ] } ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl TEXTAREA id='editor'", "reason": "subtree" }, @@ -80,6 +94,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/masked-ancestor-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/masked-ancestor-expected.png index 916eeb8d..af8a80b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/masked-ancestor-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/masked-ancestor-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/multiple-masks-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/multiple-masks-expected.png index 996a5560..65651aa6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/multiple-masks-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/multiple-masks-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/simple-composited-mask-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/simple-composited-mask-expected.png index dca2a9ff..2f63d68 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/simple-composited-mask-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/masks/simple-composited-mask-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-video-shadow-expected.png index 57ad0929..93286fa88 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-video-shadow-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-video-shadow-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png index d841027..969d6fd9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png b/third_party/WebKit/LayoutTests/platform/mac/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png index c7c5b25f..2418bca6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/4776765-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/4776765-expected.txt index 8988cbc..ec9bd476 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/4776765-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/4776765-expected.txt
@@ -25,6 +25,16 @@ }, { "object": "LayoutBR BR", + "rect": [7, 77, 3, 20], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", + "rect": [7, 77, 3, 20], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", "rect": [7, 59, 3, 20], "reason": "invalidate paint rectangle" } @@ -33,6 +43,14 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV", "reason": "outline" },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/caret-subpixel-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/caret-subpixel-expected.txt index 60299d6..cb356a3 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/caret-subpixel-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/caret-subpixel-expected.txt
@@ -22,12 +22,26 @@ "object": "LayoutBlockFlow DIV id='inner-editor'", "rect": [209, 10, 4, 15], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [209, 10, 4, 15], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [209, 10, 4, 15], + "reason": "invalidate paint rectangle" } ] } ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl INPUT id='target'", "reason": "subtree" }, @@ -38,6 +52,10 @@ { "object": "Caret", "reason": "invalidate paint rectangle" + }, + { + "object": "Caret", + "reason": "invalidate paint rectangle" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/caret-with-composited-scroll-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/caret-with-composited-scroll-expected.txt index 58a372b..5a9289a 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/caret-with-composited-scroll-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/caret-with-composited-scroll-expected.txt
@@ -39,6 +39,16 @@ "object": "LayoutBlockFlow DIV id='inner-editor'", "rect": [2, 1002, 3, 15], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [2, 1002, 3, 15], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [2, 1002, 3, 15], + "reason": "invalidate paint rectangle" } ] } @@ -69,6 +79,10 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl INPUT id='text'", "reason": "subtree" }, @@ -79,6 +93,10 @@ { "object": "Caret", "reason": "invalidate paint rectangle" + }, + { + "object": "Caret", + "reason": "invalidate paint rectangle" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt index 01653d5..d4d2b19 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
@@ -71,6 +71,14 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "style change" }, { @@ -92,14 +100,6 @@ { "object": "InlineTextBox 'test1'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/delete-into-nested-block-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/delete-into-nested-block-expected.txt index 87390c9e..4e0bf06 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/delete-into-nested-block-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/delete-into-nested-block-expected.txt
@@ -57,6 +57,16 @@ "object": "LayoutText #text", "rect": [7, 118, 3, 21], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [7, 118, 3, 21], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [7, 118, 3, 21], + "reason": "invalidate paint rectangle" } ] } @@ -103,6 +113,10 @@ "reason": "layoutObject removal" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV", "reason": "outline" }, @@ -119,6 +133,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "layoutObject insertion" },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/inline-outline-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/inline-outline-repaint-expected.txt index 969e6d6..05d34eae 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/inline-outline-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/inline-outline-repaint-expected.txt
@@ -27,6 +27,16 @@ "object": "LayoutText #text", "rect": [45, 183, 3, 20], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [45, 183, 3, 20], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [45, 183, 3, 20], + "reason": "invalidate paint rectangle" } ] } @@ -37,6 +47,10 @@ "reason": "layoutObject removal" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV", "reason": "forced by layout" }, @@ -49,6 +63,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutInline SPAN id='test'", "reason": "forced by layout" },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt index 842ac69..fe8daf5 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -80,12 +80,12 @@ ], "objectPaintInvalidations": [ { - "object": "LayoutTextControl INPUT id='root'", - "reason": "subtree" - }, - { "object": "Caret", "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutTextControl INPUT id='root'", + "reason": "subtree" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt index bb30929..72b8b765 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -39,6 +39,16 @@ "object": "LayoutText #text", "rect": [38, 6, 3, 13], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [38, 6, 3, 13], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [38, 6, 3, 13], + "reason": "invalidate paint rectangle" } ] } @@ -47,6 +57,10 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl INPUT id='root'", "reason": "subtree" }, @@ -67,6 +81,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt index 1fcd677..134678d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -113,6 +113,14 @@ ], "objectPaintInvalidations": [ { + "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "forced by layout" }, @@ -311,14 +319,6 @@ { "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/overflow-scroll-body-appear-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/overflow-scroll-body-appear-expected.txt index 37b2e39..4b7a749 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/overflow-scroll-body-appear-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/overflow-scroll-body-appear-expected.txt
@@ -69,6 +69,14 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "style change" }, { @@ -122,14 +130,6 @@ { "object": "InlineTextBox '.'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt index b73c00d..7fc5d22 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt
@@ -73,6 +73,14 @@ "reason": "invalidate paint rectangle" }, { + "object": "HorizontalScrollbar", + "reason": "scroll" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutView #document", "reason": "subtree" }, @@ -95,14 +103,6 @@ { "object": "InlineTextBox 'scroll me'", "reason": "subtree" - }, - { - "object": "HorizontalScrollbar", - "reason": "scroll" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection-after-delete-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection-after-delete-expected.txt index 7da2a029..7b6f62d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection-after-delete-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection-after-delete-expected.txt
@@ -22,6 +22,16 @@ "object": "LayoutBR BR", "rect": [38, 74, 3, 20], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", + "rect": [38, 74, 3, 20], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", + "rect": [38, 74, 3, 20], + "reason": "invalidate paint rectangle" } ] } @@ -32,6 +42,10 @@ "reason": "layoutObject removal" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV id='test'", "reason": "forced by layout" }, @@ -44,6 +58,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBR BR", "reason": "forced by layout" },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/text-match-document-change-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/text-match-document-change-expected.txt index d0241ece..985885f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/text-match-document-change-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/text-match-document-change-expected.txt
@@ -42,6 +42,10 @@ "reason": "layoutObject removal" }, { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "forced by layout" }, @@ -60,10 +64,6 @@ { "object": "InlineTextBox 'After change'", "reason": "layoutObject insertion" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/textarea-caret-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/textarea-caret-expected.txt index 53aebf9..a763b5e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/textarea-caret-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/textarea-caret-expected.txt
@@ -42,12 +42,26 @@ "object": "LayoutText #text", "rect": [131, 10, 2, 14], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [131, 10, 2, 14], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [131, 10, 2, 14], + "reason": "invalidate paint rectangle" } ] } ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl TEXTAREA id='editor'", "reason": "subtree" }, @@ -80,6 +94,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt index cb25b9a..76072b91 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
@@ -65,6 +65,10 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "bounds change" }, { @@ -134,10 +138,6 @@ { "object": "InlineTextBox 'NNNN'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] } @@ -203,6 +203,10 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "bounds change" }, { @@ -268,10 +272,6 @@ { "object": "InlineTextBox 'NNNN'", "reason": "bounds change" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] } @@ -341,6 +341,10 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "bounds change" }, { @@ -378,10 +382,6 @@ { "object": "InlineTextBox 'NNNN'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/css/text-gradient-shadow-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/css/text-gradient-shadow-expected.txt index 445aeed..1a35683 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/css/text-gradient-shadow-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/css/text-gradient-shadow-expected.txt
@@ -1,7 +1,7 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 layer at (0,0) size 800x600 - LayoutSVGRoot {svg} at (200,20) size 441x270 + LayoutSVGRoot {svg} at (200,20) size 440x269 LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGResourceLinearGradient {linearGradient} [id="gradient"] [gradientUnits=objectBoundingBox] [start=(0,0)] [end=(1,0)] LayoutSVGGradientStop {stop} [offset=0.00] [color=#FF0000]
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/css/text-shadow-multiple-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/css/text-shadow-multiple-expected.txt index 0d86443..2cc45e1 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/css/text-shadow-multiple-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/css/text-shadow-multiple-expected.txt
@@ -30,7 +30,7 @@ LayoutText {#text} at (0,0) size 567x18 text run at (0,0) width 567: "The next two texts have subtle differences, as the stroke/fill is painted seperated in SVG." LayoutBlockFlow (anonymous) at (0,238) size 800x154 - LayoutSVGRoot {svg} at (0,308) size 744x89 + LayoutSVGRoot {svg} at (0,307) size 744x92 LayoutSVGText {text} at (20,21) size 717x56 contains 1 chunk(s) LayoutSVGTSpan {tspan} at (0,0) size 95x56 LayoutSVGInlineText {#text} at (0,0) size 95x56
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-background-images-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-background-images-expected.png index 1056d1e..62eb78f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-background-images-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-background-images-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/masks/masked-ancestor-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/masks/masked-ancestor-expected.png index 3c1b7dd6..e3e3cec9 100644 --- a/third_party/WebKit/LayoutTests/platform/win/compositing/masks/masked-ancestor-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/compositing/masks/masked-ancestor-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/border-radius-split-background-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/border-radius-split-background-expected.png index 5ac01f3..826e9b8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/border-radius-split-background-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/border-radius-split-background-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/border-radius-split-background-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/border-radius-split-background-image-expected.png index e64ac73..b0bc567 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/border-radius-split-background-image-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/border-radius-split-background-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-video-shadow-expected.png index d01b6ab..8defd39 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-video-shadow-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-video-shadow-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-styles-split-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-styles-split-expected.png index 34a8820f..2393034d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-styles-split-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-styles-split-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-expected.png index 6bc47b3..5501a0a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-zoom125-expected.png index 132f3499..83921f9 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-zoom200-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-zoom200-expected.png index 076cda4e..74e46ab 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-zoom200-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-one-row-appearance-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-one-row-appearance-expected.png index 99a8f81..2f7a58c 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-one-row-appearance-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-one-row-appearance-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-two-row-appearance-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-two-row-appearance-expected.png index 99680dc..a78036d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-two-row-appearance-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/color/color-suggestion-picker-two-row-appearance-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/menulist-appearance-basic-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/menulist-appearance-basic-expected.png index f3177b8f..d5802e9c 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/menulist-appearance-basic-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/menulist-appearance-basic-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/menulist-appearance-basic-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/menulist-appearance-basic-expected.txt index 807a15e..8208717 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/menulist-appearance-basic-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/select/menulist-appearance-basic-expected.txt
@@ -87,7 +87,13 @@ text run at (4,1) width 34: "foo" LayoutText {#text} at (189,188) size 4x17 text run at (189,188) width 4: " " - LayoutBR {BR} at (193,188) size 0x17 + LayoutMenuList {SELECT} at (197,187) size 126x20 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)] + LayoutBlockFlow (anonymous) at (1,1) size 124x18 + LayoutText (anonymous) at (4,1) size 102x16 + text run at (4,1) width 102: "September 2016" + LayoutText {#text} at (327,188) size 4x17 + text run at (327,188) width 4: " " + LayoutBR {BR} at (331,188) size 0x17 LayoutMenuList {SELECT} at (6,229) size 63x26 [bgcolor=#FFFFFF] [border: (1px solid #A9A9A9)] LayoutBlockFlow (anonymous) at (1,1) size 61x25 LayoutText (anonymous) at (6,1) size 28x22
diff --git a/third_party/WebKit/LayoutTests/platform/win/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png b/third_party/WebKit/LayoutTests/platform/win/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png index 399e0c4..2eb35bf 100644 --- a/third_party/WebKit/LayoutTests/platform/win/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/http/tests/webfont/popup-menu-load-webfont-after-open-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/4776765-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/4776765-expected.txt index f28a8d1..2404e86 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/4776765-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/4776765-expected.txt
@@ -25,6 +25,16 @@ }, { "object": "LayoutBR BR", + "rect": [7, 77, 3, 19], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", + "rect": [7, 77, 3, 19], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", "rect": [7, 59, 3, 19], "reason": "invalidate paint rectangle" } @@ -33,6 +43,14 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV", "reason": "outline" },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/caret-subpixel-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/caret-subpixel-expected.txt new file mode 100644 index 0000000..c009fdc --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/caret-subpixel-expected.txt
@@ -0,0 +1,62 @@ +{ + "name": "Content Root Layer", + "bounds": [800, 600], + "children": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "contentsOpaque": true, + "drawsContent": true, + "paintInvalidations": [ + { + "object": "LayoutTextControl INPUT id='target'", + "rect": [8, 8, 224, 22], + "reason": "subtree" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [10, 11, 201, 16], + "reason": "subtree" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [208, 10, 4, 18], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [208, 10, 4, 18], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [208, 10, 4, 18], + "reason": "invalidate paint rectangle" + } + ] + } + ], + "objectPaintInvalidations": [ + { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutTextControl INPUT id='target'", + "reason": "subtree" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "reason": "subtree" + }, + { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { + "object": "Caret", + "reason": "invalidate paint rectangle" + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/caret-with-composited-scroll-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/caret-with-composited-scroll-expected.txt index dff41bb..7f8cd0d4 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/caret-with-composited-scroll-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/caret-with-composited-scroll-expected.txt
@@ -39,6 +39,16 @@ "object": "LayoutBlockFlow DIV id='inner-editor'", "rect": [2, 1002, 3, 18], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [2, 1002, 3, 18], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBlockFlow DIV id='inner-editor'", + "rect": [2, 1002, 3, 18], + "reason": "invalidate paint rectangle" } ] } @@ -69,6 +79,10 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl INPUT id='text'", "reason": "subtree" }, @@ -79,6 +93,10 @@ { "object": "Caret", "reason": "invalidate paint rectangle" + }, + { + "object": "Caret", + "reason": "invalidate paint rectangle" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt index a34ee3f..eec44b5 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
@@ -71,6 +71,14 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "style change" }, { @@ -92,14 +100,6 @@ { "object": "InlineTextBox 'test1'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/delete-into-nested-block-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/delete-into-nested-block-expected.txt index 68571ea..6b996e2 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/delete-into-nested-block-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/delete-into-nested-block-expected.txt
@@ -57,6 +57,16 @@ "object": "LayoutText #text", "rect": [7, 118, 3, 20], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [7, 118, 3, 20], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [7, 118, 3, 20], + "reason": "invalidate paint rectangle" } ] } @@ -103,6 +113,10 @@ "reason": "layoutObject removal" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV", "reason": "outline" }, @@ -119,6 +133,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "layoutObject insertion" },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/inline-outline-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/inline-outline-repaint-expected.txt index 3f3ed422..fa91e45 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/inline-outline-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/inline-outline-repaint-expected.txt
@@ -27,6 +27,16 @@ "object": "LayoutText #text", "rect": [45, 182, 3, 20], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [45, 182, 3, 20], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [45, 182, 3, 20], + "reason": "invalidate paint rectangle" } ] } @@ -37,6 +47,10 @@ "reason": "layoutObject removal" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV", "reason": "forced by layout" }, @@ -49,6 +63,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutInline SPAN id='test'", "reason": "forced by layout" },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt index 5c5cb52..e41c0d6a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -80,12 +80,12 @@ ], "objectPaintInvalidations": [ { - "object": "LayoutTextControl INPUT id='root'", - "reason": "subtree" - }, - { "object": "Caret", "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutTextControl INPUT id='root'", + "reason": "subtree" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt index 2c09ac4..7be285e 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -39,6 +39,16 @@ "object": "LayoutText #text", "rect": [64, 4, 3, 16], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [64, 4, 3, 16], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [64, 4, 3, 16], + "reason": "invalidate paint rectangle" } ] } @@ -47,6 +57,10 @@ ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl INPUT id='root'", "reason": "subtree" }, @@ -67,6 +81,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt index 49cb14a..f8dfd64 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -113,6 +113,14 @@ ], "objectPaintInvalidations": [ { + "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "forced by layout" }, @@ -311,14 +319,6 @@ { "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/overflow-scroll-body-appear-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/overflow-scroll-body-appear-expected.txt index 0047151..3bc8375 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/overflow-scroll-body-appear-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/overflow-scroll-body-appear-expected.txt
@@ -69,6 +69,14 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "style change" }, { @@ -122,14 +130,6 @@ { "object": "InlineTextBox '.'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt index 8742ad9..e3747d8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt
@@ -73,6 +73,14 @@ "reason": "invalidate paint rectangle" }, { + "object": "HorizontalScrollbar", + "reason": "scroll" + }, + { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutView #document", "reason": "subtree" }, @@ -95,14 +103,6 @@ { "object": "InlineTextBox 'scroll me'", "reason": "subtree" - }, - { - "object": "HorizontalScrollbar", - "reason": "scroll" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection-after-delete-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection-after-delete-expected.txt index f8c6c013..fbafe7f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection-after-delete-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection-after-delete-expected.txt
@@ -22,6 +22,16 @@ "object": "LayoutBR BR", "rect": [38, 74, 3, 19], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", + "rect": [38, 74, 3, 19], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutBR BR", + "rect": [38, 74, 3, 19], + "reason": "invalidate paint rectangle" } ] } @@ -32,6 +42,10 @@ "reason": "layoutObject removal" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBlockFlow DIV id='test'", "reason": "forced by layout" }, @@ -44,6 +58,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutBR BR", "reason": "forced by layout" },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/text-match-document-change-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/text-match-document-change-expected.txt index 0545b834..373cb1a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/text-match-document-change-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/text-match-document-change-expected.txt
@@ -42,6 +42,10 @@ "reason": "layoutObject removal" }, { + "object": "VerticalScrollbar", + "reason": "scroll" + }, + { "object": "LayoutBlockFlow HTML", "reason": "forced by layout" }, @@ -60,10 +64,6 @@ { "object": "InlineTextBox 'After change'", "reason": "layoutObject insertion" - }, - { - "object": "VerticalScrollbar", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/textarea-caret-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/textarea-caret-expected.txt index 65ad43a..abf1f3e 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/textarea-caret-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/textarea-caret-expected.txt
@@ -37,12 +37,26 @@ "object": "LayoutText #text", "rect": [186, 10, 2, 18], "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [186, 10, 2, 18], + "reason": "invalidate paint rectangle" + }, + { + "object": "LayoutText #text", + "rect": [186, 10, 2, 18], + "reason": "invalidate paint rectangle" } ] } ], "objectPaintInvalidations": [ { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutTextControl TEXTAREA id='editor'", "reason": "subtree" }, @@ -67,6 +81,10 @@ "reason": "invalidate paint rectangle" }, { + "object": "Caret", + "reason": "invalidate paint rectangle" + }, + { "object": "LayoutText #text", "reason": "subtree" },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt index 1d5da92..2b513fb9a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
@@ -65,6 +65,10 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "bounds change" }, { @@ -134,10 +138,6 @@ { "object": "InlineTextBox 'NNNN'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] } @@ -203,6 +203,10 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "bounds change" }, { @@ -268,10 +272,6 @@ { "object": "InlineTextBox 'NNNN'", "reason": "bounds change" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] } @@ -341,6 +341,10 @@ "objectPaintInvalidations": [ { "object": "LayoutView #document", + "reason": "scroll" + }, + { + "object": "LayoutView #document", "reason": "bounds change" }, { @@ -378,10 +382,6 @@ { "object": "InlineTextBox 'NNNN'", "reason": "forced by layout" - }, - { - "object": "LayoutView #document", - "reason": "scroll" } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/css/text-gradient-shadow-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/css/text-gradient-shadow-expected.txt index 5a3f6bb..4dbbbd5 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/css/text-gradient-shadow-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/css/text-gradient-shadow-expected.txt
@@ -1,7 +1,7 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 layer at (0,0) size 800x600 - LayoutSVGRoot {svg} at (200,22) size 441x261 + LayoutSVGRoot {svg} at (200,22) size 440x260 LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGResourceLinearGradient {linearGradient} [id="gradient"] [gradientUnits=objectBoundingBox] [start=(0,0)] [end=(1,0)] LayoutSVGGradientStop {stop} [offset=0.00] [color=#FF0000]
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/css/text-shadow-multiple-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/css/text-shadow-multiple-expected.txt index 3f0e8e4..ee2764c 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/css/text-shadow-multiple-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/css/text-shadow-multiple-expected.txt
@@ -30,7 +30,7 @@ LayoutText {#text} at (0,0) size 568x17 text run at (0,0) width 568: "The next two texts have subtle differences, as the stroke/fill is painted seperated in SVG." LayoutBlockFlow (anonymous) at (0,238) size 800x154 - LayoutSVGRoot {svg} at (0,308) size 744x89 + LayoutSVGRoot {svg} at (0,307) size 744x92 LayoutSVGText {text} at (20,21) size 717x56 contains 1 chunk(s) LayoutSVGTSpan {tspan} at (0,0) size 95x56 LayoutSVGInlineText {#text} at (0,0) size 95x56
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-background-images-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-background-images-expected.png index 11caa10..b52fae9 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-background-images-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-background-images-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png index 084f6ba..7277c25d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/storage/websql/transaction-removed-context-crash-expected.txt b/third_party/WebKit/LayoutTests/storage/websql/transaction-removed-context-crash-expected.txt new file mode 100644 index 0000000..b7ff4a9b --- /dev/null +++ b/third_party/WebKit/LayoutTests/storage/websql/transaction-removed-context-crash-expected.txt
@@ -0,0 +1 @@ +If it doesn't crash, this test has passed.
diff --git a/third_party/WebKit/LayoutTests/storage/websql/transaction-removed-context-crash.html b/third_party/WebKit/LayoutTests/storage/websql/transaction-removed-context-crash.html new file mode 100644 index 0000000..b2c36f0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/storage/websql/transaction-removed-context-crash.html
@@ -0,0 +1,30 @@ +<!DOCTYPE html> +<title>Transaction from removed execution context</title> +<script> +if (window.testRunner) { + testRunner.dumpAsText(); + testRunner.waitUntilDone(); +} + +var script = ` +var db = openDatabase('db' + Math.random() + Date.now(), '1.0', 'test database', 2*1024); +db.transaction(tx => { + tx.executeSql('DROP TABLE IF EXISTS TestTable'); + tx.executeSql('CREATE TABLE IF NOT EXISTS TestTable (id unique,text)'); + tx.executeSql('DELETE FROM TestTable WHERE id=?', [1]); + frameElement.parentNode.removeChild(frameElement); +}); +`; + +window.addEventListener('DOMContentLoaded', e => { + var blob = new Blob(['<script>' + script + '<\/script>'], {'type': 'text/html'}); + var iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + iframe.src = URL.createObjectURL(blob); + if (window.testRunner) + window.setTimeout(() => { testRunner.notifyDone() }, 250); +}); +</script> +<body> +If it doesn't crash, this test has passed. +</body>
diff --git a/third_party/WebKit/LayoutTests/webaudio/realtimeanalyser-fftsize-reset.html b/third_party/WebKit/LayoutTests/webaudio/realtimeanalyser-fftsize-reset.html new file mode 100644 index 0000000..0f7666e --- /dev/null +++ b/third_party/WebKit/LayoutTests/webaudio/realtimeanalyser-fftsize-reset.html
@@ -0,0 +1,132 @@ +<!doctype html> +<html> + <head> + <title>Test fftSize Changes Resetting AnalyserNode State </title> + <script src="../resources/testharness.js"></script> + <script src="../resources/testharnessreport.js"></script> + <script src="resources/audio-testing.js"></script> + </head> + + <body> + <script> + // Fairly arbitrary sample rate. + var sampleRate = 24000; + + var audit = Audit.createTaskRunner(); + + // Verify that setting the fftSize resets the memory for the FFT smoothing + // operation. Only a few of the possible variations are tested. + + audit.defineTask("128->1024", function (taskDone) { + testFFTSize({ + initialFFTSize: 128, + finalFFTSize: 1024, + errorThreshold: { + relativeThreshold: 1.9095e-6 + } + }).then(taskDone); + }); + + audit.defineTask("512->256", function (taskDone) { + testFFTSize({ + initialFFTSize: 512, + finalFFTSize: 256, + errorThreshold: { + relativeThreshold: 1.8166e-6 + } + }).then(taskDone); + }); + + function testFFTSize(options) { + var { + initialFFTSize, finalFFTSize, errorThreshold + } = options; + + // The duration is fairly arbitrary as long as it's long enough for the + // FFT test. + var context = new OfflineAudioContext(1, sampleRate, sampleRate); + + // Actual source doesn't matter but a sawtooth is a nice waveform with + // lots of harmonic content. + var osc = context.createOscillator(); + osc.type = "sawtooth"; + + // The analyser under test. + var testAnalyser = context.createAnalyser(); + testAnalyser.fftSize = initialFFTSize; + + // The reference analyser. The fftSize is fixed to the desired value, + // and we turn off smoothing so that we get the FFT of the current time + // data. + var refAnalyser = context.createAnalyser(); + refAnalyser.fftSize = finalFFTSize; + refAnalyser.smoothingTimeConstant = 0; + + // Setup the graph and start the oscillator. + osc.connect(testAnalyser) + .connect(context.destination); + osc.connect(refAnalyser) + .connect(context.destination); + + osc.start(); + + // Let the analyser smooth a few FFTs (rather arbitrary, but should be + // more than one), then switch the size. + + var suspendFrame = 4 * initialFFTSize; + context.suspend(suspendFrame / context.sampleRate) + .then(function () { + testAnalyser.fftSize = finalFFTSize; + }) + .then(context.resume.bind(context)); + + // Wait some frames and grab the FFT data. This is fairly arbitrary + // too, and can be independent of the FFT sizes. + suspendFrame += 1024; + context.suspend(suspendFrame / context.sampleRate) + .then(function () { + var testFFT = new Float32Array(testAnalyser.frequencyBinCount); + var refFFT = new Float32Array(refAnalyser.frequencyBinCount) + var testSignal = new Float32Array(testAnalyser.fftSize); + var refSignal = new Float32Array(refAnalyser.fftSize); + + testAnalyser.getFloatTimeDomainData(testSignal); + refAnalyser.getFloatTimeDomainData(refSignal); + + testAnalyser.getFloatFrequencyData(testFFT); + refAnalyser.getFloatFrequencyData(refFFT); + + // Convert the FFT data from dB to linear + testFFT = testFFT.map(x => Math.pow(10, x / 20)); + refFFT = refFFT.map(x => Math.pow(10, x / 20)); + + // The test data has smoothing applied, but the reference doesn't. + // Apply the smoothing factor to the reference data. + var smoothing = 1 - testAnalyser.smoothingTimeConstant; + refFFT = refFFT.map(x => x * smoothing); + + var success = true; + + // First a basic sanity check that the time domain signals are + // exactly the same for both analysers. + success = Should("Time data", testSignal) + .beCloseToArray(refSignal, 0) && success; + + success = Should("Linear FFT data after setting fftSize = " + testAnalyser.fftSize, + testFFT) + .beCloseToArray(refFFT, errorThreshold) && success; + + Should("*** Changing fftSize from " + initialFFTSize + " to " + finalFFTSize, success) + .summarize( + "correctly reset the smoothing state", + "did not correctly reset the smoothing state"); + }) + .then(context.resume.bind(context)); + + return context.startRendering(); + } + + audit.runTasks(); + </script> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt index acb1388..1da00243 100644 --- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -4075,7 +4075,6 @@ getter permissions getter platform getter plugins - getter pointerEnabled getter presentation getter product getter productSub
diff --git a/third_party/WebKit/Source/core/events/EventTarget.cpp b/third_party/WebKit/Source/core/events/EventTarget.cpp index f273e0e..4d2932f4 100644 --- a/third_party/WebKit/Source/core/events/EventTarget.cpp +++ b/third_party/WebKit/Source/core/events/EventTarget.cpp
@@ -597,21 +597,10 @@ if (event->isPointerEvent() && static_cast<PointerEvent*>(event)->pointerType() == "touch") UseCounter::count(executingWindow->document(), UseCounter::PointerDownFiredForTouch); } - } else if (checkTypeThenUseCount(event, EventTypeNames::pointerenter, UseCounter::PointerEnterLeaveFired) - || checkTypeThenUseCount(event, EventTypeNames::pointerleave, UseCounter::PointerEnterLeaveFired) - || checkTypeThenUseCount(event, EventTypeNames::pointerover, UseCounter::PointerOverOutFired) - || checkTypeThenUseCount(event, EventTypeNames::pointerout, UseCounter::PointerOverOutFired)) { - LocalDOMWindow* executingWindow = this->executingWindow(); - Node* node = toNode(); - if (executingWindow && node && node->getNodeType() == Node::kElementNode && event->isPointerEvent()) { - const Element* element = static_cast<Element*>(node); - const PointerEvent* pointerEvent = static_cast<PointerEvent*>(event); - const UseCounter::Feature feature = (event->type() == EventTypeNames::pointerenter || event->type() == EventTypeNames::pointerleave) - ? UseCounter::PointerEnterLeaveFiredWhileCaptured - : UseCounter::PointerOverOutFiredWhileCaptured; - if (element->hasPointerCapture(pointerEvent->pointerId()) && element->hasProcessedPointerCapture(pointerEvent->pointerId())) - UseCounter::count(executingWindow->document(), feature); - } + } else if (checkTypeThenUseCount(event, EventTypeNames::pointerenter, UseCounter::PointerEnterLeaveFired)) { + } else if (checkTypeThenUseCount(event, EventTypeNames::pointerleave, UseCounter::PointerEnterLeaveFired)) { + } else if (checkTypeThenUseCount(event, EventTypeNames::pointerover, UseCounter::PointerOverOutFired)) { + } else if (checkTypeThenUseCount(event, EventTypeNames::pointerout, UseCounter::PointerOverOutFired)) { } ExecutionContext* context = getExecutionContext();
diff --git a/third_party/WebKit/Source/core/events/NavigatorEvents.cpp b/third_party/WebKit/Source/core/events/NavigatorEvents.cpp index e2a419c..eba1769 100644 --- a/third_party/WebKit/Source/core/events/NavigatorEvents.cpp +++ b/third_party/WebKit/Source/core/events/NavigatorEvents.cpp
@@ -46,9 +46,4 @@ return 0; } -bool NavigatorEvents::pointerEnabled(Navigator& navigator) -{ - return false; -} - } // namespace blink
diff --git a/third_party/WebKit/Source/core/events/NavigatorEvents.h b/third_party/WebKit/Source/core/events/NavigatorEvents.h index bc47673..787d16b 100644 --- a/third_party/WebKit/Source/core/events/NavigatorEvents.h +++ b/third_party/WebKit/Source/core/events/NavigatorEvents.h
@@ -41,7 +41,6 @@ STATIC_ONLY(NavigatorEvents); public: static long maxTouchPoints(Navigator&); - static bool pointerEnabled(Navigator&); }; } // namespace blink
diff --git a/third_party/WebKit/Source/core/events/NavigatorEvents.idl b/third_party/WebKit/Source/core/events/NavigatorEvents.idl index da729c9..433a944 100644 --- a/third_party/WebKit/Source/core/events/NavigatorEvents.idl +++ b/third_party/WebKit/Source/core/events/NavigatorEvents.idl
@@ -32,10 +32,4 @@ partial interface Navigator { readonly attribute long maxTouchPoints; - - // TODO(mustaq): This is a non-standard attribute, added behind the - // PointerEvent flag only to estimate potential risks. This returns |false| - // even though PointerEvent support is there. The attribute will be removed - // once we have the UseCounter data. See crbug.com/631897. - [RuntimeEnabled=PointerEvent,DeprecateAs=NavigatorPointerEnabled,NotEnumerable] readonly attribute boolean pointerEnabled; };
diff --git a/third_party/WebKit/Source/core/frame/Deprecation.cpp b/third_party/WebKit/Source/core/frame/Deprecation.cpp index fefdbf5a..4470ec0a 100644 --- a/third_party/WebKit/Source/core/frame/Deprecation.cpp +++ b/third_party/WebKit/Source/core/frame/Deprecation.cpp
@@ -380,9 +380,6 @@ case UseCounter::V8SVGViewElement_ViewTarget_AttributeGetter: return willBeRemoved("SVGViewElement.viewTarget", M56, "5665473114931200"); - case UseCounter::NavigatorPointerEnabled: - return "Navigator.pointerEnabled is a non-standard API added for experiments only. It will be removed in near future."; - case UseCounter::WebAudioAutoplayCrossOriginIframe: return willBeRemoved("Web Audio autoplay (without user gesture) from cross-origin iframes", M55, "6406908126691328");
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h index b0e493c..2ef5f13 100644 --- a/third_party/WebKit/Source/core/frame/UseCounter.h +++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1254,7 +1254,6 @@ RadioNameMatchingStrict = 1513, RadioNameMatchingASCIICaseless = 1514, RadioNameMatchingCaseFolding = 1515, - NavigatorPointerEnabled = 1516, InputSelectionGettersThrow = 1517, UsbGetDevices = 1519, UsbRequestDevice = 1520, @@ -1276,8 +1275,6 @@ PointerEnterLeaveFired = 1535, PointerOverOutFired = 1536, - PointerEnterLeaveFiredWhileCaptured = 1537, - PointerOverOutFiredWhileCaptured = 1538, DraggableAttribute = 1539, CleanScriptElementWithNonce = 1540, PotentiallyInjectedScriptElementWithNonce = 1541,
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp index 2673420..c240a25f 100644 --- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp +++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -438,19 +438,19 @@ void PaintPropertyTreeBuilder::updateScrollAndScrollTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { - if (object.isBoxModelObject() && object.hasOverflowClip()) { - PaintLayer* layer = toLayoutBoxModelObject(object).layer(); - DCHECK(layer); - DoubleSize scrollOffset = layer->getScrollableArea()->scrollOffset(); - if (!scrollOffset.isZero() || layer->scrollsOverflow()) { + if (object.hasOverflowClip()) { + const LayoutBox& box = toLayoutBox(object); + const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea(); + DoubleSize scrollOffset = box.scrolledContentOffset(); + if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) { TransformationMatrix matrix = TransformationMatrix().translate(-scrollOffset.width(), -scrollOffset.height()); context.current.transform = object.getMutableForPainting().ensureObjectPaintProperties().createOrUpdateScrollTranslation( context.current.transform, matrix, FloatPoint3D(), context.current.shouldFlattenInheritedTransform, context.current.renderingContextID); - IntSize scrollClip = layer->getScrollableArea()->visibleContentRect().size(); - IntSize scrollBounds = layer->getScrollableArea()->contentsSize(); - bool userScrollableHorizontal = layer->getScrollableArea()->userInputScrollable(HorizontalScrollbar); - bool userScrollableVertical = layer->getScrollableArea()->userInputScrollable(VerticalScrollbar); + IntSize scrollClip = scrollableArea->visibleContentRect().size(); + IntSize scrollBounds = scrollableArea->contentsSize(); + bool userScrollableHorizontal = scrollableArea->userInputScrollable(HorizontalScrollbar); + bool userScrollableVertical = scrollableArea->userInputScrollable(VerticalScrollbar); context.current.scroll = object.getMutableForPainting().ensureObjectPaintProperties().createOrUpdateScroll( context.current.scroll, context.current.transform, scrollClip, scrollBounds, userScrollableHorizontal, userScrollableVertical);
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImageChromeClient.cpp b/third_party/WebKit/Source/core/svg/graphics/SVGImageChromeClient.cpp index 8173387..83b8ab63 100644 --- a/third_party/WebKit/Source/core/svg/graphics/SVGImageChromeClient.cpp +++ b/third_party/WebKit/Source/core/svg/graphics/SVGImageChromeClient.cpp
@@ -34,7 +34,7 @@ namespace blink { -static const double animationFrameDelay = 0.025; +static const double animationFrameDelay = 1.0 / 60; SVGImageChromeClient::SVGImageChromeClient(SVGImage* image) : m_image(image)
diff --git a/third_party/WebKit/Source/modules/accessibility/AXInlineTextBox.cpp b/third_party/WebKit/Source/modules/accessibility/AXInlineTextBox.cpp index 9252393..a2cf0fc7 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXInlineTextBox.cpp +++ b/third_party/WebKit/Source/modules/accessibility/AXInlineTextBox.cpp
@@ -66,11 +66,17 @@ outBoundsInContainer = FloatRect(); outContainerTransform.setIdentity(); - if (!m_inlineTextBox) + if (!m_inlineTextBox || !parentObject() || !parentObject()->getLayoutObject()) return; *outContainer = parentObject(); outBoundsInContainer = FloatRect(m_inlineTextBox->localBounds()); + + // Subtract the local bounding box of the parent because they're + // both in the same coordinate system. + LayoutObject* parentLayoutObject = parentObject()->getLayoutObject(); + FloatRect parentBoundingBox = parentLayoutObject->localBoundingBoxRectForAccessibility(); + outBoundsInContainer.moveBy(-parentBoundingBox.location()); } bool AXInlineTextBox::computeAccessibilityIsIgnored(IgnoredReasons* ignoredReasons) const
diff --git a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp index 1d968f9..1f8d4d1 100644 --- a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp +++ b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp
@@ -94,18 +94,13 @@ return !!m_imageBuffer; } -static bool shouldAccelerate(IntSize surfaceSize) -{ - return RuntimeEnabledFeatures::accelerated2dCanvasEnabled(); -} - ImageBuffer* OffscreenCanvasRenderingContext2D::imageBuffer() const { if (!m_imageBuffer) { IntSize surfaceSize(width(), height()); OpacityMode opacityMode = hasAlpha() ? NonOpaque : Opaque; std::unique_ptr<ImageBufferSurface> surface; - if (shouldAccelerate(surfaceSize)) { + if (RuntimeEnabledFeatures::accelerated2dCanvasEnabled()) { surface.reset(new AcceleratedImageBufferSurface(surfaceSize, opacityMode)); }
diff --git a/third_party/WebKit/Source/modules/webdatabase/Database.cpp b/third_party/WebKit/Source/modules/webdatabase/Database.cpp index 3f3b630..7176166 100644 --- a/third_party/WebKit/Source/modules/webdatabase/Database.cpp +++ b/third_party/WebKit/Source/modules/webdatabase/Database.cpp
@@ -897,11 +897,13 @@ SecurityOrigin* Database::getSecurityOrigin() const { + if (!getExecutionContext()) + return nullptr; if (getExecutionContext()->isContextThread()) return m_contextThreadSecurityOrigin.get(); if (getDatabaseContext()->databaseThread()->isDatabaseThread()) return m_databaseThreadSecurityOrigin.get(); - return 0; + return nullptr; } bool Database::opened()
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp index 2f76b3b0..ad28611 100644 --- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp +++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
@@ -380,6 +380,11 @@ } } +static void hibernateWrapperForTesting(WeakPtr<Canvas2DLayerBridge> bridge) +{ + hibernateWrapper(bridge, 0); +} + void Canvas2DLayerBridge::hibernate() { DCHECK(!isHibernating()); @@ -606,7 +611,11 @@ m_layer->clearTexture(); m_logger->reportHibernationEvent(HibernationScheduled); m_hibernationScheduled = true; - Platform::current()->currentThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, WTF::bind(&hibernateWrapper, m_weakPtrFactory.createWeakPtr())); + if (m_dontUseIdleSchedulingForTesting) { + Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, WTF::bind(&hibernateWrapperForTesting, m_weakPtrFactory.createWeakPtr())); + } else { + Platform::current()->currentThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, WTF::bind(&hibernateWrapper, m_weakPtrFactory.createWeakPtr())); + } } if (!isHidden() && m_softwareRenderingWhileHidden) { flushRecordingOnly();
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h index fc94bdc..6e095af3 100644 --- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h +++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h
@@ -120,6 +120,7 @@ void prepareSurfaceForPaintingIfNeeded(); bool isHidden() { return m_isHidden; } OpacityMode opacityMode() { return m_opacityMode; } + void dontUseIdleSchedulingForTesting() { m_dontUseIdleSchedulingForTesting = true; } void beginDestruction(); void hibernate(); @@ -259,6 +260,7 @@ bool m_softwareRenderingWhileHidden; bool m_surfaceCreationFailedAtLeastOnce = false; bool m_hibernationScheduled = false; + bool m_dontUseIdleSchedulingForTesting = false; friend class Canvas2DLayerBridgeTest; friend class CanvasRenderingContext2DTest;
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp index f4673ee4..a4c7095 100644 --- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp +++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
@@ -101,7 +101,9 @@ public: PassRefPtr<Canvas2DLayerBridge> makeBridge(std::unique_ptr<FakeWebGraphicsContext3DProvider> provider, const IntSize& size, Canvas2DLayerBridge::AccelerationMode accelerationMode) { - return adoptRef(new Canvas2DLayerBridge(std::move(provider), size, 0, NonOpaque, accelerationMode, nullptr)); + RefPtr<Canvas2DLayerBridge> bridge = adoptRef(new Canvas2DLayerBridge(std::move(provider), size, 0, NonOpaque, accelerationMode, nullptr)); + bridge->dontUseIdleSchedulingForTesting(); + return bridge.release(); } protected: @@ -462,7 +464,7 @@ EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) .WillOnce(testing::Invoke(hibernationStartedEvent.get(), &WaitableEvent::signal)); postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); - // Toggle visibility before the idle tasks that enters hibernation gets a + // Toggle visibility before the task that enters hibernation gets a // chance to run. postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), false); postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); @@ -816,23 +818,6 @@ postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); } -class IdleFenceTask : public WebThread::IdleTask { -public: - IdleFenceTask(WaitableEvent* doneEvent) - : m_doneEvent(doneEvent) - { } - - virtual ~IdleFenceTask() { } - - void run(double /*deadline*/) override - { - m_doneEvent->signal(); - } - -private: - WaitableEvent* m_doneEvent; -}; - #if CANVAS2D_HIBERNATION_ENABLED TEST_F(Canvas2DLayerBridgeTest, TeardownWhileHibernationIsPending) #else @@ -863,13 +848,13 @@ // a bridge to hold the mockLogger. hibernationScheduledEvent->wait(); // Once we know the hibernation task is scheduled, we can schedule a fence. - // Assuming Idle tasks are guaranteed to run in the order they were + // Assuming tasks are guaranteed to run in the order they were // submitted, this fence will guarantee the attempt to hibernate runs to // completion before the thread is destroyed. // This test passes by not crashing, which proves that the WeakPtr logic // is sound. std::unique_ptr<WaitableEvent> fenceEvent = wrapUnique(new WaitableEvent()); - testThread->scheduler()->postIdleTask(BLINK_FROM_HERE, new IdleFenceTask(fenceEvent.get())); + testThread->getWebTaskRunner()->postTask(BLINK_FROM_HERE, WTF::bind(&WaitableEvent::signal, unretained(fenceEvent.get()))); fenceEvent->wait(); }
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py index d502f616..d89fb00 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py
@@ -156,7 +156,7 @@ return self.device_directory() + 'fonts/' def device_forwarder_path(self): - return self.device_directory() + 'forwarder' + return self.device_directory() + 'device_forwarder' def device_fifo_directory(self): return '/data/data/' + self.package_name() + '/files/' @@ -461,7 +461,10 @@ # Local public methods. def path_to_forwarder(self): - return self._build_path('forwarder') + return self._build_path('device_forwarder') + + def path_to_forwarder_host(self): + return self._build_path('host_forwarder') def path_to_md5sum(self): return self._build_path(MD5SUM_DEVICE_FILE_NAME) @@ -506,6 +509,7 @@ result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility') result = self._check_file_exists(self.path_to_md5sum_host(), 'md5sum host utility') and result result = self._check_file_exists(self.path_to_forwarder(), 'forwarder utility') and result + result = self._check_file_exists(self.path_to_forwarder_host(), 'forwarder host utility') and result if not result: # There is a race condition in adb at least <= 4.3 on Linux that causes it to go offline periodically @@ -815,7 +819,6 @@ self._err_fifo_path = driver_details.device_fifo_directory() + 'stderr.fifo' self._read_stdout_process = None self._read_stderr_process = None - self._forwarder_process = None self._original_governors = {} self._original_kptr_restrict = None @@ -965,7 +968,10 @@ self._android_commands.push(host_file, device_file) def _push_executable(self, log_callback): - self._push_file_if_needed(self._port.path_to_forwarder(), self._driver_details.device_forwarder_path(), log_callback) + self._push_file_if_needed( + self._port.path_to_forwarder(), + self._driver_details.device_forwarder_path(), + log_callback) for resource in self._driver_details.additional_resources(): self._push_file_if_needed(self._port._build_path( resource), self._driver_details.device_directory() + resource, log_callback) @@ -1145,14 +1151,13 @@ super(ChromiumAndroidDriver, self)._start(pixel_tests, per_test_args, wait_for_ready=False) self._log_debug('Starting forwarder') - self._forwarder_process = self._port._server_process_constructor( - self._port, 'Forwarder', self._android_commands.adb_command() + - ['shell', '%s -no-spawn-daemon %s' % (self._driver_details.device_forwarder_path(), FORWARD_PORTS)]) - self._forwarder_process.start() - - deadline = time.time() + DRIVER_START_STOP_TIMEOUT_SECS - if not self._wait_for_server_process_output(self._forwarder_process, deadline, 'Forwarding device port'): - return False + self._android_commands.run(['shell', self._driver_details.device_forwarder_path()]) + for forward_port in FORWARD_PORTS.split(): + self._port.host.executive.run_command([ + self._port.path_to_forwarder_host(), + '--adb=%s' % AndroidCommands.adb_command_path(self._port.host.executive, self._debug_logging), + '--serial-id=%s' % self._android_commands.get_serial(), + '--map', forward_port, forward_port]) self._android_commands.run(['logcat', '-c']) @@ -1261,11 +1266,16 @@ self._read_stderr_process.kill() self._read_stderr_process = None - super(ChromiumAndroidDriver, self).stop() + self._android_commands.run([ + 'shell', + self._driver_details.device_forwarder_path(), + '--kill-server']) - if self._forwarder_process: - self._forwarder_process.kill() - self._forwarder_process = None + self._port.host.executive.run_command([ + self._port.path_to_forwarder_host(), + '--kill-server']) + + super(ChromiumAndroidDriver, self).stop() if self._android_devices.is_device_prepared(self._android_commands.get_serial()): if not ChromiumAndroidDriver._loop_with_timeout(self._remove_all_pipes, DRIVER_START_STOP_TIMEOUT_SECS):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/optimize_baselines_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/optimize_baselines_unittest.py index f8a3538a..715e6a6 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/optimize_baselines_unittest.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/optimize_baselines_unittest.py
@@ -75,8 +75,8 @@ self.assertEquals( out, '{"add": [], "remove-lines": [], ' - '"delete": ["/test.checkout/LayoutTests/platform/test-mac-mac10.10/another/test-expected.png", ' - '"/test.checkout/LayoutTests/platform/test-mac-mac10.10/another/test-expected.txt"]}\n') + '"delete": ["/test.checkout/LayoutTests/platform/test-mac-mac10.10/another/test-expected.txt", ' + '"/test.checkout/LayoutTests/platform/test-mac-mac10.10/another/test-expected.png"]}\n') self.assertFalse( self.tool.filesystem.exists(self.tool.filesystem.join( test_port.layout_tests_dir(), 'platform/mac/another/test-expected.txt')))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py index deebf32b..78d3011 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py
@@ -87,42 +87,29 @@ TODO(qyearsley): Refactor this so that lines to remove are only tracked in one format. """ def __init__(self, files_to_add=None, files_to_delete=None, lines_to_remove=None): - self._files_to_add = set(files_to_add or []) - self._files_to_delete = set(files_to_delete or []) - lines_to_remove = lines_to_remove or {} - self._lines_to_remove = {t: set(builders) for t, builders in lines_to_remove.iteritems()} + self.files_to_add = files_to_add or [] + self.files_to_delete = files_to_delete or [] + self.lines_to_remove = lines_to_remove or {} def add_file(self, path): - self._files_to_add.add(path) + self.files_to_add.append(path) def delete_file(self, path): - self._files_to_delete.add(path) + self.files_to_delete.append(path) def remove_line(self, test, builder): - if test not in self._lines_to_remove: - self._lines_to_remove[test] = set() - self._lines_to_remove[test].add(builder) - - @property - def files_to_add(self): - return sorted(self._files_to_add - self._files_to_delete) - - @property - def files_to_delete(self): - return sorted(self._files_to_delete - self._files_to_add) - - @property - def lines_to_remove(self): - return {t: sorted(set(builders)) for t, builders in sorted(self._lines_to_remove.iteritems())} + if test not in self.lines_to_remove: + self.lines_to_remove[test] = [] + self.lines_to_remove[test].append(builder) def to_dict(self): remove_lines = [] - for test in sorted(self.lines_to_remove): - for builder in sorted(set(self.lines_to_remove[test])): + for test in self.lines_to_remove: + for builder in self.lines_to_remove[test]: remove_lines.append({'test': test, 'builder': builder}) return { - 'add': self.files_to_add, - 'delete': self.files_to_delete, + 'add': list(self.files_to_add), + 'delete': list(self.files_to_delete), 'remove-lines': remove_lines, } @@ -143,18 +130,19 @@ lines_to_remove[test] = [] lines_to_remove[test].append(builder) return ChangeSet( - files_to_add=files_to_add, - files_to_delete=files_to_delete, + files_to_add=list(files_to_add), + files_to_delete=list(files_to_delete), lines_to_remove=lines_to_remove) def update(self, other): assert isinstance(other, ChangeSet) - self._files_to_add.update(other.files_to_add) - self._files_to_delete.update(other.files_to_delete) + assert type(other.lines_to_remove) is dict + self.files_to_add.extend(other.files_to_add) + self.files_to_delete.extend(other.files_to_delete) for test in other.lines_to_remove: - if test not in self._lines_to_remove: - self._lines_to_remove[test] = set() - self._lines_to_remove[test].update(other.lines_to_remove[test]) + if test not in self.lines_to_remove: + self.lines_to_remove[test] = [] + self.lines_to_remove[test].extend(other.lines_to_remove[test]) class BaseInternalRebaselineCommand(AbstractRebaseliningCommand): @@ -499,18 +487,28 @@ return (SKIP in full_expectations.get_expectations(test) and SKIP not in generic_expectations.get_expectations(test)) - def _run_in_parallel(self, commands): + def _run_in_parallel(self, commands, update_scm=True): if not commands: - return ChangeSet() + return {} command_results = self._tool.executive.run_in_parallel(commands) for _, _, stderr in command_results: if stderr: _log.error(stderr) - return self._extract_scm_changes(command_results) + change_set = self._extract_scm_changes(command_results) - def _rebaseline(self, options, test_prefix_list): + # TODO(qyearsley): Instead of updating the SCM state here, aggregate changes + # and update once in _rebaseline. See http://crbug.com/639410. + if update_scm: + if change_set.files_to_delete: + self._tool.scm().delete_list(change_set.files_to_delete) + if change_set.files_to_add: + self._tool.scm().add_list(change_set.files_to_add) + + return change_set.lines_to_remove + + def _rebaseline(self, options, test_prefix_list, update_scm=True): """Downloads new baselines in parallel, then updates expectations files and optimizes baselines. @@ -528,6 +526,7 @@ "some/other.html" but only from builder-1. TODO(qyearsley): Replace test_prefix_list everywhere with some sort of class that contains the same data. + update_scm: If True, commands like `git add` and `git rm` will be run. """ for test, builds_to_check in sorted(test_prefix_list.items()): _log.info("Rebaselining %s", test) @@ -536,25 +535,25 @@ copy_baseline_commands, rebaseline_commands, extra_lines_to_remove = self._rebaseline_commands( test_prefix_list, options) + lines_to_remove = {} - change_set = ChangeSet(lines_to_remove=extra_lines_to_remove) + self._run_in_parallel(copy_baseline_commands, update_scm=update_scm) + lines_to_remove = self._run_in_parallel(rebaseline_commands, update_scm=update_scm) - change_set.update(self._run_in_parallel(copy_baseline_commands)) - change_set.update(self._run_in_parallel(rebaseline_commands)) + for test in extra_lines_to_remove: + if test in lines_to_remove: + lines_to_remove[test] = lines_to_remove[test] + extra_lines_to_remove[test] + else: + lines_to_remove[test] = extra_lines_to_remove[test] - if change_set.lines_to_remove: - self._update_expectations_files(change_set.lines_to_remove) + if lines_to_remove: + self._update_expectations_files(lines_to_remove) if options.optimize: # TODO(wkorman): Consider changing temporary branch to base off of HEAD rather than # origin/master to ensure we run baseline optimization processes with the same code as # auto-rebaseline itself. - change_set.update(self._run_in_parallel(self._optimize_baselines(test_prefix_list, options.verbose))) - - if change_set.files_to_delete: - self._tool.scm().delete_list(change_set.files_to_delete) - if change_set.files_to_add: - self._tool.scm().add_list(change_set.files_to_add) + self._run_in_parallel(self._optimize_baselines(test_prefix_list, options.verbose), update_scm=update_scm) def _suffixes_for_actual_failures(self, test, build, existing_suffixes): """Gets the baseline suffixes for actual mismatch failures in some results.
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py index 0914a0d..c62fd98 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py
@@ -83,7 +83,7 @@ if options.dry_run: return - self._rebaseline(options, test_prefix_list) + self._rebaseline(options, test_prefix_list, update_scm=False) def _filter_existing(self, test_prefix_list): """Filters out entries in |test_prefix_list| for tests that don't exist."""
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py index 20c1de4..abe3f60 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
@@ -934,93 +934,3 @@ self.calls = self.calls[:num_previous_calls] self.calls.append(new_calls) return command_outputs - - -class ChangeSetTest(unittest.TestCase): - - def test_constructor_and_getters(self): - change_set = ChangeSet( - files_to_add=['add/a.html', 'add/b.html'], - files_to_delete=['del/x.html'], - lines_to_remove={'my/test.html': ['my-builder']}) - self.assertEqual(change_set.files_to_add, ['add/a.html', 'add/b.html']) - self.assertEqual(change_set.files_to_delete, ['del/x.html']) - self.assertEqual(change_set.lines_to_remove, {'my/test.html': ['my-builder']}) - - def test_files_to_add_and_files_to_delete_are_sorted_no_dupes(self): - change_set = ChangeSet(files_to_add=['b', 'a', 'a'], files_to_delete=['x', 'y', 'x']) - self.assertEqual(change_set.files_to_add, ['a', 'b']) - self.assertEqual(change_set.files_to_delete, ['x', 'y']) - - def test_files_both_added_and_deleted_are_ignored(self): - change_set = ChangeSet(files_to_add=['a', 'b', 'c'], files_to_delete=['c', 'd', 'e']) - self.assertEqual(change_set.files_to_add, ['a', 'b']) - self.assertEqual(change_set.files_to_delete, ['d', 'e']) - change_set.add_file('d') - self.assertEqual(change_set.files_to_add, ['a', 'b']) - self.assertEqual(change_set.files_to_delete, ['e']) - change_set.delete_file('b') - self.assertEqual(change_set.files_to_add, ['a']) - self.assertEqual(change_set.files_to_delete, ['e']) - - def test_add_file(self): - change_set = ChangeSet() - change_set.add_file('a') - change_set.add_file('b') - change_set.add_file('b') - self.assertEqual(change_set.files_to_add, ['a', 'b']) - - def test_delete_file(self): - change_set = ChangeSet() - change_set.delete_file('a') - change_set.delete_file('b') - change_set.delete_file('b') - self.assertEqual(change_set.files_to_delete, ['a', 'b']) - - def test_remove_line(self): - change_set = ChangeSet() - change_set.remove_line('test', 'builder-b') - change_set.remove_line('test', 'builder-a') - change_set.remove_line('test', 'builder-a') - self.assertEqual( - change_set.lines_to_remove, - {'test': ['builder-a', 'builder-b']}) - - def test_from_dict(self): - change_set = ChangeSet.from_dict({ - 'add': ['to/add.html'], - 'remove-lines': [{'test': 'some/test.html', 'builder': 'builder-name'}] - }) - self.assertEqual(change_set.files_to_add, ['to/add.html']) - self.assertEqual(change_set.lines_to_remove, {'some/test.html': ['builder-name']}) - - def test_to_dict(self): - change_set = ChangeSet( - files_to_add=['add/b.html', 'add/a.html'], - files_to_delete=['del/x.html'], - lines_to_remove={ - 'x/test.html': ['builder-b', 'builder-b', 'builder-a'], - 'y/test.html': [], - 'z/test.html': ['builder-c']}) - self.assertEqual(change_set.to_dict(), { - 'add': ['add/a.html', 'add/b.html'], - 'delete': ['del/x.html'], - 'remove-lines': [ - {'test': 'x/test.html', 'builder': 'builder-a'}, - {'test': 'x/test.html', 'builder': 'builder-b'}, - {'test': 'z/test.html', 'builder': 'builder-c'}, - ], - }) - - def test_update(self): - change_set = ChangeSet( - files_to_add=['add/a.html', 'add/b.html'], - files_to_delete=['del/x.html'], - lines_to_remove={'my/test.html': ['my-builder']}) - change_set.update(ChangeSet( - files_to_add=['add/a.html', 'add/c.html', 'del/x.html'], - files_to_delete=['add/b.html', 'del/y.html'], - lines_to_remove={'my/test.html': ['my-builder', 'other-builder']})) - self.assertEqual(change_set.files_to_add, ['add/a.html', 'add/c.html']) - self.assertEqual(change_set.files_to_delete, ['del/y.html']) - self.assertEqual(change_set.lines_to_remove, {'my/test.html': ['my-builder', 'other-builder']})
diff --git a/third_party/sqlite/amalgamation/sqlite3.c b/third_party/sqlite/amalgamation/sqlite3.c index 58981ca9..97d1ab99 100644 --- a/third_party/sqlite/amalgamation/sqlite3.c +++ b/third_party/sqlite/amalgamation/sqlite3.c
@@ -26285,36 +26285,21 @@ return sqlite3AddInt64(pA, -iB); } } -#define TWOPOWER32 (((i64)1)<<32) -#define TWOPOWER31 (((i64)1)<<31) SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){ i64 iA = *pA; - i64 iA1, iA0, iB1, iB0, r; - - iA1 = iA/TWOPOWER32; - iA0 = iA % TWOPOWER32; - iB1 = iB/TWOPOWER32; - iB0 = iB % TWOPOWER32; - if( iA1==0 ){ - if( iB1==0 ){ - *pA *= iB; - return 0; + if( iB>0 ){ + if( iA>LARGEST_INT64/iB ) return 1; + if( iA<SMALLEST_INT64/iB ) return 1; + }else if( iB<0 ){ + if( iA>0 ){ + if( iB<SMALLEST_INT64/iA ) return 1; + }else if( iA<0 ){ + if( iB==SMALLEST_INT64 ) return 1; + if( iA==SMALLEST_INT64 ) return 1; + if( -iA>LARGEST_INT64/-iB ) return 1; } - r = iA0*iB1; - }else if( iB1==0 ){ - r = iA1*iB0; - }else{ - /* If both iA1 and iB1 are non-zero, overflow will result */ - return 1; } - testcase( r==(-TWOPOWER31)-1 ); - testcase( r==(-TWOPOWER31) ); - testcase( r==TWOPOWER31 ); - testcase( r==TWOPOWER31-1 ); - if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1; - r *= TWOPOWER32; - if( sqlite3AddInt64(&r, iA0*iB0) ) return 1; - *pA = r; + *pA = iA*iB; return 0; }
diff --git a/third_party/sqlite/patches/0014-backport-Address-integer-overflow-in-sqlite3MulInt64.patch b/third_party/sqlite/patches/0014-backport-Address-integer-overflow-in-sqlite3MulInt64.patch new file mode 100644 index 0000000..19a5210 --- /dev/null +++ b/third_party/sqlite/patches/0014-backport-Address-integer-overflow-in-sqlite3MulInt64.patch
@@ -0,0 +1,112 @@ +From 7bcd4f0f17fb5bb6ae11be6ef0b70197195f2d61 Mon Sep 17 00:00:00 2001 +From: Scott Hess <shess@chromium.org> +Date: Fri, 23 Sep 2016 09:54:14 -0700 +Subject: [PATCH 14/14] [backport] Address integer overflow in sqlite3MulInt64. + +SQLite check-in http://www.sqlite.org/src/info/db3ebd7c52cfc5fc + +"Improved implementation of 64-bit signed integer multiply that +correctly detects overflow (and promotes to floating-point) in some +corner cases. Fix for ticket [1ec41379c9c1e400]" + +http://www.sqlite.org/src/info/1ec41379c9c1e400" + +BUG=601727 +--- + third_party/sqlite/src/src/util.c | 37 +++++++++++------------------------ + third_party/sqlite/src/test/expr.test | 27 +++++++++++++++++++++++++ + 2 files changed, 38 insertions(+), 26 deletions(-) + +diff --git a/third_party/sqlite/src/src/util.c b/third_party/sqlite/src/src/util.c +index b4c5e62..7640f1d 100644 +--- a/third_party/sqlite/src/src/util.c ++++ b/third_party/sqlite/src/src/util.c +@@ -1244,36 +1244,21 @@ int sqlite3SubInt64(i64 *pA, i64 iB){ + return sqlite3AddInt64(pA, -iB); + } + } +-#define TWOPOWER32 (((i64)1)<<32) +-#define TWOPOWER31 (((i64)1)<<31) + int sqlite3MulInt64(i64 *pA, i64 iB){ + i64 iA = *pA; +- i64 iA1, iA0, iB1, iB0, r; +- +- iA1 = iA/TWOPOWER32; +- iA0 = iA % TWOPOWER32; +- iB1 = iB/TWOPOWER32; +- iB0 = iB % TWOPOWER32; +- if( iA1==0 ){ +- if( iB1==0 ){ +- *pA *= iB; +- return 0; ++ if( iB>0 ){ ++ if( iA>LARGEST_INT64/iB ) return 1; ++ if( iA<SMALLEST_INT64/iB ) return 1; ++ }else if( iB<0 ){ ++ if( iA>0 ){ ++ if( iB<SMALLEST_INT64/iA ) return 1; ++ }else if( iA<0 ){ ++ if( iB==SMALLEST_INT64 ) return 1; ++ if( iA==SMALLEST_INT64 ) return 1; ++ if( -iA>LARGEST_INT64/-iB ) return 1; + } +- r = iA0*iB1; +- }else if( iB1==0 ){ +- r = iA1*iB0; +- }else{ +- /* If both iA1 and iB1 are non-zero, overflow will result */ +- return 1; + } +- testcase( r==(-TWOPOWER31)-1 ); +- testcase( r==(-TWOPOWER31) ); +- testcase( r==TWOPOWER31 ); +- testcase( r==TWOPOWER31-1 ); +- if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1; +- r *= TWOPOWER32; +- if( sqlite3AddInt64(&r, iA0*iB0) ) return 1; +- *pA = r; ++ *pA = iA*iB; + return 0; + } + +diff --git a/third_party/sqlite/src/test/expr.test b/third_party/sqlite/src/test/expr.test +index 7d7b8ce..7a6d477 100644 +--- a/third_party/sqlite/src/test/expr.test ++++ b/third_party/sqlite/src/test/expr.test +@@ -308,6 +308,33 @@ ifcapable floatingpoint {if {[working_64bit_int]} { + test_realnum_expr expr-1.257\ + {i1=-4294967296, i2=-2147483647} {i1*i2} 9223372032559808512 + ++ test_realnum_expr expr-1.260\ ++ {i1=3037000500, i2=3037000500} {i1*i2} 9.22337203700025e+18 ++ test_realnum_expr expr-1.261\ ++ {i1=3037000500, i2=-3037000500} {i1*i2} -9.22337203700025e+18 ++ test_realnum_expr expr-1.262\ ++ {i1=-3037000500, i2=3037000500} {i1*i2} -9.22337203700025e+18 ++ test_realnum_expr expr-1.263\ ++ {i1=-3037000500, i2=-3037000500} {i1*i2} 9.22337203700025e+18 ++ ++ test_realnum_expr expr-1.264\ ++ {i1=3037000500, i2=3037000499} {i1*i2} 9223372033963249500 ++ test_realnum_expr expr-1.265\ ++ {i1=3037000500, i2=-3037000499} {i1*i2} -9223372033963249500 ++ test_realnum_expr expr-1.266\ ++ {i1=-3037000500, i2=3037000499} {i1*i2} -9223372033963249500 ++ test_realnum_expr expr-1.267\ ++ {i1=-3037000500, i2=-3037000499} {i1*i2} 9223372033963249500 ++ ++ test_realnum_expr expr-1.268\ ++ {i1=3037000499, i2=3037000500} {i1*i2} 9223372033963249500 ++ test_realnum_expr expr-1.269\ ++ {i1=3037000499, i2=-3037000500} {i1*i2} -9223372033963249500 ++ test_realnum_expr expr-1.270\ ++ {i1=-3037000499, i2=3037000500} {i1*i2} -9223372033963249500 ++ test_realnum_expr expr-1.271\ ++ {i1=-3037000499, i2=-3037000500} {i1*i2} 9223372033963249500 ++ + }} + + ifcapable floatingpoint { +-- +2.5.0 +
diff --git a/third_party/sqlite/src/src/util.c b/third_party/sqlite/src/src/util.c index b4c5e62..7640f1d 100644 --- a/third_party/sqlite/src/src/util.c +++ b/third_party/sqlite/src/src/util.c
@@ -1244,36 +1244,21 @@ return sqlite3AddInt64(pA, -iB); } } -#define TWOPOWER32 (((i64)1)<<32) -#define TWOPOWER31 (((i64)1)<<31) int sqlite3MulInt64(i64 *pA, i64 iB){ i64 iA = *pA; - i64 iA1, iA0, iB1, iB0, r; - - iA1 = iA/TWOPOWER32; - iA0 = iA % TWOPOWER32; - iB1 = iB/TWOPOWER32; - iB0 = iB % TWOPOWER32; - if( iA1==0 ){ - if( iB1==0 ){ - *pA *= iB; - return 0; + if( iB>0 ){ + if( iA>LARGEST_INT64/iB ) return 1; + if( iA<SMALLEST_INT64/iB ) return 1; + }else if( iB<0 ){ + if( iA>0 ){ + if( iB<SMALLEST_INT64/iA ) return 1; + }else if( iA<0 ){ + if( iB==SMALLEST_INT64 ) return 1; + if( iA==SMALLEST_INT64 ) return 1; + if( -iA>LARGEST_INT64/-iB ) return 1; } - r = iA0*iB1; - }else if( iB1==0 ){ - r = iA1*iB0; - }else{ - /* If both iA1 and iB1 are non-zero, overflow will result */ - return 1; } - testcase( r==(-TWOPOWER31)-1 ); - testcase( r==(-TWOPOWER31) ); - testcase( r==TWOPOWER31 ); - testcase( r==TWOPOWER31-1 ); - if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1; - r *= TWOPOWER32; - if( sqlite3AddInt64(&r, iA0*iB0) ) return 1; - *pA = r; + *pA = iA*iB; return 0; }
diff --git a/third_party/sqlite/src/test/expr.test b/third_party/sqlite/src/test/expr.test index 7d7b8ce..7a6d477 100644 --- a/third_party/sqlite/src/test/expr.test +++ b/third_party/sqlite/src/test/expr.test
@@ -308,6 +308,33 @@ test_realnum_expr expr-1.257\ {i1=-4294967296, i2=-2147483647} {i1*i2} 9223372032559808512 + test_realnum_expr expr-1.260\ + {i1=3037000500, i2=3037000500} {i1*i2} 9.22337203700025e+18 + test_realnum_expr expr-1.261\ + {i1=3037000500, i2=-3037000500} {i1*i2} -9.22337203700025e+18 + test_realnum_expr expr-1.262\ + {i1=-3037000500, i2=3037000500} {i1*i2} -9.22337203700025e+18 + test_realnum_expr expr-1.263\ + {i1=-3037000500, i2=-3037000500} {i1*i2} 9.22337203700025e+18 + + test_realnum_expr expr-1.264\ + {i1=3037000500, i2=3037000499} {i1*i2} 9223372033963249500 + test_realnum_expr expr-1.265\ + {i1=3037000500, i2=-3037000499} {i1*i2} -9223372033963249500 + test_realnum_expr expr-1.266\ + {i1=-3037000500, i2=3037000499} {i1*i2} -9223372033963249500 + test_realnum_expr expr-1.267\ + {i1=-3037000500, i2=-3037000499} {i1*i2} 9223372033963249500 + + test_realnum_expr expr-1.268\ + {i1=3037000499, i2=3037000500} {i1*i2} 9223372033963249500 + test_realnum_expr expr-1.269\ + {i1=3037000499, i2=-3037000500} {i1*i2} -9223372033963249500 + test_realnum_expr expr-1.270\ + {i1=-3037000499, i2=3037000500} {i1*i2} -9223372033963249500 + test_realnum_expr expr-1.271\ + {i1=-3037000499, i2=-3037000500} {i1*i2} 9223372033963249500 + }} ifcapable floatingpoint {
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index cefb886..adc7294 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -17761,6 +17761,16 @@ </summary> </histogram> +<histogram name="GCM.DataMessageReceivedHasRegisteredApp" + enum="BooleanRegistered"> + <owner>peter@chromium.org</owner> + <summary> + Records whether a corresponding registration was found for each received + DATA_MESSAGE or DELETED_MESSAGES message from Google Cloud Messaging. + Recorded while processing the received message. + </summary> +</histogram> + <histogram name="GCM.FirstReceivedDataMessageLatencyAfterConnection" units="ms"> <owner>juyik@chromium.org</owner> <summary> @@ -61054,6 +61064,17 @@ </summary> </histogram> +<histogram name="Sync.LostNavigationCount" units="navigations"> + <owner>pnoland@chromium.org</owner> + <summary> + Counts instances of navigations that are recorded locally but not synced. + Recorded once per active tab for every inferred sync cycle. Sync cycles are + inferred by examining the is_synced and is_syncing flags of sync directories + when recording local changes to tabs or windows. Sync cycles that occur + without changes to tabs or windows won't cause this metric to be logged. + </summary> +</histogram> + <histogram name="Sync.MemoryPressureWarningBeforeCleanShutdown" units="count"> <owner>gangwu@chromium.org</owner> <summary>
diff --git a/tools/valgrind/gtest_exclude/chrome_elf_unittests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/chrome_elf_unittests.gtest-drmemory_win32.txt new file mode 100644 index 0000000..8f8d9f9 --- /dev/null +++ b/tools/valgrind/gtest_exclude/chrome_elf_unittests.gtest-drmemory_win32.txt
@@ -0,0 +1,2 @@ +# https://crbug.com/628462 +BlacklistTest.LoadBlacklistedLibrary
diff --git a/ui/gfx/image/canvas_image_source.cc b/ui/gfx/image/canvas_image_source.cc index b336b31..e53257f 100644 --- a/ui/gfx/image/canvas_image_source.cc +++ b/ui/gfx/image/canvas_image_source.cc
@@ -6,6 +6,7 @@ #include "base/logging.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/image/image_skia.h" namespace gfx {
diff --git a/ui/gfx/image/canvas_image_source.h b/ui/gfx/image/canvas_image_source.h index f41c281..1108f0d 100644 --- a/ui/gfx/image/canvas_image_source.h +++ b/ui/gfx/image/canvas_image_source.h
@@ -7,8 +7,10 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/gfx_export.h" +#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_source.h" namespace gfx { @@ -21,7 +23,18 @@ // completed. class GFX_EXPORT CanvasImageSource : public gfx::ImageSkiaSource { public: + // Factory function to create an ImageSkia from a CanvasImageSource. Example: + // gfx::ImageSkia my_image = + // CanvasImageSource::MakeImageSkia<MySource>(param1, param2); + template <typename T, typename... Args> + static ImageSkia MakeImageSkia(Args&&... args) { + auto source = base::MakeUnique<T>(std::forward<Args>(args)...); + gfx::Size size = source->size(); + return gfx::ImageSkia(source.release(), size); + } + CanvasImageSource(const gfx::Size& size, bool is_opaque); + ~CanvasImageSource() override {} // Called when a new image needs to be drawn for a scale factor. virtual void Draw(gfx::Canvas* canvas) = 0; @@ -33,8 +46,6 @@ gfx::ImageSkiaRep GetImageForScale(float scale) override; protected: - ~CanvasImageSource() override {} - const gfx::Size size_; const bool is_opaque_; DISALLOW_COPY_AND_ASSIGN(CanvasImageSource);
diff --git a/ui/gfx/ipc/gfx_param_traits_macros.h b/ui/gfx/ipc/gfx_param_traits_macros.h index 2a8c6e5..cdee2f6 100644 --- a/ui/gfx/ipc/gfx_param_traits_macros.h +++ b/ui/gfx/ipc/gfx_param_traits_macros.h
@@ -54,6 +54,7 @@ IPC_STRUCT_TRAITS_BEGIN(gfx::NativePixmapPlane) IPC_STRUCT_TRAITS_MEMBER(stride) IPC_STRUCT_TRAITS_MEMBER(offset) + IPC_STRUCT_TRAITS_MEMBER(size) IPC_STRUCT_TRAITS_MEMBER(modifier) IPC_STRUCT_TRAITS_END()
diff --git a/ui/gfx/mojo/buffer_types.mojom b/ui/gfx/mojo/buffer_types.mojom index 0f0be7e9..49c1ee1 100644 --- a/ui/gfx/mojo/buffer_types.mojom +++ b/ui/gfx/mojo/buffer_types.mojom
@@ -53,6 +53,7 @@ struct NativePixmapPlane { uint32 stride; int32 offset; + uint64 size; uint64 modifier; };
diff --git a/ui/gfx/mojo/buffer_types_traits.h b/ui/gfx/mojo/buffer_types_traits.h index 175a493..311f98e 100644 --- a/ui/gfx/mojo/buffer_types_traits.h +++ b/ui/gfx/mojo/buffer_types_traits.h
@@ -199,6 +199,9 @@ static int32_t offset(const gfx::NativePixmapPlane& plane) { return plane.offset; } + static uint64_t size(const gfx::NativePixmapPlane& plane) { + return plane.size; + } static uint64_t modifier(const gfx::NativePixmapPlane& plane) { return plane.modifier; } @@ -206,6 +209,7 @@ gfx::NativePixmapPlane* out) { out->stride = data.stride(); out->offset = data.offset(); + out->size = data.size(); out->modifier = data.modifier(); return true; }
diff --git a/ui/gfx/mojo/struct_traits_unittest.cc b/ui/gfx/mojo/struct_traits_unittest.cc index 556954b0..d837cd8 100644 --- a/ui/gfx/mojo/struct_traits_unittest.cc +++ b/ui/gfx/mojo/struct_traits_unittest.cc
@@ -144,6 +144,10 @@ const gfx::GpuMemoryBufferId kId(99); const uint32_t kOffset = 126; const int32_t kStride = 256; +#if defined(USE_OZONE) + const uint64_t kSize = kOffset + kStride; + const uint64_t kModifier = 2; +#endif base::SharedMemory shared_memory; ASSERT_TRUE(shared_memory.CreateAnonymous(1024)); ASSERT_TRUE(shared_memory.Map(1024)); @@ -155,6 +159,11 @@ handle.offset = kOffset; handle.stride = kStride; +#if defined(USE_OZONE) + handle.native_pixmap_handle.planes.emplace_back(kOffset, kStride, kSize, + kModifier); +#endif + mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); gfx::GpuMemoryBufferHandle output; proxy->EchoGpuMemoryBufferHandle(handle, &output); @@ -162,6 +171,13 @@ EXPECT_EQ(kId, output.id); EXPECT_EQ(kOffset, output.offset); EXPECT_EQ(kStride, output.stride); + +#if defined(USE_OZONE) + ASSERT_EQ(1u, output.native_pixmap_handle.planes.size()); + EXPECT_EQ(kSize, output.native_pixmap_handle.planes.back().size); + EXPECT_EQ(kModifier, output.native_pixmap_handle.planes.back().modifier); +#endif + #if !defined(OS_MACOSX) && !defined(OS_IOS) // TODO: Add support for mach_port on mac. base::SharedMemory output_memory(output.handle, true);
diff --git a/ui/gfx/native_pixmap_handle.cc b/ui/gfx/native_pixmap_handle.cc index a3092b265..f0144a8 100644 --- a/ui/gfx/native_pixmap_handle.cc +++ b/ui/gfx/native_pixmap_handle.cc
@@ -2,14 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <stdint.h> + #include "ui/gfx/native_pixmap_handle.h" namespace gfx { -NativePixmapPlane::NativePixmapPlane() : stride(0), offset(0), modifier(0) {} +NativePixmapPlane::NativePixmapPlane() + : stride(0), offset(0), size(0), modifier(0) {} -NativePixmapPlane::NativePixmapPlane(int stride, int offset, uint64_t modifier) - : stride(stride), offset(offset), modifier(modifier) {} +NativePixmapPlane::NativePixmapPlane(int stride, + int offset, + uint64_t size, + uint64_t modifier) + : stride(stride), offset(offset), size(size), modifier(modifier) {} NativePixmapPlane::NativePixmapPlane(const NativePixmapPlane& other) = default;
diff --git a/ui/gfx/native_pixmap_handle.h b/ui/gfx/native_pixmap_handle.h index 767951b..eca2bd1d 100644 --- a/ui/gfx/native_pixmap_handle.h +++ b/ui/gfx/native_pixmap_handle.h
@@ -5,7 +5,7 @@ #ifndef UI_GFX_NATIVE_PIXMAP_HANDLE_H_ #define UI_GFX_NATIVE_PIXMAP_HANDLE_H_ -#include <stdint.h> +#include <stddef.h> #include <vector> #include "ui/gfx/gfx_export.h" @@ -20,7 +20,7 @@ // buffer. More fields can be added if they are plane specific. struct GFX_EXPORT NativePixmapPlane { NativePixmapPlane(); - NativePixmapPlane(int stride, int offset, uint64_t modifier); + NativePixmapPlane(int stride, int offset, uint64_t size, uint64_t modifier); NativePixmapPlane(const NativePixmapPlane& other); ~NativePixmapPlane(); @@ -28,6 +28,9 @@ // a memory mapping. One per plane per entry. int stride; int offset; + // Size in bytes of the plane. + // This is necessary to map the buffers. + uint64_t size; // The modifier is retrieved from GBM library and passed to EGL driver. // Generally it's platform specific, and we don't need to modify it in // Chromium code. Also one per plane per entry.
diff --git a/ui/gfx/paint_vector_icon.cc b/ui/gfx/paint_vector_icon.cc index 70daac2..1ac78e27 100644 --- a/ui/gfx/paint_vector_icon.cc +++ b/ui/gfx/paint_vector_icon.cc
@@ -10,6 +10,7 @@ #include "base/i18n/rtl.h" #include "base/lazy_instance.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "third_party/skia/include/core/SkPaint.h" @@ -596,8 +597,8 @@ ImageSkia CreateVectorIconFromSource(const std::string& source, int dip_size, SkColor color) { - return ImageSkia(new VectorIconSourceLegacy(source, dip_size, color), - gfx::Size(dip_size, dip_size)); + return CanvasImageSource::MakeImageSkia<VectorIconSourceLegacy>( + source, dip_size, color); } } // namespace gfx
diff --git a/ui/ozone/gl/gl_image_ozone_native_pixmap_drm_unittest.cc b/ui/ozone/gl/gl_image_ozone_native_pixmap_drm_unittest.cc index cc2f152..8ab537f 100644 --- a/ui/ozone/gl/gl_image_ozone_native_pixmap_drm_unittest.cc +++ b/ui/ozone/gl/gl_image_ozone_native_pixmap_drm_unittest.cc
@@ -50,6 +50,10 @@ 0, pitches[0] * size.height() + pitches[1] * size.height() / 2, pitches[0] * size.height(), }; + std::vector<size_t> sizes{pitches[0] * size.height(), + pitches[1] * size.height() / 2, + pitches[2] * size.height() / 2}; + size_t byte_number = pitches[0] * size.height() + pitches[1] * size.height() / 2 + pitches[2] * size.height() / 2; @@ -89,7 +93,7 @@ gfx::NativePixmapHandle pixmap_handle; pixmap_handle.fds.emplace_back(fd, false); for (int i = 0; i < 3; i++) { - pixmap_handle.planes.emplace_back(pitches[i], offsets[i], 0); + pixmap_handle.planes.emplace_back(pitches[i], offsets[i], sizes[i], 0); } ui::SurfaceFactoryOzone* surface_factory = ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone();
diff --git a/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc b/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc index 98bf556..78514ef 100644 --- a/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc +++ b/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc
@@ -38,11 +38,16 @@ if (usage == gfx::BufferUsage::GPU_READ_CPU_READ_WRITE) { auto client_pixmap = client_pixmap_factory_->ImportFromHandle( pixmap->ExportHandle(), size, usage); - void* data = client_pixmap->Map(); - EXPECT_TRUE(data); - GLImageTestSupport::SetBufferDataToColor( - size.width(), size.height(), pixmap->GetDmaBufPitch(0), 0, - pixmap->GetBufferFormat(), color, static_cast<uint8_t*>(data)); + bool mapped = client_pixmap->Map(); + EXPECT_TRUE(mapped); + + for (size_t plane = 0; plane < NumberOfPlanesForBufferFormat(format); + ++plane) { + void* data = client_pixmap->GetMemoryAddress(plane); + GLImageTestSupport::SetBufferDataToColor( + size.width(), size.height(), pixmap->GetDmaBufPitch(plane), plane, + pixmap->GetBufferFormat(), color, static_cast<uint8_t*>(data)); + } client_pixmap->Unmap(); }
diff --git a/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc b/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc index 9c1cf29..01f0b0f 100644 --- a/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc +++ b/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc
@@ -19,12 +19,19 @@ class ClientNativePixmapCast : public ClientNativePixmap { public: // ClientNativePixmap implementation: - void* Map() override { + bool Map() override { + NOTREACHED(); + return false; + } + void* GetMemoryAddress(size_t plane) const override { NOTREACHED(); return nullptr; - } + }; void Unmap() override { NOTREACHED(); } - void GetStride(int* stride) const override { NOTREACHED(); } + int GetStride(size_t plane) const override { + NOTREACHED(); + return 0; + } }; class ClientNativePixmapFactoryCast : public ClientNativePixmapFactory {
diff --git a/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc b/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc index 19b3e104..06aada4 100644 --- a/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc +++ b/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc
@@ -21,12 +21,19 @@ ClientNativePixmapGbm() {} ~ClientNativePixmapGbm() override {} - void* Map() override { + bool Map() override { + NOTREACHED(); + return false; + } + void Unmap() override { NOTREACHED(); } + void* GetMemoryAddress(size_t plane) const override { NOTREACHED(); return nullptr; } - void Unmap() override { NOTREACHED(); } - void GetStride(int* stride) const override { NOTREACHED(); } + int GetStride(size_t plane) const override { + NOTREACHED(); + return 0; + } }; } // namespace @@ -73,21 +80,20 @@ const gfx::Size& size, gfx::BufferUsage usage) override { DCHECK(!handle.fds.empty()); - base::ScopedFD scoped_fd(handle.fds[0].fd); switch (usage) { case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: #if defined(OS_CHROMEOS) - // TODO(dcastagna): Add support for pixmaps with multiple FDs for non - // scanout buffers. - return ClientNativePixmapDmaBuf::ImportFromDmabuf( - scoped_fd.release(), size, handle.planes[0].stride); + return ClientNativePixmapDmaBuf::ImportFromDmabuf(handle, size); #else NOTREACHED(); return nullptr; #endif case gfx::BufferUsage::GPU_READ: case gfx::BufferUsage::SCANOUT: + // Close all the fds. + for (const auto& fd : handle.fds) + base::ScopedFD scoped_fd(fd.fd); return base::WrapUnique(new ClientNativePixmapGbm); } NOTREACHED();
diff --git a/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.cc b/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.cc index a04bc5c4..8c547f8 100644 --- a/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.cc +++ b/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.cc
@@ -58,42 +58,46 @@ // static std::unique_ptr<ClientNativePixmap> ClientNativePixmapDmaBuf::ImportFromDmabuf( - int dmabuf_fd, - const gfx::Size& size, - int stride) { - DCHECK_GE(dmabuf_fd, 0); - base::CheckedNumeric<size_t> map_size = stride; - map_size *= size.height(); - if (!map_size.IsValid()) - return nullptr; - return base::WrapUnique(new ClientNativePixmapDmaBuf(dmabuf_fd, size, stride, - map_size.ValueOrDie())); + const gfx::NativePixmapHandle& handle, + const gfx::Size& size) { + return base::WrapUnique(new ClientNativePixmapDmaBuf(handle, size)); } -ClientNativePixmapDmaBuf::ClientNativePixmapDmaBuf(int dmabuf_fd, - const gfx::Size& size, - int stride, - size_t map_size) - : dmabuf_fd_(dmabuf_fd), map_size_(map_size), size_(size), stride_(stride) { +ClientNativePixmapDmaBuf::ClientNativePixmapDmaBuf( + const gfx::NativePixmapHandle& handle, + const gfx::Size& size) + : pixmap_handle_(handle), size_(size), data_{0} { TRACE_EVENT0("drm", "ClientNativePixmapDmaBuf"); - data_ = mmap(nullptr, map_size_, (PROT_READ | PROT_WRITE), MAP_SHARED, - dmabuf_fd, 0); + // TODO(dcastagna): support multiple fds. + DCHECK_EQ(1u, handle.fds.size()); + DCHECK_GE(handle.fds.front().fd, 0); + dmabuf_fd_.reset(handle.fds.front().fd); + + DCHECK_GE(handle.planes.back().size, 0u); + size_t map_size = handle.planes.back().offset + handle.planes.back().size; + data_ = mmap(nullptr, map_size, (PROT_READ | PROT_WRITE), MAP_SHARED, + dmabuf_fd_.get(), 0); if (data_ == MAP_FAILED) { PLOG(ERROR) << "Failed mmap()."; - base::TerminateBecauseOutOfMemory(map_size_); + base::TerminateBecauseOutOfMemory(map_size); } } ClientNativePixmapDmaBuf::~ClientNativePixmapDmaBuf() { TRACE_EVENT0("drm", "~ClientNativePixmapDmaBuf"); - int ret = munmap(data_, map_size_); + size_t map_size = + pixmap_handle_.planes.back().offset + pixmap_handle_.planes.back().size; + int ret = munmap(data_, map_size); DCHECK(!ret); } -void* ClientNativePixmapDmaBuf::Map() { +bool ClientNativePixmapDmaBuf::Map() { TRACE_EVENT0("drm", "DmaBuf:Map"); - PrimeSyncStart(dmabuf_fd_.get()); - return data_; + if (data_ != nullptr) { + PrimeSyncStart(dmabuf_fd_.get()); + return true; + } + return false; } void ClientNativePixmapDmaBuf::Unmap() { @@ -101,8 +105,15 @@ PrimeSyncEnd(dmabuf_fd_.get()); } -void ClientNativePixmapDmaBuf::GetStride(int* stride) const { - *stride = stride_; +void* ClientNativePixmapDmaBuf::GetMemoryAddress(size_t plane) const { + DCHECK_LT(plane, pixmap_handle_.planes.size()); + uint8_t* address = reinterpret_cast<uint8_t*>(data_); + return address + pixmap_handle_.planes[plane].offset; +} + +int ClientNativePixmapDmaBuf::GetStride(size_t plane) const { + DCHECK_LT(plane, pixmap_handle_.planes.size()); + return pixmap_handle_.planes[plane].stride; } } // namespace ui
diff --git a/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.h b/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.h index 4fef392..9817235 100644 --- a/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.h +++ b/ui/ozone/platform/drm/common/client_native_pixmap_dmabuf.h
@@ -12,32 +12,33 @@ #include "base/files/scoped_file.h" #include "base/macros.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/native_pixmap_handle.h" #include "ui/ozone/public/client_native_pixmap.h" namespace ui { class ClientNativePixmapDmaBuf : public ClientNativePixmap { public: - static std::unique_ptr<ClientNativePixmap> - ImportFromDmabuf(int dmabuf_fd, const gfx::Size& size, int stride); + static std::unique_ptr<ClientNativePixmap> ImportFromDmabuf( + const gfx::NativePixmapHandle& handle, + const gfx::Size& size); ~ClientNativePixmapDmaBuf() override; // Overridden from ClientNativePixmap. - void* Map() override; + bool Map() override; void Unmap() override; - void GetStride(int* stride) const override; + + void* GetMemoryAddress(size_t plane) const override; + int GetStride(size_t plane) const override; private: - ClientNativePixmapDmaBuf(int dmabuf_fd, - const gfx::Size& size, - int stride, - size_t map_size); + ClientNativePixmapDmaBuf(const gfx::NativePixmapHandle& handle, + const gfx::Size& size); - base::ScopedFD dmabuf_fd_; - const size_t map_size_; + const gfx::NativePixmapHandle pixmap_handle_; const gfx::Size size_; - const int stride_; + base::ScopedFD dmabuf_fd_; void* data_; DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapDmaBuf);
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/ui/ozone/platform/drm/gpu/gbm_buffer.cc index 8b9a893..0f38ed8 100644 --- a/ui/ozone/platform/drm/gpu/gbm_buffer.cc +++ b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
@@ -75,6 +75,11 @@ return planes_[index].offset; } +size_t GbmBuffer::GetSize(size_t index) const { + DCHECK_LT(index, planes_.size()); + return planes_[index].size; +} + uint64_t GbmBuffer::GetFormatModifier(size_t index) const { DCHECK_LT(index, planes_.size()); return planes_[index].modifier; @@ -123,16 +128,20 @@ // kept open for the lifetime of the buffer. base::ScopedFD fd(gbm_bo_get_plane_fd(bo, i)); - if (!fd.is_valid()) { - PLOG(ERROR) << "Failed to export buffer to dma_buf"; - gbm_bo_destroy(bo); - return nullptr; + // TODO(dcastagna): support multiple fds. + // crbug.com/642410 + if (!i) { + if (!fd.is_valid()) { + PLOG(ERROR) << "Failed to export buffer to dma_buf"; + gbm_bo_destroy(bo); + return nullptr; + } + fds.emplace_back(std::move(fd)); } - fds.emplace_back(std::move(fd)); - planes.emplace_back(gbm_bo_get_plane_stride(bo, i), - gbm_bo_get_plane_offset(bo, i), - gbm_bo_get_plane_format_modifier(bo, i)); + planes.emplace_back( + gbm_bo_get_plane_stride(bo, i), gbm_bo_get_plane_offset(bo, i), + gbm_bo_get_plane_size(bo, i), gbm_bo_get_plane_format_modifier(bo, i)); } scoped_refptr<GbmBuffer> buffer(new GbmBuffer( gbm, bo, format, usage, std::move(fds), size, std::move(planes))); @@ -216,6 +225,7 @@ base::FileDescriptor(scoped_fd.release(), true /* auto_close */)); } handle.planes.emplace_back(buffer_->GetStride(i), buffer_->GetOffset(i), + buffer_->GetSize(i), buffer_->GetFormatModifier(i)); } return handle;
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.h b/ui/ozone/platform/drm/gpu/gbm_buffer.h index b8397448..f4ff68a2 100644 --- a/ui/ozone/platform/drm/gpu/gbm_buffer.h +++ b/ui/ozone/platform/drm/gpu/gbm_buffer.h
@@ -41,6 +41,7 @@ int GetFd(size_t plane) const; int GetStride(size_t plane) const; int GetOffset(size_t plane) const; + size_t GetSize(size_t plane) const; uint64_t GetFormatModifier(size_t plane) const; gfx::Size GetSize() const override;
diff --git a/ui/ozone/public/client_native_pixmap.h b/ui/ozone/public/client_native_pixmap.h index e7aa3615..a94e34f 100644 --- a/ui/ozone/public/client_native_pixmap.h +++ b/ui/ozone/public/client_native_pixmap.h
@@ -16,9 +16,13 @@ public: virtual ~ClientNativePixmap() {} - virtual void* Map() = 0; + // Map each plane in the client address space. + // Return false on error. + virtual bool Map() = 0; virtual void Unmap() = 0; - virtual void GetStride(int* stride) const = 0; + + virtual void* GetMemoryAddress(size_t plane) const = 0; + virtual int GetStride(size_t plane) const = 0; }; } // namespace ui
diff --git a/ui/views/mus/clipboard_mus.cc b/ui/views/mus/clipboard_mus.cc index 8e43571d..3fb01b9 100644 --- a/ui/views/mus/clipboard_mus.cc +++ b/ui/views/mus/clipboard_mus.cc
@@ -4,6 +4,10 @@ #include "ui/views/mus/clipboard_mus.h" +#include <string> +#include <utility> +#include <vector> + #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" @@ -256,7 +260,8 @@ void ClipboardMus::WriteObjects(ui::ClipboardType type, const ObjectMap& objects) { - current_clipboard_.reset(new mojo::Map<mojo::String, mojo::Array<uint8_t>>); + current_clipboard_ = + base::MakeUnique<mojo::Map<mojo::String, mojo::Array<uint8_t>>>(); for (const auto& p : objects) DispatchObject(static_cast<ObjectType>(p.first), p.second);
diff --git a/ui/views/mus/input_method_mus.cc b/ui/views/mus/input_method_mus.cc index fb28dbd..9540e984 100644 --- a/ui/views/mus/input_method_mus.cc +++ b/ui/views/mus/input_method_mus.cc
@@ -114,7 +114,7 @@ InputMethodBase::OnDidChangeFocusedClient(focused_before, focused); UpdateTextInputType(); - text_input_client_.reset(new TextInputClientImpl(focused, this)); + text_input_client_ = base::MakeUnique<TextInputClientImpl>(focused, this); ime_server_->StartSession(text_input_client_->CreateInterfacePtrAndBind(), GetProxy(&input_method_)); }
diff --git a/ui/views/mus/input_method_mus_unittest.cc b/ui/views/mus/input_method_mus_unittest.cc index 0c0e3c6..618a46b3 100644 --- a/ui/views/mus/input_method_mus_unittest.cc +++ b/ui/views/mus/input_method_mus_unittest.cc
@@ -4,6 +4,8 @@ #include "ui/views/mus/input_method_mus.h" +#include <memory> + #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" @@ -37,7 +39,7 @@ ~TestTextInputClient() override {} ui::KeyEvent* WaitUntilInputReceieved() { - run_loop_.reset(new base::RunLoop); + run_loop_ = base::MakeUnique<base::RunLoop>(); run_loop_->Run(); run_loop_.reset();
diff --git a/ui/views/mus/pointer_watcher_event_router_unittest.cc b/ui/views/mus/pointer_watcher_event_router_unittest.cc index ba6cec9..511cc33 100644 --- a/ui/views/mus/pointer_watcher_event_router_unittest.cc +++ b/ui/views/mus/pointer_watcher_event_router_unittest.cc
@@ -30,7 +30,7 @@ void OnPointerEventObserved(const ui::PointerEvent& event, const gfx::Point& location_in_screen, Widget* target) override { - last_event_observed_.reset(new ui::PointerEvent(event)); + last_event_observed_ = base::MakeUnique<ui::PointerEvent>(event); } private:
diff --git a/ui/views/mus/views_mus_test_suite.cc b/ui/views/mus/views_mus_test_suite.cc index 701ea56..bf63030 100644 --- a/ui/views/mus/views_mus_test_suite.cc +++ b/ui/views/mus/views_mus_test_suite.cc
@@ -5,6 +5,7 @@ #include "ui/views/mus/views_mus_test_suite.h" #include <memory> +#include <string> #include "base/command_line.h" #include "base/files/file_path.h" @@ -33,7 +34,7 @@ class DefaultService : public shell::Service { public: - DefaultService() {} + DefaultService() {} ~DefaultService() override {} private: @@ -114,12 +115,11 @@ } void SetUpConnections(base::WaitableEvent* wait) { - background_shell_.reset(new shell::BackgroundShell); + background_shell_ = base::MakeUnique<shell::BackgroundShell>(); background_shell_->Init(nullptr); - service_.reset(new DefaultService); - shell_connection_.reset(new shell::ServiceContext( - service_.get(), - background_shell_->CreateServiceRequest(GetTestName()))); + service_ = base::MakeUnique<DefaultService>(); + shell_connection_ = base::MakeUnique<shell::ServiceContext>( + service_.get(), background_shell_->CreateServiceRequest(GetTestName())); // ui/views/mus requires a WindowManager running, so launch test_wm. shell::Connector* connector = shell_connection_->connector(); @@ -166,7 +166,7 @@ EnsureCommandLineSwitch(ui::switches::kUseTestConfig); ViewsTestSuite::Initialize(); - shell_connections_.reset(new ShellConnection); + shell_connections_ = base::MakeUnique<ShellConnection>(); } void ViewsMusTestSuite::Shutdown() {
diff --git a/ui/views/mus/window_manager_connection.cc b/ui/views/mus/window_manager_connection.cc index 7b30672..8c4cf85 100644 --- a/ui/views/mus/window_manager_connection.cc +++ b/ui/views/mus/window_manager_connection.cc
@@ -4,6 +4,7 @@ #include "ui/views/mus/window_manager_connection.h" +#include <set> #include <utility> #include "base/lazy_instance.h" @@ -113,20 +114,20 @@ lazy_tls_ptr.Pointer()->Set(this); gpu_service_ = ui::GpuService::Create(connector, std::move(task_runner)); - compositor_context_factory_.reset( - new views::SurfaceContextFactory(gpu_service_.get())); + compositor_context_factory_ = + base::MakeUnique<views::SurfaceContextFactory>(gpu_service_.get()); aura::Env::GetInstance()->set_context_factory( compositor_context_factory_.get()); - client_.reset(new ui::WindowTreeClient(this, nullptr, nullptr)); + client_ = base::MakeUnique<ui::WindowTreeClient>(this, nullptr, nullptr); client_->ConnectViaWindowTreeFactory(connector_); - pointer_watcher_event_router_.reset( - new PointerWatcherEventRouter(client_.get())); + pointer_watcher_event_router_ = + base::MakeUnique<PointerWatcherEventRouter>(client_.get()); - screen_.reset(new ScreenMus(this)); + screen_ = base::MakeUnique<ScreenMus>(this); screen_->Init(connector); - std::unique_ptr<ClipboardMus> clipboard(new ClipboardMus); + std::unique_ptr<ClipboardMus> clipboard = base::MakeUnique<ClipboardMus>(); clipboard->Init(connector); ui::Clipboard::SetClipboardForCurrentThread(std::move(clipboard));
diff --git a/ui/views/mus/window_tree_host_mus.cc b/ui/views/mus/window_tree_host_mus.cc index 745c1a6c..caed8943 100644 --- a/ui/views/mus/window_tree_host_mus.cc +++ b/ui/views/mus/window_tree_host_mus.cc
@@ -54,7 +54,7 @@ dispatcher()->set_transform_events(false); compositor()->SetHostHasTransparentBackground(true); - input_method_.reset(new InputMethodMus(this, window)); + input_method_ = base::MakeUnique<InputMethodMus>(this, window); SetSharedInputMethod(input_method_.get()); }